team-skills 1.3.6 → 1.3.7
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/CHANGELOG.md +6 -0
- package/package.json +1 -1
- package/src/lib/fs-utils.js +7 -1
- package/src/lib/installers.js +58 -14
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
package/src/lib/fs-utils.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
mkdirSync, symlinkSync, unlinkSync, readlinkSync,
|
|
3
3
|
lstatSync, existsSync, readdirSync, statSync,
|
|
4
|
-
copyFileSync, chmodSync, rmSync,
|
|
4
|
+
copyFileSync, chmodSync, rmSync, realpathSync,
|
|
5
5
|
} from 'node:fs';
|
|
6
6
|
import { join, dirname } from 'node:path';
|
|
7
7
|
|
|
@@ -25,6 +25,12 @@ export function createSymlinkSafe(source, target, { force = false, dryRun = fals
|
|
|
25
25
|
if (!force) return 'conflict';
|
|
26
26
|
unlinkSync(target);
|
|
27
27
|
} else if (existsSync(target)) {
|
|
28
|
+
// 目标存在但不是软连接 — 解析真实路径,如果是同一文件则视为已安装
|
|
29
|
+
try {
|
|
30
|
+
if (realpathSync(target) === realpathSync(source)) return 'exists';
|
|
31
|
+
} catch {
|
|
32
|
+
// 任一无法解析,继续走 force 逻辑
|
|
33
|
+
}
|
|
28
34
|
if (!force) return 'conflict';
|
|
29
35
|
rmSync(target, { recursive: true });
|
|
30
36
|
}
|
package/src/lib/installers.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
|
-
import { existsSync, readdirSync, rmSync, copyFileSync } from 'node:fs';
|
|
2
|
+
import { existsSync, readdirSync, rmSync, copyFileSync, realpathSync } from 'node:fs';
|
|
3
3
|
import { createSymlinkSafe, ensureDir, isSymlink, copyRecursive } from './fs-utils.js';
|
|
4
4
|
import { GLOBAL_TARGETS, PROJECT_IDE_DIRS } from './constants.js';
|
|
5
5
|
import * as log from './logger.js';
|
|
@@ -56,21 +56,44 @@ export function verifyGlobalSymlinks(targets, skills, rules) {
|
|
|
56
56
|
log.heading('验证安装');
|
|
57
57
|
let errors = 0;
|
|
58
58
|
|
|
59
|
-
const verify = (label, dest) => {
|
|
60
|
-
if (isSymlink(dest)) {
|
|
61
|
-
log.success(label);
|
|
62
|
-
} else {
|
|
63
|
-
log.error(`${label} 未正确安装`);
|
|
64
|
-
errors++;
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
|
|
68
59
|
for (const t of targets) {
|
|
69
60
|
for (const skill of skills) {
|
|
70
|
-
|
|
61
|
+
const dest = join(t.dir, skill.name);
|
|
62
|
+
const label = `${t.label} Skill: ${skill.name}`;
|
|
63
|
+
if (isSymlink(dest)) {
|
|
64
|
+
log.success(label);
|
|
65
|
+
} else if (existsSync(dest)) {
|
|
66
|
+
try {
|
|
67
|
+
if (realpathSync(dest) === realpathSync(skill.path)) {
|
|
68
|
+
log.skip(`${label}(已存在,跳过)`);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
} catch { /* 忽略解析失败 */ }
|
|
72
|
+
log.error(`${label} 未正确安装`);
|
|
73
|
+
errors++;
|
|
74
|
+
} else {
|
|
75
|
+
log.error(`${label} 未正确安装`);
|
|
76
|
+
errors++;
|
|
77
|
+
}
|
|
71
78
|
}
|
|
72
79
|
for (const rule of rules) {
|
|
73
|
-
|
|
80
|
+
const dest = join(t.dir, '_team-rules', rule.name);
|
|
81
|
+
const label = `${t.label} Rule: ${rule.name}`;
|
|
82
|
+
if (isSymlink(dest)) {
|
|
83
|
+
log.success(label);
|
|
84
|
+
} else if (existsSync(dest)) {
|
|
85
|
+
try {
|
|
86
|
+
if (realpathSync(dest) === realpathSync(rule.path)) {
|
|
87
|
+
log.skip(`${label}(已存在,跳过)`);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
} catch { /* 忽略解析失败 */ }
|
|
91
|
+
log.error(`${label} 未正确安装`);
|
|
92
|
+
errors++;
|
|
93
|
+
} else {
|
|
94
|
+
log.error(`${label} 未正确安装`);
|
|
95
|
+
errors++;
|
|
96
|
+
}
|
|
74
97
|
}
|
|
75
98
|
}
|
|
76
99
|
|
|
@@ -98,7 +121,16 @@ export function installSkillsProject(projectDir, ides, skills, rules, { dryRun,
|
|
|
98
121
|
for (const skill of skills) {
|
|
99
122
|
const dest = join(skillsDst, skill.name);
|
|
100
123
|
if (!dryRun) {
|
|
101
|
-
|
|
124
|
+
// 如果目标就是源文件本身,跳过删除+复制(否则删了就复制不了)
|
|
125
|
+
if (existsSync(dest)) {
|
|
126
|
+
try {
|
|
127
|
+
if (realpathSync(dest) === realpathSync(skill.path)) {
|
|
128
|
+
log.skip(`${tag}Skill: ${skill.name}(自身,跳过)`);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
} catch { /* 忽略解析失败 */ }
|
|
132
|
+
rmSync(dest, { recursive: true });
|
|
133
|
+
}
|
|
102
134
|
copyRecursive(skill.path, dest);
|
|
103
135
|
}
|
|
104
136
|
log.success(`${tag}Skill: ${skill.name}`);
|
|
@@ -108,7 +140,19 @@ export function installSkillsProject(projectDir, ides, skills, rules, { dryRun,
|
|
|
108
140
|
const rulesDst = join(skillsDst, '_team-rules');
|
|
109
141
|
if (!dryRun) ensureDir(rulesDst);
|
|
110
142
|
for (const r of rules) {
|
|
111
|
-
if (!dryRun)
|
|
143
|
+
if (!dryRun) {
|
|
144
|
+
const ruleDest = join(rulesDst, r.name);
|
|
145
|
+
// 如果目标就是源文件本身,跳过复制
|
|
146
|
+
if (existsSync(ruleDest)) {
|
|
147
|
+
try {
|
|
148
|
+
if (realpathSync(ruleDest) === realpathSync(r.path)) {
|
|
149
|
+
log.skip(`${tag}Rule: ${r.name}(自身,跳过)`);
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
} catch { /* 忽略解析失败 */ }
|
|
153
|
+
}
|
|
154
|
+
copyFileSync(r.path, ruleDest);
|
|
155
|
+
}
|
|
112
156
|
log.success(`${tag}Rule: ${r.name}`);
|
|
113
157
|
count++;
|
|
114
158
|
}
|