luckiest-co 1.0.23 → 1.0.24

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.22",
12
+ "version": "0.1.23",
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.22",
4
+ "version": "0.1.23",
5
5
  "author": { "name": "Luckiest" }
6
6
  }
package/bin/install.js CHANGED
@@ -64,7 +64,7 @@ function parseConfigDirArg() {
64
64
  const explicitConfigDir = parseConfigDirArg();
65
65
  const hasHelp = args.includes('--help') || args.includes('-h');
66
66
 
67
- console.log(banner);
67
+ if (require.main === module) console.log(banner);
68
68
 
69
69
  // Show help if requested
70
70
  if (hasHelp) {
@@ -201,6 +201,28 @@ function hasUnzip() {
201
201
  * Sync owned skills from luckiest.co into ~/.claude/skills/<name>/.
202
202
  * Never logs the key or the Authorization header.
203
203
  */
204
+ /**
205
+ * Move extracted skill folders into skillsRoot, replacing older copies.
206
+ * Published zips hold one top-level <skill>/ folder per skill (a bundle holds
207
+ * several). Older zips put SKILL.md at the root; those install as fallbackName.
208
+ * Returns the names placed. Entries without a SKILL.md or with an unsafe name
209
+ * are skipped.
210
+ */
211
+ function placeSkills(extractDir, skillsRoot, fallbackName) {
212
+ const moves = fs.existsSync(path.join(extractDir, 'SKILL.md'))
213
+ ? [[extractDir, fallbackName]]
214
+ : fs.readdirSync(extractDir, { withFileTypes: true })
215
+ .filter((e) => e.isDirectory() && !e.name.startsWith('.'))
216
+ .filter((e) => fs.existsSync(path.join(extractDir, e.name, 'SKILL.md')))
217
+ .map((e) => [path.join(extractDir, e.name), e.name]);
218
+ for (const [src, name] of moves) {
219
+ const dest = path.join(skillsRoot, name);
220
+ fs.rmSync(dest, { recursive: true, force: true });
221
+ fs.renameSync(src, dest);
222
+ }
223
+ return moves.map(([, name]) => name);
224
+ }
225
+
204
226
  async function syncSkills() {
205
227
  // Self-heal the /luckiest:luckiest:* duplicate: older installers copied
206
228
  // commands into ~/.claude/commands/luckiest even when the marketplace
@@ -273,15 +295,23 @@ async function syncSkills() {
273
295
  const buf = Buffer.from(await zipRes.arrayBuffer());
274
296
 
275
297
  if (unzipAvailable) {
276
- const dest = path.join(skillsRoot, safeName);
277
- fs.mkdirSync(dest, { recursive: true });
278
- const tmpZip = path.join(os.tmpdir(), `luckiest-skill-${safeName}-${Date.now()}.zip`);
279
- fs.writeFileSync(tmpZip, buf);
298
+ // Extract into a private temp dir under skillsRoot (same disk, so the
299
+ // final move is a rename), then place each skill folder at the top level.
300
+ const tmpDir = fs.mkdtempSync(path.join(skillsRoot, '.luckiest-sync-'));
280
301
  try {
281
- execFileSync('unzip', ['-q', '-o', tmpZip, '-d', dest]);
282
- results.push({ name, version: version || 'unknown', path: dest });
302
+ const tmpZip = path.join(tmpDir, 'skill.zip');
303
+ fs.writeFileSync(tmpZip, buf);
304
+ const extractDir = path.join(tmpDir, 'x');
305
+ execFileSync('unzip', ['-q', tmpZip, '-d', extractDir]);
306
+ for (const placed of placeSkills(extractDir, skillsRoot, safeName)) {
307
+ results.push({
308
+ name: placed,
309
+ version: placed === safeName ? (version || 'unknown') : `bundled with ${safeName}`,
310
+ path: path.join(skillsRoot, placed),
311
+ });
312
+ }
283
313
  } finally {
284
- fs.unlinkSync(tmpZip);
314
+ fs.rmSync(tmpDir, { recursive: true, force: true });
285
315
  }
286
316
  } else {
287
317
  const zipPath = path.join(skillsRoot, `${safeName}.zip`);
@@ -681,7 +711,11 @@ async function main() {
681
711
  await syncSkills();
682
712
  }
683
713
 
684
- main().catch((err) => {
685
- console.error(` ${red}Install failed: ${err.message}${reset}`);
686
- process.exit(1);
687
- });
714
+ if (require.main === module) {
715
+ main().catch((err) => {
716
+ console.error(` ${red}Install failed: ${err.message}${reset}`);
717
+ process.exit(1);
718
+ });
719
+ }
720
+
721
+ module.exports = { placeSkills };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "luckiest-co",
3
- "version": "1.0.23",
3
+ "version": "1.0.24",
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"
@@ -14,7 +14,8 @@
14
14
  "hooks",
15
15
  ".claude-plugin",
16
16
  "!**/__pycache__",
17
- "!**/*.pyc"
17
+ "!**/*.pyc",
18
+ "!bin/*.test.js"
18
19
  ],
19
20
  "license": "SEE LICENSE IN LICENSE",
20
21
  "engines": {