|
1 | 1 | #!/usr/bin/env node |
2 | 2 |
|
3 | | -import { existsSync, mkdirSync, cpSync, readdirSync, readFileSync, writeFileSync, copyFileSync, statSync } from 'fs'; |
| 3 | +import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, copyFileSync, lstatSync, realpathSync } from 'fs'; |
4 | 4 | import { resolve, dirname, join } from 'path'; |
5 | 5 | import { fileURLToPath } from 'url'; |
6 | 6 |
|
7 | | -// Node 14/16 兼容:cpSync 在 Node 16.7+ 才可用 |
| 7 | +// 手动递归复制:跨 Node 版本和操作系统行为一致 |
| 8 | +// 不使用 cpSync —— 在 Windows + npx 缓存(含 junction)+ Node 16.7-18 下不稳定 |
8 | 9 | function copyDirSync(src, dest) { |
9 | | - if (typeof cpSync === 'function') { |
10 | | - cpSync(src, dest, { recursive: true }); |
11 | | - return; |
12 | | - } |
13 | | - // 手动递归复制(兼容 Node 14+) |
| 10 | + // 解析 junction/symlink,避免 Windows npx 缓存路径下 readdir 返回空 |
| 11 | + let realSrc = src; |
| 12 | + try { realSrc = realpathSync(src); } catch {} |
| 13 | + |
14 | 14 | mkdirSync(dest, { recursive: true }); |
15 | | - for (const entry of readdirSync(src, { withFileTypes: true })) { |
16 | | - const srcPath = join(src, entry.name); |
| 15 | + const entries = readdirSync(realSrc, { withFileTypes: true }); |
| 16 | + for (const entry of entries) { |
| 17 | + const srcPath = join(realSrc, entry.name); |
17 | 18 | const destPath = join(dest, entry.name); |
18 | | - if (entry.isDirectory()) { |
| 19 | + let stat; |
| 20 | + try { stat = lstatSync(srcPath); } catch { continue; } |
| 21 | + if (stat.isSymbolicLink()) { |
| 22 | + // 取消引用后按实际类型处理 |
| 23 | + try { |
| 24 | + const real = realpathSync(srcPath); |
| 25 | + const realStat = lstatSync(real); |
| 26 | + if (realStat.isDirectory()) copyDirSync(real, destPath); |
| 27 | + else copyFileSync(real, destPath); |
| 28 | + } catch {} |
| 29 | + } else if (stat.isDirectory()) { |
19 | 30 | copyDirSync(srcPath, destPath); |
20 | | - } else { |
| 31 | + } else if (stat.isFile()) { |
21 | 32 | copyFileSync(srcPath, destPath); |
22 | 33 | } |
23 | 34 | } |
@@ -340,9 +351,19 @@ function showHelp() { |
340 | 351 |
|
341 | 352 | function installForTarget(target) { |
342 | 353 | const dest = resolve(PROJECT_DIR, target.dir); |
| 354 | + const srcCount = countDirs(SKILLS_SRC); |
343 | 355 | mkdirSync(dest, { recursive: true }); |
344 | 356 | copyDirSync(SKILLS_SRC, dest); |
345 | 357 | const count = countDirs(dest); |
| 358 | + if (srcCount > 0 && count === 0) { |
| 359 | + throw new Error( |
| 360 | + `复制 skills 失败:源目录 ${SKILLS_SRC} 有 ${srcCount} 个 skill,但目标 ${dest} 为空。` + |
| 361 | + `\n 这通常是 npx 缓存目录权限或路径问题。请尝试:\n` + |
| 362 | + ` 1. 清理缓存后重试: npm cache clean --force && npx superpowers-zh\n` + |
| 363 | + ` 2. 或全局安装: npm i -g superpowers-zh && superpowers-zh\n` + |
| 364 | + ` 3. 或手动克隆复制: 见 https://github.com/jnMetaCode/superpowers-zh#方式二手动安装` |
| 365 | + ); |
| 366 | + } |
346 | 367 | console.log(` ✅ ${target.name}: ${count} 个 skills -> ${dest}`); |
347 | 368 |
|
348 | 369 | if (target.name === 'Trae') { |
|
0 commit comments