library-skills 0.0.12 → 0.0.13
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 +6 -2
- package/package.json +1 -1
- package/ts/dist/cli.d.ts +23 -0
- package/ts/dist/cli.js +124 -8
package/README.md
CHANGED
|
@@ -47,9 +47,13 @@ In JavaScript/TypeScript, you can install them with:
|
|
|
47
47
|
$ npx library-skills
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
This will scan the dependencies for the current project, find the installed libraries, and
|
|
50
|
+
This will scan the dependencies for the current project, find the installed libraries, and show the current skill installation status.
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
It can install new skills, repair managed symlinks that point to old skill locations, and remove managed symlinks for packages or skills that disappeared.
|
|
53
|
+
|
|
54
|
+
It will only remove managed symlinks, not hand-authored skill directories.
|
|
55
|
+
|
|
56
|
+
Then it will ask where to install new skills and add them as symbolic links, so when you update the libraries, the skills are updated too.
|
|
53
57
|
|
|
54
58
|
By default it selects `.agents/skills`, the agent-neutral target. If the project already has a `.claude/` directory, it selects `.claude/skills` too.
|
|
55
59
|
|
package/package.json
CHANGED
package/ts/dist/cli.d.ts
CHANGED
|
@@ -82,6 +82,23 @@ declare function installedStatuses({ targets, skills, }: {
|
|
|
82
82
|
targets: InstallTarget[];
|
|
83
83
|
skills: Skill[];
|
|
84
84
|
}): InstalledStatus[];
|
|
85
|
+
declare function syncTargetDirs({ projectRoot, includeClaude, yes, check, }: {
|
|
86
|
+
projectRoot: string;
|
|
87
|
+
includeClaude?: boolean;
|
|
88
|
+
yes?: boolean;
|
|
89
|
+
check?: boolean;
|
|
90
|
+
}): InstallTarget[];
|
|
91
|
+
declare function repairableStatuses(statuses: InstalledStatus[]): InstalledStatus[];
|
|
92
|
+
declare function removableStatuses(statuses: InstalledStatus[]): InstalledStatus[];
|
|
93
|
+
declare function selectStatusesInteractive(statuses: InstalledStatus[], action: "repair" | "remove"): Promise<InstalledStatus[]>;
|
|
94
|
+
declare function repairSelected({ statuses, projectRoot, }: {
|
|
95
|
+
statuses: InstalledStatus[];
|
|
96
|
+
projectRoot: string;
|
|
97
|
+
}): number;
|
|
98
|
+
declare function removeSelected({ statuses, projectRoot, }: {
|
|
99
|
+
statuses: InstalledStatus[];
|
|
100
|
+
projectRoot: string;
|
|
101
|
+
}): number;
|
|
85
102
|
declare function installSelected({ skills, targets, projectRoot, copy, }: {
|
|
86
103
|
skills: Skill[];
|
|
87
104
|
targets: InstallTarget[];
|
|
@@ -100,7 +117,13 @@ declare const testing: {
|
|
|
100
117
|
installSelected: typeof installSelected;
|
|
101
118
|
installedStatuses: typeof installedStatuses;
|
|
102
119
|
listCommand: typeof listCommand;
|
|
120
|
+
removableStatuses: typeof removableStatuses;
|
|
121
|
+
removeSelected: typeof removeSelected;
|
|
122
|
+
repairSelected: typeof repairSelected;
|
|
123
|
+
repairableStatuses: typeof repairableStatuses;
|
|
103
124
|
scanCommand: typeof scanCommand;
|
|
125
|
+
selectStatusesInteractive: typeof selectStatusesInteractive;
|
|
126
|
+
syncTargetDirs: typeof syncTargetDirs;
|
|
104
127
|
sync: typeof sync;
|
|
105
128
|
topLevelSkills: typeof topLevelSkills;
|
|
106
129
|
displayPath: typeof displayPath;
|
package/ts/dist/cli.js
CHANGED
|
@@ -1138,6 +1138,20 @@ function isDirectory4(path) {
|
|
|
1138
1138
|
}
|
|
1139
1139
|
|
|
1140
1140
|
// ts/src/cli.ts
|
|
1141
|
+
var ACTION_COLORS = {
|
|
1142
|
+
install: "\x1B[1;32m",
|
|
1143
|
+
repair: "\x1B[1;34m",
|
|
1144
|
+
remove: "\x1B[1;31m",
|
|
1145
|
+
reset: "\x1B[0m"
|
|
1146
|
+
};
|
|
1147
|
+
function printActionHeader(action) {
|
|
1148
|
+
const labels = {
|
|
1149
|
+
install: "Install new skills",
|
|
1150
|
+
repair: "Repair installed skills",
|
|
1151
|
+
remove: "Remove stale skills"
|
|
1152
|
+
};
|
|
1153
|
+
console.log(`${ACTION_COLORS[action]}${labels[action]}${ACTION_COLORS.reset}`);
|
|
1154
|
+
}
|
|
1141
1155
|
function getProjectContext(cwd = process.cwd()) {
|
|
1142
1156
|
const workspace = findUvWorkspace(cwd);
|
|
1143
1157
|
const nodeWorkspace = findNodeWorkspace(cwd);
|
|
@@ -1438,6 +1452,7 @@ function printInstalledSkillsTable(statuses, projectRoot) {
|
|
|
1438
1452
|
printStatusTable(installed, projectRoot);
|
|
1439
1453
|
}
|
|
1440
1454
|
async function selectSkillsInteractive(skills) {
|
|
1455
|
+
printActionHeader("install");
|
|
1441
1456
|
return checkbox({
|
|
1442
1457
|
message: "Select skills to install (press Space to select, Enter to confirm):",
|
|
1443
1458
|
choices: skills.map((skill) => ({
|
|
@@ -1487,6 +1502,82 @@ async function selectInstalledSkillsInteractive(statuses) {
|
|
|
1487
1502
|
}))
|
|
1488
1503
|
});
|
|
1489
1504
|
}
|
|
1505
|
+
function syncTargetDirs({
|
|
1506
|
+
projectRoot,
|
|
1507
|
+
includeClaude,
|
|
1508
|
+
yes,
|
|
1509
|
+
check
|
|
1510
|
+
}) {
|
|
1511
|
+
const targetsByName = new Map(
|
|
1512
|
+
getExistingTargetDirs(projectRoot).map((target) => [target.name, target])
|
|
1513
|
+
);
|
|
1514
|
+
const defaultTargets = yes || check || includeClaude ? getTargetDirs(projectRoot, { includeClaude }) : getDefaultInstallTargetDirs(projectRoot);
|
|
1515
|
+
for (const target of defaultTargets) {
|
|
1516
|
+
targetsByName.set(target.name, target);
|
|
1517
|
+
}
|
|
1518
|
+
return [...targetsByName.values()];
|
|
1519
|
+
}
|
|
1520
|
+
function repairableStatuses(statuses) {
|
|
1521
|
+
return statuses.filter(
|
|
1522
|
+
(status) => status.type === "symlink" && (status.status === "broken" || status.status === "outdated") && status.skill !== null
|
|
1523
|
+
);
|
|
1524
|
+
}
|
|
1525
|
+
function removableStatuses(statuses) {
|
|
1526
|
+
return statuses.filter(
|
|
1527
|
+
(status) => status.type === "symlink" && (status.status === "orphaned" || status.status === "broken" && status.skill === null)
|
|
1528
|
+
);
|
|
1529
|
+
}
|
|
1530
|
+
async function selectStatusesInteractive(statuses, action) {
|
|
1531
|
+
if (statuses.length === 0) {
|
|
1532
|
+
return [];
|
|
1533
|
+
}
|
|
1534
|
+
printActionHeader(action);
|
|
1535
|
+
return checkbox({
|
|
1536
|
+
message: `Select skills to ${action} (press Space to select, Enter to confirm):`,
|
|
1537
|
+
choices: statuses.map((status) => ({
|
|
1538
|
+
name: `${status.name} [${status.target.name}]`,
|
|
1539
|
+
value: status,
|
|
1540
|
+
checked: true
|
|
1541
|
+
}))
|
|
1542
|
+
});
|
|
1543
|
+
}
|
|
1544
|
+
function repairSelected({
|
|
1545
|
+
statuses,
|
|
1546
|
+
projectRoot
|
|
1547
|
+
}) {
|
|
1548
|
+
let repairedCount = 0;
|
|
1549
|
+
for (const status of statuses) {
|
|
1550
|
+
installSkill(status.skill, status.target.path);
|
|
1551
|
+
console.log(
|
|
1552
|
+
`Repaired: ${status.name} (${status.target.name}) -> ${displayPath(
|
|
1553
|
+
status.path,
|
|
1554
|
+
projectRoot
|
|
1555
|
+
)}`
|
|
1556
|
+
);
|
|
1557
|
+
repairedCount++;
|
|
1558
|
+
}
|
|
1559
|
+
return repairedCount;
|
|
1560
|
+
}
|
|
1561
|
+
function removeSelected({
|
|
1562
|
+
statuses,
|
|
1563
|
+
projectRoot
|
|
1564
|
+
}) {
|
|
1565
|
+
let removedCount = 0;
|
|
1566
|
+
for (const status of statuses) {
|
|
1567
|
+
if (uninstallSkill(status.name, status.target.path)) {
|
|
1568
|
+
console.log(
|
|
1569
|
+
`Removed ${status.status} symlink: ${status.name} (${status.target.name}) -> ${displayPath(
|
|
1570
|
+
status.path,
|
|
1571
|
+
projectRoot
|
|
1572
|
+
)}`
|
|
1573
|
+
);
|
|
1574
|
+
removedCount++;
|
|
1575
|
+
} else {
|
|
1576
|
+
console.log(`Not found: ${status.name} (${status.target.name})`);
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
return removedCount;
|
|
1580
|
+
}
|
|
1490
1581
|
function installSelected({
|
|
1491
1582
|
skills,
|
|
1492
1583
|
targets,
|
|
@@ -1520,9 +1611,12 @@ function installSelected({
|
|
|
1520
1611
|
async function sync(options) {
|
|
1521
1612
|
const context = getProjectContext();
|
|
1522
1613
|
const result = scanContext(context);
|
|
1523
|
-
let targets =
|
|
1524
|
-
|
|
1525
|
-
|
|
1614
|
+
let targets = syncTargetDirs({
|
|
1615
|
+
projectRoot: context.projectRoot,
|
|
1616
|
+
includeClaude: options.claude,
|
|
1617
|
+
yes: options.yes,
|
|
1618
|
+
check: options.check
|
|
1619
|
+
});
|
|
1526
1620
|
printContext(context);
|
|
1527
1621
|
console.log();
|
|
1528
1622
|
printWarnings(result.warnings);
|
|
@@ -1560,10 +1654,23 @@ async function sync(options) {
|
|
|
1560
1654
|
}
|
|
1561
1655
|
return;
|
|
1562
1656
|
}
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1657
|
+
const repairable = repairableStatuses(drift);
|
|
1658
|
+
const removable = removableStatuses(drift);
|
|
1659
|
+
if (drift.length > 0) {
|
|
1660
|
+
console.log();
|
|
1661
|
+
console.log("Some installed skills need attention.");
|
|
1662
|
+
console.log("Select the skills to install, repair, or remove.");
|
|
1663
|
+
console.log("Only managed symlinks will be changed.");
|
|
1664
|
+
}
|
|
1665
|
+
const selectedRepairs = options.yes ? repairable : await selectStatusesInteractive(repairable, "repair");
|
|
1666
|
+
const selectedRemovals = options.yes ? removable : await selectStatusesInteractive(removable, "remove");
|
|
1667
|
+
if (selectedRepairs.length > 0) {
|
|
1668
|
+
console.log();
|
|
1669
|
+
repairSelected({ statuses: selectedRepairs, projectRoot: context.projectRoot });
|
|
1670
|
+
}
|
|
1671
|
+
if (selectedRemovals.length > 0) {
|
|
1672
|
+
console.log();
|
|
1673
|
+
removeSelected({ statuses: selectedRemovals, projectRoot: context.projectRoot });
|
|
1567
1674
|
}
|
|
1568
1675
|
let selected = filterInstallableSkills({
|
|
1569
1676
|
skills: candidateSkills,
|
|
@@ -1595,6 +1702,9 @@ async function sync(options) {
|
|
|
1595
1702
|
console.log();
|
|
1596
1703
|
console.log(`Installed ${installedCount} skill target(s).`);
|
|
1597
1704
|
}
|
|
1705
|
+
if (selectedRepairs.length === 0 && selectedRemovals.length === 0 && selected.length === 0) {
|
|
1706
|
+
console.log("No changes needed.");
|
|
1707
|
+
}
|
|
1598
1708
|
}
|
|
1599
1709
|
function scanCommand(options) {
|
|
1600
1710
|
const context = getProjectContext();
|
|
@@ -1728,7 +1838,7 @@ async function removeCommand(skillNames, options) {
|
|
|
1728
1838
|
function createProgram() {
|
|
1729
1839
|
const program = new Command().enablePositionalOptions();
|
|
1730
1840
|
program.name("library-skills").description(
|
|
1731
|
-
"Discover and
|
|
1841
|
+
"Discover and reconcile agent skills from installed library packages."
|
|
1732
1842
|
).option("--claude", "Also manage .claude/skills alongside .agents/skills").option("-y, --yes", "Skip confirmation prompts").option("--check", "Validate only; exit 1 if installs drift").option("--all", "Install all newly discovered unmanaged skills").option(
|
|
1733
1843
|
"-s, --skill <name>",
|
|
1734
1844
|
"Install a specific discovered skill by name",
|
|
@@ -1792,7 +1902,13 @@ var testing = {
|
|
|
1792
1902
|
installSelected,
|
|
1793
1903
|
installedStatuses,
|
|
1794
1904
|
listCommand,
|
|
1905
|
+
removableStatuses,
|
|
1906
|
+
removeSelected,
|
|
1907
|
+
repairSelected,
|
|
1908
|
+
repairableStatuses,
|
|
1795
1909
|
scanCommand,
|
|
1910
|
+
selectStatusesInteractive,
|
|
1911
|
+
syncTargetDirs,
|
|
1796
1912
|
sync,
|
|
1797
1913
|
topLevelSkills,
|
|
1798
1914
|
displayPath,
|