skiller 0.9.4 → 0.9.5

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.
@@ -86,6 +86,88 @@ function formatMatch(match) {
86
86
  const source = match.source || match.slug;
87
87
  return `${source}@${match.name} (${formatInstalls(match.installs)})`;
88
88
  }
89
+ async function readJsonObject(filePath) {
90
+ try {
91
+ const raw = JSON.parse(await fs.readFile(filePath, 'utf8'));
92
+ return raw && typeof raw === 'object'
93
+ ? raw
94
+ : null;
95
+ }
96
+ catch {
97
+ return null;
98
+ }
99
+ }
100
+ async function cleanupLegacyClaudePluginState(projectRoot, pluginIds) {
101
+ const pluginIdSet = new Set(pluginIds);
102
+ if (pluginIdSet.size === 0)
103
+ return;
104
+ const settingsPath = path.join(projectRoot, '.claude', 'settings.json');
105
+ const settings = await readJsonObject(settingsPath);
106
+ if (settings) {
107
+ const enabledPlugins = settings.enabledPlugins;
108
+ if (enabledPlugins && typeof enabledPlugins === 'object') {
109
+ const nextEnabledPlugins = Object.fromEntries(Object.entries(enabledPlugins).filter(([pluginId]) => !pluginIdSet.has(pluginId)));
110
+ if (Object.keys(nextEnabledPlugins).length !==
111
+ Object.keys(enabledPlugins).length) {
112
+ if (Object.keys(nextEnabledPlugins).length === 0) {
113
+ delete settings.enabledPlugins;
114
+ }
115
+ else {
116
+ settings.enabledPlugins = nextEnabledPlugins;
117
+ }
118
+ await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2) + '\n');
119
+ }
120
+ }
121
+ }
122
+ for (const manifestPath of [
123
+ path.join(projectRoot, '.agents', '.skiller.json'),
124
+ path.join(projectRoot, '.claude', '.skiller.json'),
125
+ ]) {
126
+ const manifest = await readJsonObject(manifestPath);
127
+ if (!manifest)
128
+ continue;
129
+ const targets = manifest.targets;
130
+ if (!targets || typeof targets !== 'object')
131
+ continue;
132
+ let changed = false;
133
+ const nextTargets = {};
134
+ for (const [targetKey, rawEntries] of Object.entries(targets)) {
135
+ if (!Array.isArray(rawEntries)) {
136
+ nextTargets[targetKey] = rawEntries;
137
+ continue;
138
+ }
139
+ const filteredEntries = rawEntries.filter((entry) => {
140
+ if (!entry || typeof entry !== 'object')
141
+ return true;
142
+ const sourceType = entry.sourceType;
143
+ const pluginId = entry.pluginId;
144
+ if (sourceType !== 'plugin' || typeof pluginId !== 'string') {
145
+ return true;
146
+ }
147
+ return !pluginIdSet.has(pluginId);
148
+ });
149
+ if (filteredEntries.length !== rawEntries.length) {
150
+ changed = true;
151
+ }
152
+ if (filteredEntries.length > 0) {
153
+ nextTargets[targetKey] = filteredEntries;
154
+ }
155
+ else {
156
+ changed = true;
157
+ }
158
+ }
159
+ if (!changed)
160
+ continue;
161
+ manifest.targets = nextTargets;
162
+ const localSkills = manifest.localSkills;
163
+ const hasLocalSkills = Array.isArray(localSkills) && localSkills.length > 0;
164
+ if (Object.keys(nextTargets).length === 0 && !hasLocalSkills) {
165
+ await fs.rm(manifestPath, { force: true });
166
+ continue;
167
+ }
168
+ await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
169
+ }
170
+ }
89
171
  async function promptLine(message) {
90
172
  const rl = readline.createInterface({
91
173
  input: process.stdin,
@@ -329,10 +411,14 @@ async function migrateClaudePluginsHandler(argv) {
329
411
  for (const install of plan.installs) {
330
412
  await (0, skills_cli_1.runSkillsCli)(projectRoot, buildClaudePluginMigrationArgs(install.source));
331
413
  }
414
+ await cleanupLegacyClaudePluginState(projectRoot, [
415
+ ...plan.installs.flatMap((install) => install.pluginIds),
416
+ ...plan.unresolved.map((entry) => entry.pluginId),
417
+ ]);
332
418
  if (plan.unresolved.length > 0) {
333
419
  console.log(`[skiller] Skipped unresolved plugins:\n${plan.unresolved.map((entry) => `- ${entry.pluginId}: ${entry.reason}`).join('\n')}`);
334
420
  }
335
- console.log('[skiller] Claude plugin repo migration completed. Remove the plugin entries from .claude/settings.json, then rerun skiller apply.');
421
+ console.log('[skiller] Claude plugin repo migration completed. Legacy Claude plugin entries were removed from settings/manifests; rerun skiller apply.');
336
422
  }
337
423
  catch (err) {
338
424
  const message = err instanceof Error ? err.message : String(err);
@@ -218,6 +218,27 @@ async function planDirectoryMigration(sourceDir, destinationDir, plannedWrites,
218
218
  deletePaths.add(sourceDir);
219
219
  }
220
220
  }
221
+ async function planLegacySkillsMigration(legacySkillsDir, canonicalSkillsDir, plannedWrites, deletePaths, conflicts) {
222
+ if (!(await pathExists(legacySkillsDir)))
223
+ return;
224
+ const entries = await fs.readdir(legacySkillsDir, { withFileTypes: true });
225
+ for (const entry of entries) {
226
+ if (!entry.isDirectory())
227
+ continue;
228
+ const sourceDir = path.join(legacySkillsDir, entry.name);
229
+ const destinationDir = path.join(canonicalSkillsDir, entry.name);
230
+ if (await pathExists(destinationDir)) {
231
+ // Canonical .agents/skills already owns this name now. Legacy
232
+ // .claude/skills duplicates are stale migration debris.
233
+ deletePaths.add(sourceDir);
234
+ continue;
235
+ }
236
+ await planDirectoryMigration(sourceDir, destinationDir, plannedWrites, deletePaths, conflicts);
237
+ }
238
+ if (conflicts.length === 0) {
239
+ deletePaths.add(legacySkillsDir);
240
+ }
241
+ }
221
242
  async function findRuleSkillFolders(dir) {
222
243
  const folders = [];
223
244
  const entries = await fs.readdir(dir, { withFileTypes: true });
@@ -395,7 +416,7 @@ async function migrateLegacyProjectState(projectRoot, dryRun) {
395
416
  }
396
417
  }
397
418
  await planFileMigration(path.join(legacyDir, project_paths_1.PROJECT_AGENTS_FILE), path.join(canonicalDir, project_paths_1.PROJECT_AGENTS_FILE), plannedWrites, deletePaths, conflicts);
398
- await planDirectoryMigration(path.join(legacyDir, 'skills'), canonicalSkillsDir, plannedWrites, deletePaths, conflicts);
419
+ await planLegacySkillsMigration(path.join(legacyDir, 'skills'), canonicalSkillsDir, plannedWrites, deletePaths, conflicts);
399
420
  await planLegacyRulesMigration(path.join(legacyDir, 'rules'), canonicalRulesDir, canonicalSkillsDir, plannedWrites, deletePaths, conflicts);
400
421
  if (conflicts.length > 0) {
401
422
  throw new Error(`Legacy .claude migration conflicts:\n- ${conflicts.join('\n- ')}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skiller",
3
- "version": "0.9.4",
3
+ "version": "0.9.5",
4
4
  "description": "Skiller — apply the same rules to all coding agents",
5
5
  "main": "dist/lib.js",
6
6
  "publishConfig": {