speccrew 0.1.1 → 0.1.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.
Files changed (51) hide show
  1. package/README.ar.md +98 -91
  2. package/README.bn.md +122 -0
  3. package/README.bs.md +321 -0
  4. package/README.da.md +321 -0
  5. package/README.de.md +321 -0
  6. package/README.el.md +122 -0
  7. package/README.en.md +92 -85
  8. package/README.es.md +96 -89
  9. package/README.fr.md +321 -0
  10. package/README.it.md +321 -0
  11. package/README.ja.md +321 -0
  12. package/README.ko.md +321 -0
  13. package/README.md +92 -109
  14. package/README.no.md +321 -0
  15. package/README.pl.md +321 -0
  16. package/README.pt-BR.md +321 -0
  17. package/README.ru.md +321 -0
  18. package/README.th.md +239 -0
  19. package/README.tr.md +239 -0
  20. package/README.uk.md +239 -0
  21. package/README.vi.md +122 -0
  22. package/README.zh-TW.md +321 -0
  23. package/bin/cli.js +5 -1
  24. package/docs/GETTING-STARTED.ar.md +452 -0
  25. package/docs/GETTING-STARTED.bn.md +449 -0
  26. package/docs/GETTING-STARTED.bs.md +449 -0
  27. package/docs/GETTING-STARTED.da.md +448 -0
  28. package/docs/GETTING-STARTED.de.md +448 -0
  29. package/docs/GETTING-STARTED.el.md +449 -0
  30. package/docs/GETTING-STARTED.en.md +448 -0
  31. package/docs/GETTING-STARTED.es.md +448 -0
  32. package/docs/GETTING-STARTED.fr.md +448 -0
  33. package/docs/GETTING-STARTED.it.md +448 -0
  34. package/docs/GETTING-STARTED.ja.md +448 -0
  35. package/docs/GETTING-STARTED.ko.md +448 -0
  36. package/docs/GETTING-STARTED.md +448 -0
  37. package/docs/GETTING-STARTED.no.md +449 -0
  38. package/docs/GETTING-STARTED.pl.md +449 -0
  39. package/docs/GETTING-STARTED.pt-BR.md +449 -0
  40. package/docs/GETTING-STARTED.ru.md +449 -0
  41. package/docs/GETTING-STARTED.th.md +449 -0
  42. package/docs/GETTING-STARTED.tr.md +449 -0
  43. package/docs/GETTING-STARTED.uk.md +449 -0
  44. package/docs/GETTING-STARTED.vi.md +449 -0
  45. package/docs/GETTING-STARTED.zh-TW.md +448 -0
  46. package/lib/commands/init.js +238 -41
  47. package/lib/commands/uninstall.js +150 -32
  48. package/lib/commands/update.js +159 -24
  49. package/lib/ide-adapters.js +257 -3
  50. package/lib/utils.js +23 -7
  51. package/package.json +5 -2
