6a-spec-install 1.0.1-dev.1 → 1.0.1-dev.3

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/README.md CHANGED
@@ -7,19 +7,32 @@
7
7
  ### 安装
8
8
 
9
9
  ```bash
10
- # 安装 Cursor 配置(默认)
10
+ # 安装 Cursor 配置(默认,完全覆盖模式)
11
11
  npx 6a-spec-install
12
12
 
13
- # 安装 Cursor 配置
14
- npx 6a-spec-install --tool cursor
13
+ # 智能合并模式(推荐,保留用户自定义文件)
14
+ npx 6a-spec-install --tool cursor --merge
15
15
 
16
- # 安装 Claude 配置
17
- npx 6a-spec-install --tool claude
16
+ # 安装 Claude 配置(合并模式)
17
+ npx 6a-spec-install --tool claude --merge
18
18
 
19
19
  # 同时安装两种工具
20
- npx 6a-spec-install --tool all
20
+ npx 6a-spec-install --tool all --merge
21
21
  ```
22
22
 
23
+ ### 安装模式
24
+
25
+ #### 完全覆盖模式(默认)
26
+ - 会先备份现有配置到 `.cursor.backup.时间戳`
27
+ - 完全覆盖目标目录
28
+ - 适合首次安装或需要完全重置配置
29
+
30
+ #### 智能合并模式(`--merge`)
31
+ - **只更新包中存在的文件**,保留用户新增的自定义文件
32
+ - 不会创建备份(因为不会删除文件)
33
+ - 适合更新配置时保留自定义修改
34
+ - 会显示更新和新增的文件统计
35
+
23
36
  ### 全局安装(可选)
24
37
 
25
38
  ```bash
@@ -33,7 +46,8 @@ npm install -g 6a-spec-install
33
46
 
34
47
  - ✅ 支持 Cursor IDE
35
48
  - ✅ 支持 Claude Desktop
36
- - ✅ 自动备份现有配置
49
+ - ✅ 智能合并模式:保留用户自定义文件,只更新包中文件
50
+ - ✅ 完全覆盖模式:自动备份现有配置后完全覆盖
37
51
  - ✅ 一键安装,开箱即用
38
52
 
39
53
  ## 目录结构
@@ -52,10 +66,18 @@ npm install -g 6a-spec-install
52
66
  ## 更新
53
67
 
54
68
  ```bash
55
- # 更新到最新版本
69
+ # 更新到最新版本(智能合并,推荐)
70
+ npx 6a-spec-install@latest --tool cursor --merge
71
+
72
+ # 更新到最新版本(完全覆盖)
56
73
  npx 6a-spec-install@latest --tool cursor
