adspecs 0.1.28 → 0.1.30

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 (45) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.qoder-plugin/plugin.json +1 -1
  4. package/.workbuddy-plugin/plugin.json +24 -0
  5. package/CHANGELOG.md +13 -0
  6. package/CLAUDE.md +14 -8
  7. package/INSTALL.md +85 -5
  8. package/README.md +33 -10
  9. package/bin/adspecs.js +8 -7
  10. package/hooks/hooks.json +2 -2
  11. package/hooks/platform.js +22 -11
  12. package/package.json +4 -2
  13. package/scripts/postinstall.js +79 -36
  14. package/scripts/sync-version.js +11 -1
  15. package/skills/adspecs-adversarial-review/SKILL.md +1 -0
  16. package/skills/adspecs-analyze/SKILL.md +1 -0
  17. package/skills/adspecs-architecture-planning/SKILL.md +1 -0
  18. package/skills/adspecs-biz-blueprint/SKILL.md +555 -554
  19. package/skills/adspecs-clarify/SKILL.md +1 -0
  20. package/skills/adspecs-constitution/SKILL.md +1 -0
  21. package/skills/adspecs-export-word/SKILL.md +499 -498
  22. package/skills/adspecs-front-prototype/SKILL.md +1 -0
  23. package/skills/adspecs-front-spec/SKILL.md +1 -0
  24. package/skills/adspecs-front-tasks/SKILL.md +1 -0
  25. package/skills/adspecs-git-commit/SKILL.md +1 -0
  26. package/skills/adspecs-git-initialize/SKILL.md +1 -0
  27. package/skills/adspecs-new-requirement/SKILL.md +1 -0
  28. package/skills/adspecs-plan/SKILL.md +1 -0
  29. package/skills/adspecs-prd/SKILL.md +1 -0
  30. package/skills/adspecs-prd-to-demo/SKILL.md +1 -0
  31. package/skills/adspecs-save-session/SKILL.md +179 -178
  32. package/skills/adspecs-tasks/SKILL.md +1 -0
  33. package/skills/adspecs-update-status/SKILL.md +1 -0
  34. package/skills/adspecs-utest/SKILL.md +1 -0
  35. package/skills/adspecs-write-planning-report/SKILL.md +1 -0
  36. package/skills/adspecs-write-roadmap/SKILL.md +1 -0
  37. package/skills/adspecs-write-sow/SKILL.md +1 -0
  38. package/skills/grill-me/SKILL.md +1 -0
  39. package/skills/playwright-cli/SKILL.md +1 -0
  40. package/skills/playwright-trace/SKILL.md +1 -0
  41. package/skills/project-init/SKILL.md +1 -0
  42. package/skills/sie-front-code-review/SKILL.md +1 -0
  43. package/skills/wiki-update/SKILL.md +233 -232
  44. package/src/commands/plugin.js +376 -61
  45. package/src/commands/update.js +5 -1
@@ -3,6 +3,7 @@
3
3
  const { execSync } = require('child_process');
4
4
  const path = require('path');
5
5
  const fs = require('fs');
6
+ const os = require('os');
6
7
  const chalk = require('chalk');
7
8
 
8
9
  /**
@@ -29,7 +30,14 @@ function checkClaudeCli() {
29
30
  * @param {string} platform - 'claude' | 'qoder'
30
31
  */
