luckiest-co 1.0.34 → 1.0.35

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.
@@ -9,7 +9,7 @@
9
9
  "name": "luckiest",
10
10
  "source": "./",
11
11
  "description": "Plan, go, finish, plus bundled free Luckiest skills that work in Claude Code, web chat, and Cowork.",
12
- "version": "0.1.33",
12
+ "version": "0.1.34",
13
13
  "author": { "name": "Luckiest" }
14
14
  }
15
15
  ]
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "luckiest",
3
3
  "description": "Plan, go, finish. Guided planning, progress dashboards, and your luckiest.co tribe inside Claude Code.",
4
- "version": "0.1.33",
4
+ "version": "0.1.34",
5
5
  "author": { "name": "Luckiest" }
6
6
  }
package/bin/install.js CHANGED
@@ -224,6 +224,58 @@ function placeSkills(extractDir, skillsRoot, fallbackName) {
224
224
  }
225
225
 
226
226
  async function syncSkills() {
227
+ try {
228
+ await syncOwnedSkills();
229
+ } finally {
230
+ // Self-heal the luckiest-x + luckiest:luckiest-x duplicate: the marketplace
231
+ // plugin already registers its skills, so a copy in ~/.claude/skills (from
232
+ // older installers or from this sync) lists every one of them twice.
233
+ const globalDir = expandTilde(explicitConfigDir) || expandTilde(process.env.CLAUDE_CONFIG_DIR) || path.join(os.homedir(), '.claude');
234
+ const removed = dropPluginDuplicates(path.join(globalDir, 'skills'), pluginSkillNames(globalDir));
235
+ if (removed.length) {
236
+ console.log(` ${green}✓${reset} Removed ${removed.length} duplicate skill(s) from skills/ (plugin marketplace already provides them)`);
237
+ }
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Skill folder names shipped by the enabled marketplace plugin, read from its
243
+ * installPath. Empty when the plugin is absent or disabled.
244
+ */
245
+ function pluginSkillNames(globalClaudeDir) {
246
+ const names = new Set();
247
+ if (!hasMarketplacePlugin(globalClaudeDir)) return names;
248
+ try {
249
+ const manifest = JSON.parse(
250
+ fs.readFileSync(path.join(globalClaudeDir, 'plugins', 'installed_plugins.json'), 'utf8')
251
+ );
252
+ for (const [key, value] of Object.entries(manifest.plugins || {})) {
253
+ if (!key.startsWith('luckiest@')) continue;
254
+ for (const item of Array.isArray(value) ? value : [value]) {
255
+ const dir = item && item.installPath && path.join(item.installPath, 'skills');
256
+ if (dir && fs.existsSync(dir)) fs.readdirSync(dir).forEach((n) => names.add(n));
257
+ }
258
+ }
259
+ } catch {
260
+ // Unreadable manifest: remove nothing.
261
+ }
262
+ return names;
263
+ }
264
+
265
+ /** Remove skills/<name> for every name the plugin ships. Returns removed names. */
266
+ function dropPluginDuplicates(skillsRoot, names) {
267
+ const removed = [];
268
+ for (const name of names) {
269
+ const dest = path.join(skillsRoot, name);
270
+ if (fs.existsSync(path.join(dest, 'SKILL.md'))) {
271
+ fs.rmSync(dest, { recursive: true, force: true });
272
+ removed.push(name);
273
+ }
274
+ }
275
+ return removed;
276
+ }
277
+
278
+ async function syncOwnedSkills() {
227
279
  // Self-heal the /luckiest:luckiest:* duplicate: older installers copied
228
280
  // commands into ~/.claude/commands/luckiest even when the marketplace
229
281
  // plugin already registers them. Sync runs every session, so clean it here.
@@ -428,8 +480,12 @@ function install(isGlobal) {
428
480
  }
429
481
 
430
482
  // Copy references/, templates/, .claude-plugin/ into the target root
483
+ // skills/ is skipped when the marketplace plugin ships them, for the same
484
+ // reason as commands/ above (luckiest-x and luckiest:luckiest-x).
485
+ const pluginPresent = hasMarketplacePlugin(defaultGlobalDir);
431
486
  const topLevelDirs = ['references', 'templates', 'skills', '.claude-plugin', 'hooks'];
432
487
  for (const dir of topLevelDirs) {
488
+ if (dir === 'skills' && pluginPresent) continue;
433
489
  const dirSrc = path.join(src, dir);
434
490
  const dirDest = path.join(claudeDir, dir);
435
491
  if (fs.existsSync(dirSrc)) {
@@ -718,4 +774,4 @@ if (require.main === module) {
718
774
  });
719
775
  }
720
776
 
721
- module.exports = { placeSkills };
777
+ module.exports = { placeSkills, dropPluginDuplicates };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "luckiest-co",
3
- "version": "1.0.34",
3
+ "version": "1.0.35",
4
4
  "description": "Luckiest for Claude Code: plan, go, finish. Your luckiest.co skills and tribe, inside Claude.",
5
5
  "bin": {
6
6
  "luckiest-co": "./bin/install.js"