57
74
  ```
58
75
 
76
+ **推荐使用 `--merge` 模式**,这样可以:
77
+ - 保留你在 `.cursor/` 目录中自定义的文件
78
+ - 只更新包中提供的文件
79
+ - 不会丢失你的个性化配置
80
+
59
81
  ## 开发
60
82
 
61
83
  ### 本地测试
@@ -7,6 +7,7 @@ const installer = require('../lib/installer');
7
7
  const args = process.argv.slice(2);
8
8
  let tool = 'cursor';
9
9
  let help = false;
10
+ let mergeMode = false;
10
11
 
11
12
  for (let i = 0; i < args.length; i++) {
12
13
  const arg = args[i];
@@ -14,6 +15,8 @@ for (let i = 0; i < args.length; i++) {
14
15
  tool = args[++i] || 'cursor';
15
16
  } else if (arg === '--help' || arg === '-h') {
16
17
  help = true;
18
+ } else if (arg === '--merge' || arg === '-m') {
19
+ mergeMode = true;
17
20
  }
18
21
  }
19
22
 
@@ -26,13 +29,14 @@ if (help) {
26
29
 
27
30
  选项:
28
31
  --tool, -t TOOL 安装目标工具 (cursor/claude/all, 默认: cursor)
32
+ --merge, -m 智能合并模式(保留用户自定义文件,只更新包中文件)
29
33
  --help, -h 显示帮助信息
30
34
 
31
35
  示例:
32
- npx 6a-spec-install --tool cursor # 只安装 Cursor 配置
33
- npx 6a-spec-install --tool claude # 只安装 Claude 配置
34
- npx 6a-spec-install --tool all # 安装两种工具配置
35
- npx 6a-spec-install # 默认安装 Cursor 配置
36
+ npx 6a-spec-install --tool cursor # 完全覆盖模式(会先备份)
37
+ npx 6a-spec-install --tool cursor --merge # 智能合并模式(保留自定义文件)
38
+ npx 6a-spec-install --tool claude --merge # 安装 Claude 配置(合并模式)
39
+ npx 6a-spec-install --tool all # 安装两种工具配置
36
40
 
37
41
  支持的工具:
38
42
  cursor - 安装 Cursor IDE 的提示词配置
@@ -43,7 +47,7 @@ if (help) {
43
47
  }
44
48
 
45
49
  // 执行安装
46
- installer.install(tool).catch(err => {
50
+ installer.install(tool, mergeMode).catch(err => {
47
51
  console.error('❌ 安装失败:', err.message);
48
52
  process.exit(1);
49
53
  });
package/lib/installer.js CHANGED
@@ -6,6 +6,7 @@ class Installer {
6
6
  // 获取包目录(安装后)
7
7
  this.packageDir = this.findPackageDir();
8
8
  this.targetDir = process.cwd();
9
+ this.mergeMode = false; // 合并模式标志
9
10
  }
10
11
 
11
12
  // 查找包目录(支持本地开发和 npm 安装)
@@ -50,24 +51,98 @@ class Installer {
50
51
  return null;
51
52
  }
52
53
 
53
- // 递归复制目录
54
+ // 递归删除目录
55
+ removeDir(dirPath) {
56
+ if (fs.existsSync(dirPath)) {
57
+ const entries = fs.readdirSync(dirPath, { withFileTypes: true });
58
+ for (const entry of entries) {
59
+ const fullPath = path.join(dirPath, entry.name);
60
+ if (entry.isDirectory()) {
61
+ this.removeDir(fullPath);
62
+ } else {
63
+ fs.unlinkSync(fullPath);
64
+ }
65
+ }
66
+ fs.rmdirSync(dirPath);
67
+ }
68
+ }
69
+
70
+ // 递归复制目录(完全覆盖模式)
54
71
  copyDir(src, dest) {
72
+ // 如果目标目录存在,先完全删除(确保完全覆盖)
73
+ if (fs.existsSync(dest)) {
74
+ this.removeDir(dest);
75
+ }
76
+
77
+ // 重新创建目录并复制
78
+ fs.mkdirSync(dest, { recursive: true });
79
+
80
+ const entries = fs.readdirSync(src, { withFileTypes: true });
81
+
82
+ for (const entry of entries) {
83
+ const srcPath = path.join(src, entry.name);
84
+ const destPath = path.join(dest, entry.name);
85
+
86
+ if (entry.isDirectory()) {
87
+ this.copyDir(srcPath, destPath);
88
+ } else {
89
+ fs.copyFileSync(srcPath, destPath);
90
+ }
91
+ }
92
+ }
93
+
94
+ // 智能合并目录(只更新包中存在的文件,保留用户自定义文件)
95
+ mergeDir(src, dest) {
55
96
  if (!fs.existsSync(dest)) {
56
97
  fs.mkdirSync(dest, { recursive: true });
57
98
  }
58
99
 
59
100
  const entries = fs.readdirSync(src, { withFileTypes: true });
101
+ let updatedCount = 0;
102
+ let addedCount = 0;
60
103
 
61
104
  for (const entry of entries) {
62
105
  const srcPath = path.join(src, entry.name);
63
106
  const destPath = path.join(dest, entry.name);
64
107
 
65
108
  if (entry.isDirectory()) {
66
- this.copyDir(srcPath, destPath);
109
+ const result = this.mergeDir(srcPath, destPath);
110
+ updatedCount += result.updated;
111
+ addedCount += result.added;
67
112
  } else {
113
+ const exists = fs.existsSync(destPath);
68
114
  fs.copyFileSync(srcPath, destPath);
115
+ if (exists) {
116
+ updatedCount++;
117
+ } else {
118
+ addedCount++;
119
+ }
120
+ }
121
+ }
122
+
123
+ return { updated: updatedCount, added: addedCount };
124
+ }
125
+
126
+ // 获取目录中所有文件的相对路径列表
127
+ getAllFiles(dir, baseDir = dir) {
128
+ const files = [];
129
+ if (!fs.existsSync(dir)) {
130
+ return files;
131
+ }
132
+
133
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
134
+ for (const entry of entries) {
135
+ const fullPath = path.join(dir, entry.name);
136
+ const relativePath = path.relative(baseDir, fullPath);
137
+
138
+ if (entry.isDirectory()) {
139
+ files.push(...this.getAllFiles(fullPath, baseDir));
140
+ } else {
141
+ files.push(relativePath);
69
142
  }
70
143
  }
144
+
145
+ return files;
71
146
  }
72
147
 
73
148
  // 安装 Cursor 配置
@@ -81,10 +156,35 @@ class Installer {
81
156
  throw new Error('未找到 .cursor 源目录');
82
157
  }
83
158
 
84
- this.backupDir(targetDir);
159
+ const targetExists = fs.existsSync(targetDir);
85
160
 
86
- this.log('复制 Cursor 配置文件...');
87
- this.copyDir(sourceDir, targetDir);
161
+ if (this.mergeMode && targetExists) {
162
+ // 智能合并模式
163
+ this.log('使用智能合并模式(保留用户自定义文件)...');
164
+
165
+ // 统计用户自定义文件
166
+ const sourceFiles = new Set(this.getAllFiles(sourceDir, sourceDir));
167
+ const targetFiles = this.getAllFiles(targetDir, targetDir);
168
+ const customFiles = targetFiles.filter(f => !sourceFiles.has(f));
169
+
170
+ if (customFiles.length > 0) {
171
+ this.log(`检测到 ${customFiles.length} 个用户自定义文件,将保留`, 'info');
172
+ }
173
+
174
+ const result = this.mergeDir(sourceDir, targetDir);
175
+ this.log(`更新了 ${result.updated} 个文件,新增了 ${result.added} 个文件`, 'info');
176
+
177
+ if (customFiles.length > 0) {
178
+ this.log(`保留了 ${customFiles.length} 个用户自定义文件`, 'info');
179
+ }
180
+ } else {
181
+ // 完全覆盖模式
182
+ if (targetExists) {
183
+ this.backupDir(targetDir);
184
+ }
185
+ this.log('复制 Cursor 配置文件...');
186
+ this.copyDir(sourceDir, targetDir);
187
+ }
88
188
 
89
189
  this.log('✓ Cursor 配置安装完成!', 'success');
90
190
  }
@@ -100,44 +200,90 @@ class Installer {
100
200
  throw new Error('未找到源配置目录');
101
201
  }
102
202
 
103
- this.backupDir(targetDir);
104
-
105
- this.log('转换并复制 Claude 配置文件...');
203
+ const targetExists = fs.existsSync(targetDir);
106
204
 
107
205
  // 创建目标目录结构
108
206
  if (!fs.existsSync(targetDir)) {
109
207
  fs.mkdirSync(targetDir, { recursive: true });
110
208
  }
111
209
 
112
- // 复制 commands prompts
113
- const commandsDir = path.join(sourceDir, 'commands');
114
- if (fs.existsSync(commandsDir)) {
115
- const promptsDir = path.join(targetDir, 'prompts');
116
- this.copyDir(commandsDir, promptsDir);
117
- }
210
+ if (this.mergeMode && targetExists) {
211
+ // 智能合并模式
212
+ this.log('使用智能合并模式(保留用户自定义文件)...');
213
+
214
+ // 复制 commands 到 prompts
215
+ const commandsDir = path.join(sourceDir, 'commands');
216
+ if (fs.existsSync(commandsDir)) {
217
+ const promptsDir = path.join(targetDir, 'prompts');
218
+ const result = this.mergeDir(commandsDir, promptsDir);
219
+ if (result.updated > 0 || result.added > 0) {
220
+ this.log(`prompts: 更新了 ${result.updated} 个文件,新增了 ${result.added} 个文件`, 'info');
221
+ }
222
+ }
118
223
 
119
- // 复制 rules
120
- const rulesDir = path.join(sourceDir, 'rules');
121
- if (fs.existsSync(rulesDir)) {
122
- const targetRulesDir = path.join(targetDir, 'rules');
123
- this.copyDir(rulesDir, targetRulesDir);
124
- }
224
+ // 复制 rules
225
+ const rulesDir = path.join(sourceDir, 'rules');
226
+ if (fs.existsSync(rulesDir)) {
227
+ const targetRulesDir = path.join(targetDir, 'rules');
228
+ const result = this.mergeDir(rulesDir, targetRulesDir);
229
+ if (result.updated > 0 || result.added > 0) {
230
+ this.log(`rules: 更新了 ${result.updated} 个文件,新增了 ${result.added} 个文件`, 'info');
231
+ }
232
+ }
125
233
 
126
- // 复制 scripts
127
- const scriptDir = path.join(sourceDir, 'script');
128
- if (fs.existsSync(scriptDir)) {
129
- const targetScriptDir = path.join(targetDir, 'script');
130
- this.copyDir(scriptDir, targetScriptDir);
234
+ // 复制 scripts
235
+ const scriptDir = path.join(sourceDir, 'script');
236
+ if (fs.existsSync(scriptDir)) {
237
+ const targetScriptDir = path.join(targetDir, 'script');
238
+ const result = this.mergeDir(scriptDir, targetScriptDir);
239
+ if (result.updated > 0 || result.added > 0) {
240
+ this.log(`script: 更新了 ${result.updated} 个文件,新增了 ${result.added} 个文件`, 'info');
241
+ }
242
+ }
243
+ } else {
244
+ // 完全覆盖模式
245
+ if (targetExists) {
246
+ this.backupDir(targetDir);
247
+ }
248
+ this.log('转换并复制 Claude 配置文件...');
249
+
250
+ // 复制 commands 到 prompts
251
+ const commandsDir = path.join(sourceDir, 'commands');
252
+ if (fs.existsSync(commandsDir)) {
253
+ const promptsDir = path.join(targetDir, 'prompts');
254
+ this.copyDir(commandsDir, promptsDir);
255
+ }
256
+
257
+ // 复制 rules
258
+ const rulesDir = path.join(sourceDir, 'rules');
259
+ if (fs.existsSync(rulesDir)) {
260
+ const targetRulesDir = path.join(targetDir, 'rules');
261
+ this.copyDir(rulesDir, targetRulesDir);
262
+ }
263
+
264
+ // 复制 scripts
265
+ const scriptDir = path.join(sourceDir, 'script');
266
+ if (fs.existsSync(scriptDir)) {
267
+ const targetScriptDir = path.join(targetDir, 'script');
268
+ this.copyDir(scriptDir, targetScriptDir);
269
+ }
131
270
  }
132
271
 
133
272
  this.log('✓ Claude 配置安装完成!', 'success');
134
273
  }
135
274
 
136
275
  // 主安装方法
137
- async install(tool = 'cursor') {
276
+ async install(tool = 'cursor', mergeMode = false) {
277
+ this.mergeMode = mergeMode;
278
+
138
279
  this.log(`6A-spec 安装工具`);
139
280
  this.log(`目标目录: ${this.targetDir}`);
140
281
  this.log(`工具: ${tool}`);
282
+ if (mergeMode) {
283
+ this.log(`模式: 智能合并(保留用户自定义文件)`, 'info');
284
+ } else {
285
+ this.log(`模式: 完全覆盖(会先备份现有配置)`, 'info');
286
+ }
141
287
 
142
288
  try {
143
289
  switch (tool.toLowerCase()) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "6a-spec-install",
3
- "version": "1.0.1-dev.1",
3
+ "version": "1.0.1-dev.3",
4
4
  "description": "6A-spec 驱动开发提示词安装工具,支持 Cursor 和 Claude",
5
5
  "main": "lib/installer.js",
6
6
  "bin": {