31
32
  function getPluginName(platform) {
32
- var manifestDir = platform === 'qoder' ? '.qoder-plugin' : '.claude-plugin';
33
+ var manifestDir;
34
+ if (platform === 'qoder') {
35
+ manifestDir = '.qoder-plugin';
36
+ } else if (platform === 'workbuddy') {
37
+ manifestDir = '.workbuddy-plugin';
38
+ } else {
39
+ manifestDir = '.claude-plugin';
40
+ }
33
41
  const pluginJsonPath = path.join(PLUGIN_DIR, manifestDir, 'plugin.json');
34
42
  if (!fs.existsSync(pluginJsonPath)) {
35
43
  // 回退到 .claude-plugin
@@ -46,18 +54,22 @@ function getPluginName(platform) {
46
54
 
47
55
  /**
48
56
  * 检测当前可用的 AI 平台
49
- * @returns {'claude'|'qoder'}
57
+ * @returns {'claude'|'qoder'|'workbuddy'}
50
58
  */
51
59
  function detectInstalledPlatform() {
52
60
  // 显式环境变量优先
53
61
  var explicit = (process.env.ADSPECS_PLATFORM || '').toLowerCase();
54
- if (explicit === 'qoder' || explicit === 'claude') {
62
+ if (['qoder', 'claude', 'workbuddy'].includes(explicit)) {
55
63
  return explicit;
56
64
  }
57
65
  // 检测 claude CLI
58
66
  if (checkClaudeCli()) {
59
67
  return 'claude';
60
68
  }
69
+ // WorkBuddy 运行环境检测(HOME 下有 ~/.workbuddy)
70
+ if (fs.existsSync(path.join(os.homedir(), '.workbuddy'))) {
71
+ return 'workbuddy';
72
+ }
61
73
  // 默认回退 qoder(当前运行环境)
62
74
  return 'qoder';
63
75
  }
@@ -67,7 +79,7 @@ function detectInstalledPlatform() {
67
79
  *
68
80
  * @param {object} options
69
81
  * @param {string} options.scope - 安装范围: user|project
70
- * @param {string} options.platform - 目标平台: claude|qoder(可选,自动检测)
82
+ * @param {string} options.platform - 目标平台: claude|qoder|workbuddy(可选,自动检测)
71
83
  */
72
84
  exports.install = async function install(options) {
73
85
  const scope = options.scope || 'user';
@@ -82,17 +94,96 @@ exports.install = async function install(options) {
82
94
  console.log(` 目标平台: ${platform}`);
83
95
  console.log('');
84
96
 
85
- if (platform === 'qoder') {
97
+ if (platform === 'workbuddy') {
98
+ await installForWorkBuddy(pluginName, scope);
99
+ } else if (platform === 'qoder') {
86
100
  await installForQoder(pluginName, scope);
87
101
  } else {
88
102
  await installForClaude(pluginName, scope);
89
103
  }
90
104
  };
91
105
 
106
+ /**
107
+ * 检查 codebuddy CLI 是否可用(WorkBuddy 共享 CodeBuddy CLI)
108
+ */
109
+ function checkCodebuddyCli() {
110
+ try {
111
+ execSync('codebuddy --version', { stdio: 'ignore' });
112
+ return true;
113
+ } catch {
114
+ return false;
115
+ }
116
+ }
117
+
118
+ /**
119
+ * WorkBuddy 平台安装
120
+ * 通过 CodeBuddy 插件系统安装,自动处理 skills/hooks 加载和 enabledPlugins
121
+ * 若 CLI 不可用则回退为手工配置 enabledPlugins
122
+ */
123
+ async function installForWorkBuddy(pluginName, scope) {
124
+ const workbuddyManifest = path.join(PLUGIN_DIR, '.workbuddy-plugin', 'plugin.json');
125
+ if (!fs.existsSync(workbuddyManifest)) {
126
+ throw new Error(`未找到 WorkBuddy 插件清单: ${workbuddyManifest}`);
127
+ }
128
+
129
+ if (checkCodebuddyCli()) {
130
+ // 方式 A:通过 CodeBuddy CLI 安装(自动处理一切)
131
+ const scopeFlag = scope === 'project' ? '--scope project' : '--scope user';
132
+ console.log(chalk.gray(' 通过 CodeBuddy CLI 安装 adspecs 插件...'));
133
+ try {
134
+ execSync(`codebuddy plugin install "${PLUGIN_DIR}" ${scopeFlag}`, {
135
+ encoding: 'utf8',
136
+ stdio: 'pipe'
137
+ });
138
+ console.log(chalk.green(' ✅ adspecs 插件安装成功'));
139
+ console.log(chalk.gray(' 请重启 WorkBuddy 使 skills 与 hooks 生效。'));
140
+ } catch (err) {
141
+ const stderr = err.stderr ? err.stderr.toString() : '';
142
+ const combined = stderr + (err.stdout ? err.stdout.toString() : '');
143
+ if (combined.includes('already exists') || combined.includes('already installed')) {
144
+ console.log(chalk.yellow(' ⏭ 插件已安装,跳过'));
145
+ } else {
146
+ throw new Error(`WorkBuddy 插件安装失败: ${combined.trim() || err.message}`);
147
+ }
148
+ }
149
+ } else {
150
+ // 方式 B:回退为手工配置 enabledPlugins(无 CLI 时)
151
+ console.log(chalk.yellow(' ⚠ 未检测到 codebuddy CLI,使用手工配置方式'));
152
+ const workbuddyHome = path.join(os.homedir(), '.workbuddy');
153
+ const settingsPath = path.join(workbuddyHome, 'settings.json');
154
+
155
+ // 确保 .workbuddy 目录存在
156
+ fs.mkdirSync(workbuddyHome, { recursive: true });
157
+
158
+ // 读取或创建 settings.json
159
+ let settings = {};
160
+ if (fs.existsSync(settingsPath)) {
161
+ try { settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')); } catch (_e) { /* use default */ }
162
+ }
163
+ if (!settings.enabledPlugins) settings.enabledPlugins = {};
164
+
165
+ var registryKey;
166
+ if (scope === 'project') {
167
+ registryKey = `${pluginName}@${PLUGIN_DIR.replace(/\\/g, '/')}`;
168
+ } else {
169
+ registryKey = `${pluginName}@local`;
170
+ }
171
+
172
+ settings.enabledPlugins[registryKey] = true;
173
+
174
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf8');
175
+ console.log(chalk.green(` ✅ 已在 settings.json 中启用插件 (${registryKey})`));
176
+ console.log(chalk.gray(' 📁 请确保 WorkBuddy 可访问以下目录:'));
177
+ console.log(chalk.gray(` ${PLUGIN_DIR}`));
178
+ console.log(chalk.gray(' 请重启 WorkBuddy 使 skills 与 hooks 生效。'));
179
+ }
180
+ }
181
+
92
182
  /**
93
183
  * Qoder 平台安装
94
- * Qoder 通过 .qoder-plugin/plugin.json 自动发现插件,
95
- * 安装即确保插件目录可被 Qoder 访问。
184
+ * 自动注册到 Qoder 插件系统:
185
+ * - 用户级:创建 junction 链接 + 写入注册表 + 启用插件
186
+ * - 项目级:在项目目录创建 .qoder-plugin 链接
96
187
  */
97
188
  async function installForQoder(pluginName, scope) {
98
189
  const qoderManifest = path.join(PLUGIN_DIR, '.qoder-plugin', 'plugin.json');
@@ -101,64 +192,238 @@ async function installForQoder(pluginName, scope) {
101
192
  }
102
193
 
103
194
  if (scope === 'project') {
104
- // 项目级:在当前项目创建 .qoder-plugin 符号链接或复制
105
- const targetDir = path.join(process.cwd(), '.qoder-plugin');
106
- if (fs.existsSync(targetDir)) {
107
- console.log(chalk.yellow(' ⏭ 当前项目已存在 .qoder-plugin/,跳过'));
108
- } else {
109
- try {
110
- // 尝试创建符号链接(节省空间)
111
- fs.symlinkSync(
112
- path.join(PLUGIN_DIR, '.qoder-plugin'),
113
- targetDir,
114
- 'junction' // Windows 使用 junction
115
- );
116
- console.log(chalk.green(' ✅ 已创建 .qoder-plugin 符号链接'));
117
- } catch (_err) {
118
- // 符号链接失败,复制清单文件
119
- fs.mkdirSync(targetDir, { recursive: true });
120
- fs.copyFileSync(qoderManifest, path.join(targetDir, 'plugin.json'));
121
- console.log(chalk.green(' ✅ 已复制 .qoder-plugin/plugin.json 到项目'));
122
- }
195
+ await installQoderProject(pluginName, qoderManifest);
196
+ } else {
197
+ await installQoderUser(pluginName, qoderManifest);
198
+ }
199
+
200
+ console.log('');
201
+ console.log(chalk.green.bold(`✅ adspecs 插件已配置到 Qoder (scope=${scope})`));
202
+ console.log('');
203
+ printSkillList();
204
+ }
205
+
206
+ /**
207
+ * Qoder 用户级安装 — 自动注册到 ~/.qoder/plugins/
208
+ */
209
+ async function installQoderUser(pluginName, qoderManifest) {
210
+ const qoderHome = path.join(os.homedir(), '.qoder');
211
+ const pluginsDir = path.join(qoderHome, 'plugins');
212
+ const cacheDir = path.join(pluginsDir, 'cache', 'local');
213
+ const linkPath = path.join(cacheDir, pluginName);
214
+ const registryKey = `${pluginName}@local`;
215
+
216
+ console.log(chalk.gray(' → Qoder 用户级插件自动注册...'));
217
+ console.log('');
218
+
219
+ // 1. 确保目录结构存在
220
+ fs.mkdirSync(cacheDir, { recursive: true });
221
+
222
+ // 2. 创建 junction 链接(已存在则先移除重建,确保指向最新路径)
223
+ if (fs.existsSync(linkPath)) {
224
+ try {
225
+ fs.rmSync(linkPath, { recursive: true, force: true });
226
+ } catch (_err) {
227
+ // junction 可能无法用 rmSync 删除,尝试 unlink
228
+ try { fs.unlinkSync(linkPath); } catch (_e) { /* ignore */ }
123
229
  }
230
+ }
124
231
 
125
- // 确保 skills 可被访问
126
- const targetSkills = path.join(process.cwd(), 'skills');
127
- if (!fs.existsSync(targetSkills)) {
128
- try {
129
- fs.symlinkSync(path.join(PLUGIN_DIR, 'skills'), targetSkills, 'junction');
130
- console.log(chalk.green(' ✅ 已创建 skills/ 符号链接'));
131
- } catch (_err) {
132
- console.log(chalk.yellow(' ⚠ skills/ 链接创建失败,请手动确保 Qoder 可访问插件 skills 目录'));
133
- }
232
+ try {
233
+ fs.symlinkSync(PLUGIN_DIR, linkPath, 'junction');
234
+ console.log(chalk.green(` ✅ 已创建插件链接: ${linkPath}`));
235
+ console.log(chalk.gray(` → ${PLUGIN_DIR}`));
236
+ } catch (err) {
237
+ // junction 创建失败(权限不足等),回退为复制关键文件
238
+ console.log(chalk.yellow(` ⚠ 链接创建失败(${err.message}),回退为文件复制...`));
239
+ fs.mkdirSync(linkPath, { recursive: true });
240
+ copyDirRecursive(PLUGIN_DIR, linkPath, [
241
+ 'node_modules', '.git', '.claude', 'assets'
242
+ ]);
243
+ console.log(chalk.green(` ✅ 已复制插件文件到: ${linkPath}`));
244
+ }
245
+
246
+ // 3. 读取插件版本
247
+ const pluginJson = JSON.parse(fs.readFileSync(qoderManifest, 'utf8'));
248
+ const version = pluginJson.version || '0.0.0';
249
+ const now = new Date().toISOString();
250
+
251
+ // 4. 写入 installed_plugins_v2.json
252
+ const v2Path = path.join(pluginsDir, 'installed_plugins_v2.json');
253
+ let v2Data = { version: 2, plugins: {} };
254
+ if (fs.existsSync(v2Path)) {
255
+ try { v2Data = JSON.parse(fs.readFileSync(v2Path, 'utf8')); } catch (_e) { /* use default */ }
256
+ }
257
+ if (!v2Data.plugins) v2Data.plugins = {};
258
+
259
+ v2Data.plugins[registryKey] = [{
260
+ scope: 'user',
261
+ installPath: linkPath,
262
+ version: version,
263
+ source: 'local',
264
+ installedAt: (v2Data.plugins[registryKey] && v2Data.plugins[registryKey][0] && v2Data.plugins[registryKey][0].installedAt) || now,
265
+ lastUpdated: now,
266
+ enabled: true
267
+ }];
268
+
269
+ fs.writeFileSync(v2Path, JSON.stringify(v2Data, null, 2) + '\n', 'utf8');
270
+ console.log(chalk.green(' ✅ 已写入插件注册表 (installed_plugins_v2.json)'));
271
+
272
+ // 5. 写入 installed_plugins.json(v1 兼容)
273
+ const v1Path = path.join(pluginsDir, 'installed_plugins.json');
274
+ let v1Data = { plugins: {} };
275
+ if (fs.existsSync(v1Path)) {
276
+ try { v1Data = JSON.parse(fs.readFileSync(v1Path, 'utf8')); } catch (_e) { /* use default */ }
277
+ }
278
+ if (!v1Data.plugins) v1Data.plugins = {};
279
+
280
+ v1Data.plugins[registryKey] = { installPath: linkPath };
281
+
282
+ fs.writeFileSync(v1Path, JSON.stringify(v1Data, null, 2) + '\n', 'utf8');
283
+ console.log(chalk.green(' ✅ 已写入插件注册表 (installed_plugins.json)'));
284
+
285
+ // 6. 在 settings.json 中启用插件
286
+ const settingsPath = path.join(qoderHome, 'settings.json');
287
+ let settings = {};
288
+ if (fs.existsSync(settingsPath)) {
289
+ try { settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')); } catch (_e) { /* use default */ }
290
+ }
291
+ if (!settings.enabledPlugins) settings.enabledPlugins = {};
292
+
293
+ settings.enabledPlugins[registryKey] = true;
294
+
295
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf8');
296
+ console.log(chalk.green(' ✅ 已在 settings.json 中启用插件'));
297
+
298
+ console.log('');
299
+ console.log(chalk.gray(' 请重启 Qoder 使插件生效。'));
300
+ }
301
+
302
+ /**
303
+ * Qoder 项目级安装
304
+ */
305
+ async function installQoderProject(pluginName, qoderManifest) {
306
+ // 项目级:在当前项目创建 .qoder-plugin 符号链接或复制
307
+ const targetDir = path.join(process.cwd(), '.qoder-plugin');
308
+ if (fs.existsSync(targetDir)) {
309
+ console.log(chalk.yellow(' ⏭ 当前项目已存在 .qoder-plugin/,跳过'));
310
+ } else {
311
+ try {
312
+ fs.symlinkSync(
313
+ path.join(PLUGIN_DIR, '.qoder-plugin'),
314
+ targetDir,
315
+ 'junction'
316
+ );
317
+ console.log(chalk.green(' ✅ 已创建 .qoder-plugin 符号链接'));
318
+ } catch (_err) {
319
+ fs.mkdirSync(targetDir, { recursive: true });
320
+ fs.copyFileSync(qoderManifest, path.join(targetDir, 'plugin.json'));
321
+ console.log(chalk.green(' ✅ 已复制 .qoder-plugin/plugin.json 到项目'));
134
322
  }
323
+ }
135
324
 
136
- // 确保 hooks 可被访问
137
- const targetHooks = path.join(process.cwd(), 'hooks');
138
- if (!fs.existsSync(targetHooks)) {
139
- try {
140
- fs.symlinkSync(path.join(PLUGIN_DIR, 'hooks'), targetHooks, 'junction');
141
- console.log(chalk.green(' ✅ 已创建 hooks/ 符号链接'));
142
- } catch (_err) {
143
- console.log(chalk.yellow(' ⚠ hooks/ 链接创建失败,请手动确保 Qoder 可访问插件 hooks 目录'));
144
- }
325
+ // 确保 skills 可被访问
326
+ const targetSkills = path.join(process.cwd(), 'skills');
327
+ if (!fs.existsSync(targetSkills)) {
328
+ try {
329
+ fs.symlinkSync(path.join(PLUGIN_DIR, 'skills'), targetSkills, 'junction');
330
+ console.log(chalk.green(' ✅ 已创建 skills/ 符号链接'));
331
+ } catch (_err) {
332
+ console.log(chalk.yellow(' ⚠ skills/ 链接创建失败,请手动确保 Qoder 可访问插件 skills 目录'));
333
+ }
334
+ }
335
+
336
+ // 确保 hooks 可被访问
337
+ const targetHooks = path.join(process.cwd(), 'hooks');
338
+ if (!fs.existsSync(targetHooks)) {
339
+ try {
340
+ fs.symlinkSync(path.join(PLUGIN_DIR, 'hooks'), targetHooks, 'junction');
341
+ console.log(chalk.green(' ✅ 已创建 hooks/ 符号链接'));
342
+ } catch (_err) {
343
+ console.log(chalk.yellow(' ⚠ hooks/ 链接创建失败,请手动确保 Qoder 可访问插件 hooks 目录'));
344
+ }
345
+ }
346
+ }
347
+
348
+ /**
349
+ * 递归复制目录(排除指定目录名)
350
+ */
351
+ function copyDirRecursive(src, dest, excludeDirs) {
352
+ fs.mkdirSync(dest, { recursive: true });
353
+ const entries = fs.readdirSync(src, { withFileTypes: true });
354
+ for (const entry of entries) {
355
+ if (excludeDirs && excludeDirs.includes(entry.name)) continue;
356
+ const srcPath = path.join(src, entry.name);
357
+ const destPath = path.join(dest, entry.name);
358
+ if (entry.isDirectory()) {
359
+ copyDirRecursive(srcPath, destPath, excludeDirs);
360
+ } else {
361
+ fs.copyFileSync(srcPath, destPath);
362
+ }
363
+ }
364
+ }
365
+
366
+ /**
367
+ * Qoder 用户级卸载 — 移除链接 + 注册表条目 + 启用配置
368
+ */
369
+ function uninstallQoderUser(pluginName) {
370
+ const qoderHome = path.join(os.homedir(), '.qoder');
371
+ const pluginsDir = path.join(qoderHome, 'plugins');
372
+ const linkPath = path.join(pluginsDir, 'cache', 'local', pluginName);
373
+ const registryKey = `${pluginName}@local`;
374
+
375
+ // 1. 移除 junction 链接 / 复制的目录
376
+ if (fs.existsSync(linkPath)) {
377
+ try {
378
+ fs.rmSync(linkPath, { recursive: true, force: true });
379
+ console.log(chalk.green(` ✅ 已移除插件目录: ${linkPath}`));
380
+ } catch (err) {
381
+ console.log(chalk.yellow(` ⚠ 移除插件目录失败: ${err.message}`));
145
382
  }
146
383
  } else {
147
- // 用户级:输出指引
148
- console.log(chalk.gray(' → Qoder 用户级插件安装...'));
149
- console.log('');
150
- console.log(chalk.green(' ✅ Qoder 插件清单已就绪: ' + qoderManifest));
151
- console.log('');
152
- console.log(' Qoder 用户级安装方式:');
153
- console.log(' 1. 将本插件目录路径添加到 Qoder 插件搜索路径');
154
- console.log(' 2. 或在 Qoder 设置中配置插件目录指向: ' + PLUGIN_DIR);
155
- console.log(' 3. 重启 Qoder 使插件生效');
384
+ console.log(chalk.yellow(' ⏭ 插件目录不存在,跳过'));
156
385
  }
157
386
 
158
- console.log('');
159
- console.log(chalk.green.bold(`✅ adspecs 插件已配置到 Qoder (scope=${scope})`));
160
- console.log('');
161
- printSkillList();
387
+ // 2. 从 installed_plugins_v2.json 移除
388
+ const v2Path = path.join(pluginsDir, 'installed_plugins_v2.json');
389
+ if (fs.existsSync(v2Path)) {
390
+ try {
391
+ const v2Data = JSON.parse(fs.readFileSync(v2Path, 'utf8'));
392
+ if (v2Data.plugins && v2Data.plugins[registryKey]) {
393
+ delete v2Data.plugins[registryKey];
394
+ fs.writeFileSync(v2Path, JSON.stringify(v2Data, null, 2) + '\n', 'utf8');
395
+ console.log(chalk.green(' ✅ 已从注册表移除 (installed_plugins_v2.json)'));
396
+ }
397
+ } catch (_err) { /* ignore */ }
398
+ }
399
+
400
+ // 3. 从 installed_plugins.json 移除
401
+ const v1Path = path.join(pluginsDir, 'installed_plugins.json');
402
+ if (fs.existsSync(v1Path)) {
403
+ try {
404
+ const v1Data = JSON.parse(fs.readFileSync(v1Path, 'utf8'));
405
+ if (v1Data.plugins && v1Data.plugins[registryKey]) {
406
+ delete v1Data.plugins[registryKey];
407
+ fs.writeFileSync(v1Path, JSON.stringify(v1Data, null, 2) + '\n', 'utf8');
408
+ console.log(chalk.green(' ✅ 已从注册表移除 (installed_plugins.json)'));
409
+ }
410
+ } catch (_err) { /* ignore */ }
411
+ }
412
+
413
+ // 4. 从 settings.json 禁用
414
+ const settingsPath = path.join(qoderHome, 'settings.json');
415
+ if (fs.existsSync(settingsPath)) {
416
+ try {
417
+ const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
418
+ if (settings.enabledPlugins && settings.enabledPlugins[registryKey] !== undefined) {
419
+ delete settings.enabledPlugins[registryKey];
420
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf8');
421
+ console.log(chalk.green(' ✅ 已从 settings.json 移除启用配置'));
422
+ }
423
+ } catch (_err) { /* ignore */ }
424
+ }
425
+
426
+ console.log(chalk.gray(' 请重启 Qoder 使变更生效。'));
162
427
  }
163
428
 
164
429
  /**
@@ -244,12 +509,59 @@ function printSkillList() {
244
509
  console.log('');
245
510
  }
246
511
 
512
+ /**
513
+ * WorkBuddy 平台卸载
514
+ * 通过 CodeBuddy CLI 卸载,或手工清除 settings.json 中的 enabledPlugins 条目
515
+ */
516
+ async function uninstallWorkBuddy(pluginName, scope) {
517
+ if (checkCodebuddyCli()) {
518
+ const scopeFlag = scope === 'project' ? '--scope project' : '--scope user';
519
+ try {
520
+ execSync(`codebuddy plugin uninstall ${pluginName} ${scopeFlag}`, {
521
+ stdio: 'pipe',
522
+ });
523
+ console.log(chalk.green(` ✅ ${pluginName} 插件已卸载`));
524
+ } catch (err) {
525
+ const stderr = err.stderr ? err.stderr.toString() : '';
526
+ if (stderr.includes('not installed') || stderr.includes('not found')) {
527
+ console.log(chalk.yellow(` ⏭ ${pluginName} 未安装,无需卸载`));
528
+ } else {
529
+ throw new Error(`WorkBuddy 插件卸载失败: ${stderr || err.message}`);
530
+ }
531
+ }
532
+ } else {
533
+ // 回退:手工清除 enabledPlugins 条目
534
+ const settingsPath = path.join(os.homedir(), '.workbuddy', 'settings.json');
535
+ if (fs.existsSync(settingsPath)) {
536
+ let settings = {};
537
+ try { settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')); } catch (_e) { /* use default */ }
538
+ if (settings.enabledPlugins) {
539
+ // 移除所有 adspecs 相关条目
540
+ const keysBefore = Object.keys(settings.enabledPlugins).length;
541
+ for (var key in settings.enabledPlugins) {
542
+ if (key.startsWith(pluginName + '@')) {
543
+ delete settings.enabledPlugins[key];
544
+ }
545
+ }
546
+ const keysAfter = Object.keys(settings.enabledPlugins).length;
547
+ if (keysBefore > keysAfter) {
548
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf8');
549
+ console.log(chalk.green(` ✅ 已从 settings.json 移除插件配置`));
550
+ } else {
551
+ console.log(chalk.yellow(` ⏭ 未在 settings.json 中找到插件配置`));
552
+ }
553
+ }
554
+ }
555
+ }
556
+ console.log(chalk.gray(' 请重启 WorkBuddy 使变更生效。'));
557
+ }
558
+
247
559
  /**
248
560
  * 卸载插件
249
561
  *
250
562
  * @param {object} options
251
563
  * @param {string} options.scope - 卸载范围: user|project
252
- * @param {string} options.platform - 目标平台: claude|qoder(可选,自动检测)
564
+ * @param {string} options.platform - 目标平台: claude|qoder|workbuddy(可选,自动检测)
253
565
  */
254
566
  exports.uninstall = async function uninstall(options) {
255
567
  const scope = options.scope || 'user';
@@ -261,9 +573,11 @@ exports.uninstall = async function uninstall(options) {
261
573
  console.log(` 目标平台: ${platform}`);
262
574
  console.log('');
263
575
 
264
- if (platform === 'qoder') {
265
- // Qoder 卸载:移除项目级链接
576
+ if (platform === 'workbuddy') {
577
+ await uninstallWorkBuddy(pluginName, scope);
578
+ } else if (platform === 'qoder') {
266
579
  if (scope === 'project') {
580
+ // Qoder 项目级卸载:移除项目链接
267
581
  const targetDir = path.join(process.cwd(), '.qoder-plugin');
268
582
  if (fs.existsSync(targetDir)) {
269
583
  try {
@@ -276,7 +590,8 @@ exports.uninstall = async function uninstall(options) {
276
590
  console.log(chalk.yellow(' ⏭ 项目未安装 .qoder-plugin/,无需卸载'));
277
591
  }
278
592
  } else {
279
- console.log(chalk.yellow(' 用户级卸载请从 Qoder 插件设置中移除本插件路径'));
593
+ // Qoder 用户级卸载:移除链接 + 注册表 + 启用配置
594
+ uninstallQoderUser(pluginName);
280
595
  }
281
596
  console.log('');
282
597
  console.log(chalk.green(`✅ ${pluginName} 已从 Qoder 卸载 (scope=${scope})`));
@@ -81,7 +81,11 @@ module.exports = async function update(options) {
81
81
  console.log(chalk.green.bold(`✅ 升级完成: ${oldVersion} → ${newVersion}`));
82
82
  }
83
83
  console.log('');
84
- console.log(' Claude Code 插件注册已通过 postinstall 钩子自动刷新。');
84
+ console.log(' 🔄 插件注册刷新:');
85
+ console.log(' Claude Code — postinstall 钩子已自动刷新命令行插件注册');
86
+ console.log(' Qoder — 通过 .qoder-plugin/ 清单自动发现,无需手动刷新');
87
+ console.log(' WorkBuddy — 通过 .workbuddy-plugin/ 清单自动发现,无需手动刷新');
88
+ console.log('');
85
89
  console.log(` 验证: ${chalk.gray('adspecs --version')}`);
86
90
  console.log('');
87
91
  };