@@ -1,5 +1,6 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
+ const readline = require('readline');
3
4
  const {
4
5
  copyDirRecursive,
5
6
  isSpeccrewFile,
@@ -9,7 +10,7 @@ const {
9
10
  getWorkspaceTemplatePath,
10
11
  ensureDirectories,
11
12
  } = require('../utils');
12
- const { resolveIDE } = require('../ide-adapters');
13
+ const { resolveIDE, transformAgentForIDE, transformSkillForIDE } = require('../ide-adapters');
13
14
 
14
15
  // 解析命令行参数
15
16
  function parseArgs() {
@@ -35,8 +36,22 @@ function checkNodeVersion() {
35
36
  }
36
37
  }
37
38
 
39
+ // 进度显示辅助函数
40
+ function printProgress(step, total, message) {
41
+ process.stdout.write(`[${step}/${total}] ${message}... `);
42
+ }
43
+
44
+ function printDone() {
45
+ console.log('done');
46
+ }
47
+
48
+ // 获取 npm 包根目录
49
+ function getPackageRoot() {
50
+ return path.resolve(__dirname, '..', '..');
51
+ }
52
+
38
53
  // 复制 agents(speccrew-* 前缀文件,总是覆盖)
39
- function copyAgents(sourceDir, destDir) {
54
+ function copyAgents(sourceDir, destDir, ideConfig) {
40
55
  if (!fs.existsSync(sourceDir)) return { copied: 0, skipped: 0 };
41
56
 
42
57
  fs.mkdirSync(destDir, { recursive: true });
@@ -52,7 +67,14 @@ function copyAgents(sourceDir, destDir) {
52
67
  const srcPath = path.join(sourceDir, entry.name);
53
68
  const destPath = path.join(destDir, entry.name);
54
69
 
55
- fs.copyFileSync(srcPath, destPath);
70
+ // 如果 IDE 需要转换 frontmatter 且是 .md 文件
71
+ if (ideConfig && ideConfig.transformFrontmatter && entry.name.endsWith('.md')) {
72
+ const originalContent = fs.readFileSync(srcPath, 'utf8');
73
+ const transformedContent = transformAgentForIDE(originalContent, ideConfig);
74
+ fs.writeFileSync(destPath, transformedContent, 'utf8');
75
+ } else {
76
+ fs.copyFileSync(srcPath, destPath);
77
+ }
56
78
  copied++;
57
79
  }
58
80
 
@@ -60,7 +82,7 @@ function copyAgents(sourceDir, destDir) {
60
82
  }
61
83
 
62
84
  // 复制 skills(speccrew-* 前缀目录,递归复制,总是覆盖)
63
- function copySkills(sourceDir, destDir) {
85
+ function copySkills(sourceDir, destDir, ideConfig) {
64
86
  if (!fs.existsSync(sourceDir)) return { copied: 0, skipped: 0 };
65
87
 
66
88
  let copied = 0, skipped = 0;
@@ -76,7 +98,18 @@ function copySkills(sourceDir, destDir) {
76
98
  const destPath = path.join(destDir, entry.name);
77
99
 
78
100
  if (entry.isDirectory()) {
79
- const result = copyDirRecursive(srcPath, destPath);
101
+ // 构建 contentTransform 回调:只对 SKILL.md 文件转化
102
+ let contentTransform = null;
103
+ if (ideConfig && ideConfig.transformFrontmatter) {
104
+ contentTransform = (content, fileName, filePath) => {
105
+ if (fileName === 'SKILL.md') {
106
+ return transformSkillForIDE(content, ideConfig);
107
+ }
108
+ // 其他文件返回 null,表示按原方式复制
109
+ return null;
110
+ };
111
+ }
112
+ const result = copyDirRecursive(srcPath, destPath, null, contentTransform);
80
113
  copied += result.copied;
81
114
  skipped += result.skipped;
82
115
  } else {
@@ -143,23 +176,148 @@ function copyWorkspaceTemplate(templateDir, workspaceDir) {
143
176
  return { copied, skipped };
144
177
  }
145
178
 
146
- // 主函数
147
- function run() {
179
+ // 复制文档文件(仅复制不存在的文件)
180
+ function copyDocs(packageRoot, workspaceDir) {
181
+ let copied = 0;
182
+
183
+ // 复制 GETTING-STARTED*.md 到 workspace/docs/
184
+ const docsSourceDir = path.join(packageRoot, 'docs');
185
+ const docsDestDir = path.join(workspaceDir, 'docs');
186
+
187
+ if (fs.existsSync(docsSourceDir)) {
188
+ const entries = fs.readdirSync(docsSourceDir, { withFileTypes: true });
189
+ for (const entry of entries) {
190
+ if (entry.name.startsWith('GETTING-STARTED') && entry.name.endsWith('.md')) {
191
+ const srcPath = path.join(docsSourceDir, entry.name);
192
+ const destPath = path.join(docsDestDir, entry.name);
193
+
194
+ if (!fs.existsSync(destPath)) {
195
+ fs.copyFileSync(srcPath, destPath);
196
+ copied++;
197
+ }
198
+ }
199
+ }
200
+ }
201
+
202
+ // 复制 README*.md 到 workspace/
203
+ const entries = fs.readdirSync(packageRoot, { withFileTypes: true });
204
+ for (const entry of entries) {
205
+ if (entry.name.startsWith('README') && entry.name.endsWith('.md')) {
206
+ const srcPath = path.join(packageRoot, entry.name);
207
+ const destPath = path.join(workspaceDir, entry.name);
208
+
209
+ if (!fs.existsSync(destPath)) {
210
+ fs.copyFileSync(srcPath, destPath);
211
+ copied++;
212
+ }
213
+ }
214
+ }
215
+
216
+ return { copied };
217
+ }
218
+
219
+ // 迁移旧的 .speccrewrc 到 workspace 目录
220
+ function migrateOldSpeccrewRC(projectRoot, workspaceDir) {
221
+ const oldRcPath = path.join(projectRoot, '.speccrewrc');
222
+ const newRcPath = path.join(workspaceDir, '.speccrewrc');
223
+
224
+ if (fs.existsSync(oldRcPath) && !fs.existsSync(newRcPath)) {
225
+ fs.renameSync(oldRcPath, newRcPath);
226
+ return true;
227
+ }
228
+ return false;
229
+ }
230
+
231
+ // 统计 agents 和 skills 数量
232
+ function countAgentsAndSkills(sourceRoot) {
233
+ let agentCount = 0;
234
+ let skillCount = 0;
235
+
236
+ const agentsSourceDir = path.join(sourceRoot, 'agents');
237
+ const skillsSourceDir = path.join(sourceRoot, 'skills');
238
+
239
+ if (fs.existsSync(agentsSourceDir)) {
240
+ const entries = fs.readdirSync(agentsSourceDir, { withFileTypes: true });
241
+ agentCount = entries.filter(e => isSpeccrewFile(e.name)).length;
242
+ }
243
+
244
+ if (fs.existsSync(skillsSourceDir)) {
245
+ const entries = fs.readdirSync(skillsSourceDir, { withFileTypes: true });
246
+ skillCount = entries.filter(e => isSpeccrewFile(e.name)).length;
247
+ }
248
+
249
+ return { agentCount, skillCount };
250
+ }
251
+
252
+ // 询问用户确认
253
+ function askConfirm(message) {
254
+ return new Promise((resolve) => {
255
+ const rl = readline.createInterface({
256
+ input: process.stdin,
257
+ output: process.stdout,
258
+ });
259
+
260
+ rl.question(message, (answer) => {
261
+ rl.close();
262
+ const normalized = answer.trim().toLowerCase();
263
+ resolve(normalized === '' || normalized === 'y' || normalized === 'yes');
264
+ });
265
+ });
266
+ }
267
+
268
+ // 核心安装逻辑
269
+ async function runInit(options = {}) {
270
+ const {
271
+ projectRoot = process.cwd(),
272
+ ideArg = null,
273
+ silent = false,
274
+ skipConfirm = false,
275
+ } = options;
276
+
277
+ const log = silent ? () => {} : console.log;
278
+
148
279
  try {
149
- // 1. 解析参数
150
- const { ide: cliIdeArg } = parseArgs();
280
+ // 1. 检查 Node.js 版本
281
+ checkNodeVersion();
282
+
283
+ // 2. 确定源文件路径
284
+ const sourceRoot = getSourceRoot();
285
+ const packageRoot = getPackageRoot();
151
286
 
152
- // 2. 确定项目根目录
153
- const projectRoot = process.cwd();
287
+ // 3. 解析 IDE
288
+ if (!silent) printProgress(1, 5, 'Detecting IDE environment');
289
+ const ideConfigs = resolveIDE(projectRoot, ideArg);
290
+ if (!silent) printDone();
154
291
 
155
- // 3. 检查 Node.js 版本
156
- checkNodeVersion();
292
+ // 4. 统计 agents 和 skills 数量
293
+ const { agentCount, skillCount } = countAgentsAndSkills(sourceRoot);
157
294
 
158
- // 4. 解析 IDE
159
- const ideConfigs = resolveIDE(projectRoot, cliIdeArg);
295
+ // 5. 显示安装摘要并确认
296
+ const version = getPackageVersion();
297
+ const workspaceDir = path.join(projectRoot, 'speccrew-workspace');
160
298
 
161
- // 5. 确定源文件路径
162
- const sourceRoot = getSourceRoot();
299
+ if (!skipConfirm && !silent) {
300
+ log(`\nSpecCrew v${version}\n`);
301
+ log('Installation Summary:');
302
+ if (ideConfigs.length === 1) {
303
+ log(` IDE: ${ideConfigs[0].name} (${ideConfigs[0].baseDir}/)`);
304
+ } else {
305
+ log(` IDE: ${ideConfigs.map(c => c.name).join(', ')}`);
306
+ }
307
+ log(` Agents: ${agentCount} agents`);
308
+ log(` Skills: ${skillCount} skills`);
309
+ log(` Workspace: speccrew-workspace/\n`);
310
+
311
+ const confirmed = await askConfirm('Proceed with installation? (Y/n) ');
312
+ if (!confirmed) {
313
+ log('Installation cancelled.');
314
+ return { cancelled: true };
315
+ }
316
+ log('');
317
+ }
318
+
319
+ // 6. 迁移旧的 .speccrewrc(如果存在)
320
+ migrateOldSpeccrewRC(projectRoot, workspaceDir);
163
321
 
164
322
  // 统计信息
165
323
  const stats = {
@@ -167,17 +325,19 @@ function run() {
167
325
  totalAgents: 0,
168
326
  totalSkills: 0,
169
327
  workspaceCreated: false,
328
+ docsInstalled: 0,
170
329
  };
171
330
 
172
- // 6. 对每个检测到的 IDE 复制 agents 和 skills
331
+ // 7. 复制 agents 和 skills
332
+ if (!silent) printProgress(2, 5, `Installing agents (${agentCount})`);
173
333
  for (const ideConfig of ideConfigs) {
174
334
  const agentsSourceDir = path.join(sourceRoot, 'agents');
175
335
  const skillsSourceDir = path.join(sourceRoot, 'skills');
176
336
  const agentsDestDir = path.join(projectRoot, ideConfig.agentsDir);
177
337
  const skillsDestDir = path.join(projectRoot, ideConfig.skillsDir);
178
338
 
179
- const agentsResult = copyAgents(agentsSourceDir, agentsDestDir);
180
- const skillsResult = copySkills(skillsSourceDir, skillsDestDir);
339
+ const agentsResult = copyAgents(agentsSourceDir, agentsDestDir, ideConfig);
340
+ const skillsResult = copySkills(skillsSourceDir, skillsDestDir, ideConfig);
181
341
 
182
342
  stats.ides.push({
183
343
  name: ideConfig.name,
@@ -188,44 +348,81 @@ function run() {
188
348
  stats.totalAgents += agentsResult.copied;
189
349
  stats.totalSkills += skillsResult.copied;
190
350
  }
351
+ if (!silent) printDone();
191
352
 
192
- // 7. 创建 speccrew-workspace 目录结构
193
- const workspaceDir = path.join(projectRoot, 'speccrew-workspace');
353
+ // 8. 复制 skills(显示进度)
354
+ if (!silent) printProgress(3, 5, `Installing skills (${skillCount})`);
355
+ // skills 已经在上面复制完成,这里只是显示进度
356
+ if (!silent) printDone();
357
+
358
+ // 9. 创建 speccrew-workspace 目录结构
359
+ if (!silent) printProgress(4, 5, 'Creating workspace structure');
194
360
  createWorkspaceStructure(workspaceDir);
195
361
  stats.workspaceCreated = true;
362
+ if (!silent) printDone();
196
363
 
197
- // 8. 复制 workspace 模板
364
+ // 10. 复制 workspace 模板
198
365
  const templateDir = getWorkspaceTemplatePath();
199
366
  copyWorkspaceTemplate(templateDir, workspaceDir);
200
367
 
201
- // 9. 写入 .speccrewrc
202
- const version = getPackageVersion();
368
+ // 11. 复制文档
369
+ if (!silent) printProgress(5, 5, 'Installing documentation');
370
+ const docsResult = copyDocs(packageRoot, workspaceDir);
371
+ stats.docsInstalled = docsResult.copied;
372
+ if (!silent) printDone();
373
+
374
+ // 12. 写入 .speccrewrc 到 workspace 目录
203
375
  const rcConfig = {
204
376
  ide: ideConfigs.length === 1 ? ideConfigs[0].id : ideConfigs.map(c => c.id),
205
377
  version: version,
206
378
  installedAt: new Date().toISOString(),
207
379
  };
208
- writeSpeccrewRC(projectRoot, rcConfig);
380
+ writeSpeccrewRC(workspaceDir, rcConfig);
209
381
 
210
- // 10. 输出安装摘要
211
- console.log(`SpecCrew v${version} installed successfully!\n`);
212
-
213
- for (const ide of stats.ides) {
214
- console.log(`IDE: ${ide.name} (${ide.baseDir}/)`);
215
- console.log(`Agents: ${ide.agents} installed`);
216
- console.log(`Skills: ${ide.skills} installed`);
217
- }
218
-
219
- if (stats.workspaceCreated) {
220
- console.log('Workspace: speccrew-workspace/ created');
382
+ // 13. 输出安装摘要
383
+ if (!silent) {
384
+ log(`\nSpecCrew v${version} installed successfully!\n`);
385
+
386
+ if (ideConfigs.length === 1) {
387
+ log(` IDE: ${ideConfigs[0].name} (${ideConfigs[0].baseDir}/)`);
388
+ } else {
389
+ log(` IDE: ${ideConfigs.map(c => c.name).join(', ')}`);
390
+ }
391
+ log(` Agents: ${stats.totalAgents} installed`);
392
+ log(` Skills: ${stats.totalSkills} installed`);
393
+ log(` Workspace: speccrew-workspace/ created`);
394
+ log(` Docs: ${stats.docsInstalled} files installed`);
395
+
396
+ log(`\nRun 'speccrew doctor' to verify your installation.`);
221
397
  }
222
398
 
223
- console.log('\nGet started: Ask your AI agent to help with your project!');
399
+ return {
400
+ success: true,
401
+ version,
402
+ stats,
403
+ ideConfigs,
404
+ };
224
405
 
225
406
  } catch (error) {
226
- console.error(`Error: ${error.message}`);
227
- process.exit(1);
407
+ if (!silent) {
408
+ console.error(`Error: ${error.message}`);
409
+ }
410
+ throw error;
228
411
  }
229
412
  }
230
413
 
231
- module.exports = { run };
414
+ // CLI 入口
415
+ function run() {
416
+ const { ide: cliIdeArg } = parseArgs();
417
+
418
+ runInit({
419
+ projectRoot: process.cwd(),
420
+ ideArg: cliIdeArg,
421
+ silent: false,
422
+ skipConfirm: false,
423
+ }).catch(() => {
424
+ process.exit(1);
425
+ });
426
+ }
427
+
428
+ module.exports = { runInit, run };
@@ -1,32 +1,141 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
+ const readline = require('readline');
3
4
  const { readSpeccrewRC, isSpeccrewFile, removeDirRecursive } = require('../utils');
4
5
 
5
6
  function run(projectRoot, args) {
6
- // 检查是否已初始化
7
- const rc = readSpeccrewRC(projectRoot);
7
+ // 检查是否已初始化(兼容新旧两个路径)
8
+ let rc = readSpeccrewRC(projectRoot);
9
+ const newRcPath = path.join(projectRoot, 'speccrew-workspace', '.speccrewrc');
10
+
11
+ // 如果旧路径没有配置文件,检查新路径
12
+ if (!rc && fs.existsSync(newRcPath)) {
13
+ try {
14
+ rc = JSON.parse(fs.readFileSync(newRcPath, 'utf8'));
15
+ } catch (e) {
16
+ // 忽略解析错误
17
+ }
18
+ }
19
+
8
20
  if (!rc) {
9
21
  console.log('SpecCrew is not initialized in this project.');
10
22
  console.log('Run "speccrew init" to initialize.');
11
23
  return false;
12
24
  }
13
25
 
14
- console.log('SpecCrew Uninstall\n');
15
-
16
26
  const isAll = args.includes('--all');
17
- let removedAgents = 0;
18
- let removedSkills = 0;
19
- const removedItems = [];
20
27
 
21
28
  // 获取 IDE 配置
22
29
  const ides = rc.ide ? (Array.isArray(rc.ide) ? rc.ide : [rc.ide]) : [];
23
30
 
24
- // 删除每个 IDE 目录下的 speccrew-* agents 和 skills
31
+ // 扫描将要删除的内容(不实际删除)
32
+ let totalAgents = 0;
33
+ let totalSkills = 0;
34
+ const idePaths = [];
35
+
25
36
  for (const ideId of ides) {
26
37
  const ideConfig = getIDEConfig(ideId);
27
38
  if (!ideConfig) continue;
28
39
 
29
- // 删除 agents
40
+ let agentCount = 0;
41
+ let skillCount = 0;
42
+
43
+ // 统计 agents
44
+ const agentsDir = path.join(projectRoot, ideConfig.agentsDir);
45
+ if (fs.existsSync(agentsDir)) {
46
+ const entries = fs.readdirSync(agentsDir, { withFileTypes: true });
47
+ for (const entry of entries) {
48
+ if (isSpeccrewFile(entry.name)) {
49
+ agentCount++;
50
+ }
51
+ }
52
+ }
53
+
54
+ // 统计 skills
55
+ const skillsDir = path.join(projectRoot, ideConfig.skillsDir);
56
+ if (fs.existsSync(skillsDir)) {
57
+ const entries = fs.readdirSync(skillsDir, { withFileTypes: true });
58
+ for (const entry of entries) {
59
+ if (isSpeccrewFile(entry.name)) {
60
+ skillCount++;
61
+ }
62
+ }
63
+ }
64
+
65
+ totalAgents += agentCount;
66
+ totalSkills += skillCount;
67
+ idePaths.push({ ideConfig, agentCount, skillCount });
68
+ }
69
+
70
+ // 检查配置文件是否存在(新路径和旧路径)
71
+ const oldRcPath = path.join(projectRoot, '.speccrewrc');
72
+ const hasNewRc = fs.existsSync(newRcPath);
73
+ const hasOldRc = fs.existsSync(oldRcPath);
74
+ const hasRcFile = hasNewRc || hasOldRc;
75
+
76
+ // 检查 workspace 是否存在
77
+ const workspaceDir = path.join(projectRoot, 'speccrew-workspace');
78
+ const hasWorkspace = fs.existsSync(workspaceDir);
79
+
80
+ // 显示将要删除的内容
81
+ console.log('SpecCrew Uninstall\n');
82
+ console.log('The following will be removed:');
83
+
84
+ for (const { ideConfig, agentCount, skillCount } of idePaths) {
85
+ if (agentCount > 0) {
86
+ console.log(` - ${agentCount} agent(s) from ${ideConfig.baseDir}/agents/`);
87
+ }
88
+ if (skillCount > 0) {
89
+ console.log(` - ${skillCount} skill(s) from ${ideConfig.baseDir}/skills/`);
90
+ }
91
+ }
92
+
93
+ if (hasRcFile) {
94
+ console.log(' - SpecCrew configuration (speccrew-workspace/.speccrewrc)');
95
+ }
96
+
97
+ if (isAll && hasWorkspace) {
98
+ console.log(' - speccrew-workspace/ (including all iterations, docs and knowledge base)');
99
+ console.log('\nWARNING: This will permanently delete your workspace data!');
100
+ }
101
+
102
+ // 询问用户确认
103
+ const rl = readline.createInterface({
104
+ input: process.stdin,
105
+ output: process.stdout
106
+ });
107
+
108
+ return new Promise((resolve) => {
109
+ rl.question('\nProceed with uninstall? (Y/n) ', (answer) => {
110
+ rl.close();
111
+
112
+ const confirmed = answer.toLowerCase() === 'y' || answer === '' || answer.toLowerCase() === 'yes';
113
+
114
+ if (!confirmed) {
115
+ console.log('Uninstall cancelled.');
116
+ resolve(false);
117
+ return;
118
+ }
119
+
120
+ // 执行卸载
121
+ const result = performUninstall(projectRoot, idePaths, isAll, hasOldRc);
122
+ resolve(result);
123
+ });
124
+ });
125
+ }
126
+
127
+ function performUninstall(projectRoot, idePaths, isAll, hasOldRc) {
128
+ let removedAgents = 0;
129
+ let removedSkills = 0;
130
+
131
+ // 计算总步骤数
132
+ const totalSteps = isAll ? 4 : 3;
133
+ let currentStep = 0;
134
+
135
+ // 删除 agents
136
+ currentStep++;
137
+ process.stdout.write(`[${currentStep}/${totalSteps}] Removing agents... `);
138
+ for (const { ideConfig } of idePaths) {
30
139
  const agentsDir = path.join(projectRoot, ideConfig.agentsDir);
31
140
  if (fs.existsSync(agentsDir)) {
32
141
  const entries = fs.readdirSync(agentsDir, { withFileTypes: true });
@@ -39,12 +148,16 @@ function run(projectRoot, args) {
39
148
  fs.unlinkSync(fullPath);
40
149
  }
41
150
  removedAgents++;
42
- removedItems.push(`${ideConfig.baseDir}/agents/${entry.name}`);
43
151
  }
44
152
  }
45
153
  }
154
+ }
155
+ console.log(`done (${removedAgents} removed)`);
46
156
 
47
- // 删除 skills
157
+ // 删除 skills
158
+ currentStep++;
159
+ process.stdout.write(`[${currentStep}/${totalSteps}] Removing skills... `);
160
+ for (const { ideConfig } of idePaths) {
48
161
  const skillsDir = path.join(projectRoot, ideConfig.skillsDir);
49
162
  if (fs.existsSync(skillsDir)) {
50
163
  const entries = fs.readdirSync(skillsDir, { withFileTypes: true });
@@ -57,39 +170,44 @@ function run(projectRoot, args) {
57
170
  fs.unlinkSync(fullPath);
58
171
  }
59
172
  removedSkills++;
60
- removedItems.push(`${ideConfig.baseDir}/skills/${entry.name}`);
61
173
  }
62
174
  }
63
175
  }
64
176
  }
177
+ console.log(`done (${removedSkills} removed)`);
178
+
179
+ // 删除配置文件
180
+ currentStep++;
181
+ process.stdout.write(`[${currentStep}/${totalSteps}] Removing configuration... `);
182
+
183
+ // 删除新路径的配置文件
184
+ const newRcPath = path.join(projectRoot, 'speccrew-workspace', '.speccrewrc');
185
+ if (fs.existsSync(newRcPath)) {
186
+ fs.unlinkSync(newRcPath);
187
+ }
188
+
189
+ // 兼容处理:删除旧路径的配置文件
190
+ const oldRcPath = path.join(projectRoot, '.speccrewrc');
191
+ if (fs.existsSync(oldRcPath)) {
192
+ fs.unlinkSync(oldRcPath);
193
+ }
194
+
195
+ console.log('done');
65
196
 
66
197
  // 如果 --all,删除 workspace
67
- let workspaceRemoved = false;
68
198
  if (isAll) {
199
+ currentStep++;
200
+ process.stdout.write(`[${currentStep}/${totalSteps}] Removing workspace... `);
69
201
  const workspaceDir = path.join(projectRoot, 'speccrew-workspace');
70
202
  if (fs.existsSync(workspaceDir)) {
71
203
  removeDirRecursive(workspaceDir);
72
- workspaceRemoved = true;
73
- removedItems.push('speccrew-workspace/');
74
204
  }
75
- }
205
+ console.log('done');
76
206
 
77
- // 删除 .speccrewrc
78
- const rcPath = path.join(projectRoot, '.speccrewrc');
79
- if (fs.existsSync(rcPath)) {
80
- fs.unlinkSync(rcPath);
81
- removedItems.push('.speccrewrc');
82
- }
83
-
84
- // 输出摘要
85
- console.log(`Removed ${removedAgents} agent(s) and ${removedSkills} skill(s)`);
86
- if (workspaceRemoved) {
87
- console.log('Removed speccrew-workspace/');
88
- }
89
- console.log('Removed .speccrewrc');
90
- console.log('\nSpecCrew has been uninstalled.');
91
- if (!isAll) {
92
- console.log('Workspace data preserved. Use --all to remove everything.');
207
+ console.log('\nSpecCrew has been completely uninstalled.');
208
+ } else {
209
+ console.log('\nSpecCrew has been uninstalled.');
210
+ console.log('Workspace data preserved in speccrew-workspace/');
93
211
  }
94
212
 
95
213
  return true;