library-skills 0.0.11 → 0.0.12

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/README.md CHANGED
@@ -49,11 +49,13 @@ $ npx library-skills
49
49
 
50
50
  This will scan the dependencies for the current project, find the installed libraries, and ask you which of their skills you want to install in the project.
51
51
 
52
- Then it will add them to the `.agents` directory as symbolic links, so when you update the libraries, the skills are updated too.
52
+ Then it will ask where to install them and add them as symbolic links, so when you update the libraries, the skills are updated too.
53
+
54
+ By default it selects `.agents/skills`, the agent-neutral target. If the project already has a `.claude/` directory, it selects `.claude/skills` too.
53
55
 
54
56
  /// tip
55
57
 
56
- If you are using Claude Code, add the `--claude` CLI Option to install the skills in the `.claude/skills` directory too, as Claude Code doesn't support the standard `.agents` directory.
58
+ If you are using Claude Code, select `.claude/skills` when asked for installation targets, as Claude Code doesn't support the standard `.agents` directory. For non-interactive installs, add the `--claude` CLI option to install the skills in `.claude/skills` too.
57
59
 
58
60
  ///
59
61
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "library-skills",
3
- "version": "0.0.11",
3
+ "version": "0.0.12",
4
4
  "description": "Library Skills, AI Agents using libraries, as intended, always up to date.",
5
5
  "type": "module",
6
6
  "exports": {
package/ts/dist/cli.js CHANGED
@@ -660,6 +660,26 @@ function getTargetDirs(projectRoot, options = {}) {
660
660
  }
661
661
  return targets;
662
662
  }
663
+ function getAllTargetDirs(projectRoot) {
664
+ return getTargetDirs(projectRoot, { includeClaude: true });
665
+ }
666
+ function getDefaultInstallTargetDirs(projectRoot) {
667
+ const [universal, claude] = getAllTargetDirs(projectRoot);
668
+ const selected = [];
669
+ if (existsSync3(join3(projectRoot, ".agents"))) {
670
+ selected.push(universal);
671
+ }
672
+ if (existsSync3(join3(projectRoot, ".claude"))) {
673
+ selected.push(claude);
674
+ }
675
+ return selected.length > 0 ? selected : [universal];
676
+ }
677
+ function getExistingTargetDirs(projectRoot, options = {}) {
678
+ if (options.includeClaude) {
679
+ return getTargetDirs(projectRoot, { includeClaude: true });
680
+ }
681
+ return getAllTargetDirs(projectRoot).filter((target) => isDirectory2(target.path));
682
+ }
663
683
  function installSkill(skill, targetDir, options = {}) {
664
684
  const dest = join3(targetDir, skill.name);
665
685
  const source = resolve2(skill.skillDir);
@@ -1304,6 +1324,19 @@ function findCollisions(skills) {
1304
1324
  [...counts.entries()].filter(([, count]) => count > 1).map(([name]) => name)
1305
1325
  );
1306
1326
  }
1327
+ function deduplicateSkills(skills) {
1328
+ const seen = /* @__PURE__ */ new Set();
1329
+ const unique = [];
1330
+ for (const skill of skills) {
1331
+ const key = resolve5(skill.skillDir);
1332
+ if (seen.has(key)) {
1333
+ continue;
1334
+ }
1335
+ seen.add(key);
1336
+ unique.push(skill);
1337
+ }
1338
+ return unique;
1339
+ }
1307
1340
  function filterInstallableSkills({
1308
1341
  skills,
1309
1342
  selectedNames,
@@ -1413,6 +1446,34 @@ async function selectSkillsInteractive(skills) {
1413
1446
  }))
1414
1447
  });
1415
1448
  }
