skiller 0.7.21 → 0.7.23
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/index.js +0 -0
- package/dist/core/ClaudePluginSync.js +63 -5
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
File without changes
|
|
@@ -143,6 +143,50 @@ async function resolvePluginMarketplaceRoot(pluginId, claudeDir) {
|
|
|
143
143
|
}
|
|
144
144
|
return null;
|
|
145
145
|
}
|
|
146
|
+
async function hasPluginContent(root) {
|
|
147
|
+
const candidates = [
|
|
148
|
+
path.join(root, 'skills'),
|
|
149
|
+
path.join(root, 'commands'),
|
|
150
|
+
path.join(root, 'agents'),
|
|
151
|
+
];
|
|
152
|
+
for (const candidate of candidates) {
|
|
153
|
+
if (await dirExists(candidate))
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
async function readPluginPackageManifestEntries(installPath) {
|
|
159
|
+
const packageJsonPath = path.join(installPath, 'package.json');
|
|
160
|
+
try {
|
|
161
|
+
const raw = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
|
|
162
|
+
if (!Array.isArray(raw.plugins))
|
|
163
|
+
return [];
|
|
164
|
+
return raw.plugins.filter((entry) => Boolean(entry) && typeof entry === 'object');
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
return [];
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
async function resolveInstalledPluginSourceRoot(pluginId, installPath) {
|
|
171
|
+
const manifestEntries = await readPluginPackageManifestEntries(installPath);
|
|
172
|
+
const parsedPluginId = parsePluginId(pluginId);
|
|
173
|
+
const matchingEntry = manifestEntries.find((entry) => entry.name === parsedPluginId?.pluginName) ?? (manifestEntries.length === 1 ? manifestEntries[0] : null);
|
|
174
|
+
const rawSource = typeof matchingEntry?.source === 'string' &&
|
|
175
|
+
matchingEntry.source.trim() !== ''
|
|
176
|
+
? matchingEntry.source
|
|
177
|
+
: '.';
|
|
178
|
+
const sourceRoot = path.resolve(installPath, rawSource);
|
|
179
|
+
const normalizedInstallPath = path.resolve(installPath);
|
|
180
|
+
const isWithinInstallPath = sourceRoot === normalizedInstallPath ||
|
|
181
|
+
sourceRoot.startsWith(normalizedInstallPath + path.sep);
|
|
182
|
+
if (isWithinInstallPath && (await hasPluginContent(sourceRoot))) {
|
|
183
|
+
return sourceRoot;
|
|
184
|
+
}
|
|
185
|
+
if (await hasPluginContent(installPath)) {
|
|
186
|
+
return installPath;
|
|
187
|
+
}
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
146
190
|
function resolvePluginInstall(pluginId, projectRoot, index) {
|
|
147
191
|
const entries = index.plugins?.[pluginId];
|
|
148
192
|
if (!entries || !Array.isArray(entries) || entries.length === 0)
|
|
@@ -622,21 +666,27 @@ async function syncClaudePluginsToSkillsDirs(args) {
|
|
|
622
666
|
const resolvedSources = [];
|
|
623
667
|
const unresolvedEnabled = new Set();
|
|
624
668
|
for (const pluginId of enabledPlugins) {
|
|
625
|
-
const
|
|
669
|
+
const resolved = index
|
|
670
|
+
? resolvePluginInstall(pluginId, projectRoot, index)
|
|
671
|
+
: null;
|
|
672
|
+
const marketplaceRoot = await resolvePluginMarketplaceRoot(pluginId, claudeDir);
|
|
673
|
+
const pluginRoot = (marketplaceRoot && (await hasPluginContent(marketplaceRoot))
|
|
674
|
+
? marketplaceRoot
|
|
675
|
+
: null) ??
|
|
676
|
+
(resolved
|
|
677
|
+
? await resolveInstalledPluginSourceRoot(pluginId, resolved.installPath)
|
|
678
|
+
: null);
|
|
626
679
|
if (!pluginRoot) {
|
|
627
680
|
unresolvedEnabled.add(pluginId);
|
|
628
681
|
const hasIndexEntry = Boolean(index?.plugins?.[pluginId]?.length);
|
|
629
682
|
if (hasIndexEntry) {
|
|
630
|
-
(0, constants_1.logVerboseInfo)(`[plugins] Enabled plugin has no
|
|
683
|
+
(0, constants_1.logVerboseInfo)(`[plugins] Enabled plugin has no syncable content, skipping: ${pluginId}`, verbose, dryRun);
|
|
631
684
|
}
|
|
632
685
|
else {
|
|
633
686
|
(0, constants_1.logWarn)(`[plugins] Enabled plugin not installed: ${pluginId}`, dryRun);
|
|
634
687
|
}
|
|
635
688
|
continue;
|
|
636
689
|
}
|
|
637
|
-
const resolved = index
|
|
638
|
-
? resolvePluginInstall(pluginId, projectRoot, index)
|
|
639
|
-
: null;
|
|
640
690
|
resolvedSources.push({
|
|
641
691
|
pluginId,
|
|
642
692
|
pluginRoot,
|
|
@@ -742,6 +792,7 @@ async function syncClaudePluginsToSkillsDirs(args) {
|
|
|
742
792
|
continue;
|
|
743
793
|
const desiredPrefix = pluginNamespacePrefixByPluginId.get(item.pluginId) ??
|
|
744
794
|
sanitizeId(item.pluginId);
|
|
795
|
+
const currentNamespacedBase = `${desiredPrefix}-${item.baseName}`;
|
|
745
796
|
// Migration: previous versions used `${pluginId}__${name}`, then
|
|
746
797
|
// `${pluginId}-${name}`. If we changed the namespace prefix (for example
|
|
747
798
|
// to omit marketplace), don't preserve the old destination so the item
|
|
@@ -751,6 +802,13 @@ async function syncClaudePluginsToSkillsDirs(args) {
|
|
|
751
802
|
if (prev.startsWith(`${sanitizeId(item.pluginId)}-`) &&
|
|
752
803
|
desiredPrefix !== sanitizeId(item.pluginId))
|
|
753
804
|
continue;
|
|
805
|
+
// If the item was previously namespaced only because its base name was
|
|
806
|
+
// unavailable, drop that sticky destination once the base name is free.
|
|
807
|
+
if ((prev === currentNamespacedBase ||
|
|
808
|
+
prev.startsWith(`${currentNamespacedBase}-`)) &&
|
|
809
|
+
!taken.has(item.baseName)) {
|
|
810
|
+
continue;
|
|
811
|
+
}
|
|
754
812
|
if (taken.has(prev))
|
|
755
813
|
continue;
|
|
756
814
|
assignedDestByItemKey.set(item.itemKey, prev);
|