1449
+ async function selectTargetsInteractive({
1450
+ projectRoot,
1451
+ defaultTargets
1452
+ }) {
1453
+ const defaultNames = new Set(defaultTargets.map((target) => target.name));
1454
+ return checkbox({
1455
+ message: "Select installation targets (press Space to select, Enter to confirm):",
1456
+ choices: getAllTargetDirs(projectRoot).map((target) => ({
1457
+ name: displayPath(target.path, projectRoot),
1458
+ value: target,
1459
+ checked: defaultNames.has(target.name)
1460
+ })),
1461
+ validate: (selected) => selected.length > 0 || "Please select at least one installation target."
1462
+ });
1463
+ }
1464
+ async function selectInstallTargets({
1465
+ projectRoot,
1466
+ includeClaude,
1467
+ interactive
1468
+ }) {
1469
+ if (!interactive) {
1470
+ return getTargetDirs(projectRoot, { includeClaude });
1471
+ }
1472
+ return selectTargetsInteractive({
1473
+ projectRoot,
1474
+ defaultTargets: getDefaultInstallTargetDirs(projectRoot)
1475
+ });
1476
+ }
1416
1477
  async function selectInstalledSkillsInteractive(statuses) {
1417
1478
  const removable = statuses.filter((status) => status.type === "symlink");
1418
1479
  if (removable.length === 0) {
@@ -1459,9 +1520,9 @@ function installSelected({
1459
1520
  async function sync(options) {
1460
1521
  const context = getProjectContext();
1461
1522
  const result = scanContext(context);
1462
- const targets = getTargetDirs(context.projectRoot, {
1523
+ let targets = options.yes || options.check || options.claude ? getTargetDirs(context.projectRoot, {
1463
1524
  includeClaude: options.claude
1464
- });
1525
+ }) : getDefaultInstallTargetDirs(context.projectRoot);
1465
1526
  printContext(context);
1466
1527
  console.log();
1467
1528
  printWarnings(result.warnings);
@@ -1512,10 +1573,19 @@ async function sync(options) {
1512
1573
  if (selected.length === 0 && !options.yes && selectedNames.length === 0 && !options.all) {
1513
1574
  const newSkills = visibleStatuses.filter((status) => status.status === "new" && status.skill !== null).map((status) => status.skill);
1514
1575
  if (newSkills.length > 0) {
1515
- selected = await selectSkillsInteractive(newSkills);
1576
+ selected = await selectSkillsInteractive(deduplicateSkills(newSkills));
1516
1577
  }
1517
1578
  }
1518
1579
  if (selected.length > 0) {
1580
+ targets = await selectInstallTargets({
1581
+ projectRoot: context.projectRoot,
1582
+ includeClaude: options.claude,
1583
+ interactive: !options.yes && !options.claude
1584
+ });
1585
+ if (targets.length === 0) {
1586
+ console.log("No installation targets selected.");
1587
+ return;
1588
+ }
1519
1589
  console.log();
1520
1590
  const installedCount = installSelected({
1521
1591
  skills: selected,
@@ -1556,7 +1626,7 @@ function listCommand(options) {
1556
1626
  skills: result.skills,
1557
1627
  includeAll: Boolean(options.all)
1558
1628
  });
1559
- const targets = getTargetDirs(context.projectRoot, {
1629
+ const targets = getExistingTargetDirs(context.projectRoot, {
1560
1630
  includeClaude: options.claude
1561
1631
  });
1562
1632
  const statuses = installedStatuses({ targets, skills: result.skills });
@@ -1596,9 +1666,6 @@ async function installCommand(options) {
1596
1666
  skills: result.skills,
1597
1667
  includeAll: Boolean(options.all) || selectedNames.length > 0
1598
1668
  });
1599
- const targets = getTargetDirs(context.projectRoot, {
1600
- includeClaude: options.claude
1601
- });
1602
1669
  printWarnings(result.warnings);
1603
1670
  let selected = filterInstallableSkills({
1604
1671
  skills,
@@ -1612,6 +1679,15 @@ async function installCommand(options) {
1612
1679
  console.log("No skills selected.");
1613
1680
  return;
1614
1681
  }
1682
+ const targets = await selectInstallTargets({
1683
+ projectRoot: context.projectRoot,
1684
+ includeClaude: options.claude,
1685
+ interactive: !options.yes && !options.claude
1686
+ });
1687
+ if (targets.length === 0) {
1688
+ console.log("No installation targets selected.");
1689
+ return;
1690
+ }
1615
1691
  const installedCount = installSelected({
1616
1692
  skills: selected,
1617
1693
  targets,
@@ -1624,7 +1700,7 @@ async function installCommand(options) {
1624
1700
  async function removeCommand(skillNames, options) {
1625
1701
  const context = getProjectContext();
1626
1702
  const result = scanContext(context);
1627
- const targets = getTargetDirs(context.projectRoot, {
1703
+ const targets = getExistingTargetDirs(context.projectRoot, {
1628
1704
  includeClaude: options.claude
1629
1705
  });
1630
1706
  const statuses = installedStatuses({ targets, skills: result.skills });
@@ -23,6 +23,9 @@ __export(installer_exports, {
23
23
  CLAUDE_SKILLS_DIR: () => CLAUDE_SKILLS_DIR,
24
24
  InstallError: () => InstallError,
25
25
  UNIVERSAL_SKILLS_DIR: () => UNIVERSAL_SKILLS_DIR,
26
+ getAllTargetDirs: () => getAllTargetDirs,
27
+ getDefaultInstallTargetDirs: () => getDefaultInstallTargetDirs,
28
+ getExistingTargetDirs: () => getExistingTargetDirs,
26
29
  getTargetDirs: () => getTargetDirs,
27
30
  installSkill: () => installSkill,
28
31
  listInstalledSkills: () => listInstalledSkills,
@@ -52,6 +55,26 @@ function getTargetDirs(projectRoot, options = {}) {
52
55
  }
53
56
  return targets;
54
57
  }
58
+ function getAllTargetDirs(projectRoot) {
59
+ return getTargetDirs(projectRoot, { includeClaude: true });
60
+ }
61
+ function getDefaultInstallTargetDirs(projectRoot) {
62
+ const [universal, claude] = getAllTargetDirs(projectRoot);
63
+ const selected = [];
64
+ if ((0, import_node_fs.existsSync)((0, import_node_path.join)(projectRoot, ".agents"))) {
65
+ selected.push(universal);
66
+ }
67
+ if ((0, import_node_fs.existsSync)((0, import_node_path.join)(projectRoot, ".claude"))) {
68
+ selected.push(claude);
69
+ }
70
+ return selected.length > 0 ? selected : [universal];
71
+ }
72
+ function getExistingTargetDirs(projectRoot, options = {}) {
73
+ if (options.includeClaude) {
74
+ return getTargetDirs(projectRoot, { includeClaude: true });
75
+ }
76
+ return getAllTargetDirs(projectRoot).filter((target) => isDirectory(target.path));
77
+ }
55
78
  function installSkill(skill, targetDir, options = {}) {
56
79
  const dest = (0, import_node_path.join)(targetDir, skill.name);
57
80
  const source = (0, import_node_path.resolve)(skill.skillDir);
@@ -137,6 +160,9 @@ var testing = {
137
160
  CLAUDE_SKILLS_DIR,
138
161
  InstallError,
139
162
  UNIVERSAL_SKILLS_DIR,
163
+ getAllTargetDirs,
164
+ getDefaultInstallTargetDirs,
165
+ getExistingTargetDirs,
140
166
  getTargetDirs,
141
167
  installSkill,
142
168
  listInstalledSkills,
@@ -19,6 +19,11 @@ declare class InstallError extends Error {
19
19
  declare function getTargetDirs(projectRoot: string, options?: {
20
20
  includeClaude?: boolean;
21
21
  }): InstallTarget[];
22
+ declare function getAllTargetDirs(projectRoot: string): InstallTarget[];
23
+ declare function getDefaultInstallTargetDirs(projectRoot: string): InstallTarget[];
24
+ declare function getExistingTargetDirs(projectRoot: string, options?: {
25
+ includeClaude?: boolean;
26
+ }): InstallTarget[];
22
27
  declare function installSkill(skill: Skill, targetDir: string, options?: {
23
28
  copy?: boolean;
24
29
  }): string;
@@ -38,4 +43,4 @@ declare const testing: {
38
43
  resolveSymlink: typeof resolveSymlink;
39
44
  };
40
45
 
41
- export { CLAUDE_SKILLS_DIR, InstallError, type InstallTarget, type InstalledSkill, UNIVERSAL_SKILLS_DIR, getTargetDirs, installSkill, listInstalledSkills, testing, uninstallSkill };
46
+ export { CLAUDE_SKILLS_DIR, InstallError, type InstallTarget, type InstalledSkill, UNIVERSAL_SKILLS_DIR, getAllTargetDirs, getDefaultInstallTargetDirs, getExistingTargetDirs, getTargetDirs, installSkill, listInstalledSkills, testing, uninstallSkill };
@@ -19,6 +19,11 @@ declare class InstallError extends Error {
19
19
  declare function getTargetDirs(projectRoot: string, options?: {
20
20
  includeClaude?: boolean;
21
21
  }): InstallTarget[];
22
+ declare function getAllTargetDirs(projectRoot: string): InstallTarget[];
23
+ declare function getDefaultInstallTargetDirs(projectRoot: string): InstallTarget[];
24
+ declare function getExistingTargetDirs(projectRoot: string, options?: {
25
+ includeClaude?: boolean;
26
+ }): InstallTarget[];
22
27
  declare function installSkill(skill: Skill, targetDir: string, options?: {
23
28
  copy?: boolean;
24
29
  }): string;
@@ -38,4 +43,4 @@ declare const testing: {
38
43
  resolveSymlink: typeof resolveSymlink;
39
44
  };
40
45
 
41
- export { CLAUDE_SKILLS_DIR, InstallError, type InstallTarget, type InstalledSkill, UNIVERSAL_SKILLS_DIR, getTargetDirs, installSkill, listInstalledSkills, testing, uninstallSkill };
46
+ export { CLAUDE_SKILLS_DIR, InstallError, type InstallTarget, type InstalledSkill, UNIVERSAL_SKILLS_DIR, getAllTargetDirs, getDefaultInstallTargetDirs, getExistingTargetDirs, getTargetDirs, installSkill, listInstalledSkills, testing, uninstallSkill };
@@ -31,6 +31,26 @@ function getTargetDirs(projectRoot, options = {}) {
31
31
  }
32
32
  return targets;
33
33
  }
34
+ function getAllTargetDirs(projectRoot) {
35
+ return getTargetDirs(projectRoot, { includeClaude: true });
36
+ }
37
+ function getDefaultInstallTargetDirs(projectRoot) {
38
+ const [universal, claude] = getAllTargetDirs(projectRoot);
39
+ const selected = [];
40
+ if (existsSync(join(projectRoot, ".agents"))) {
41
+ selected.push(universal);
42
+ }
43
+ if (existsSync(join(projectRoot, ".claude"))) {
44
+ selected.push(claude);
45
+ }
46
+ return selected.length > 0 ? selected : [universal];
47
+ }
48
+ function getExistingTargetDirs(projectRoot, options = {}) {
49
+ if (options.includeClaude) {
50
+ return getTargetDirs(projectRoot, { includeClaude: true });
51
+ }
52
+ return getAllTargetDirs(projectRoot).filter((target) => isDirectory(target.path));
53
+ }
34
54
  function installSkill(skill, targetDir, options = {}) {
35
55
  const dest = join(targetDir, skill.name);
36
56
  const source = resolve(skill.skillDir);
@@ -115,6 +135,9 @@ export {
115
135
  CLAUDE_SKILLS_DIR,
116
136
  InstallError,
117
137
  UNIVERSAL_SKILLS_DIR,
138
+ getAllTargetDirs,
139
+ getDefaultInstallTargetDirs,
140
+ getExistingTargetDirs,
118
141
  getTargetDirs,
119
142
  installSkill,
120
143
  listInstalledSkills,