@adversity/coding-tool-x 3.1.0 → 3.1.1

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 (137) hide show
  1. package/CHANGELOG.md +15 -18
  2. package/README.md +8 -8
  3. package/dist/web/assets/ConfigTemplates-Bidwfdf2.css +1 -0
  4. package/dist/web/assets/ConfigTemplates-ZrK_s7ma.js +1 -0
  5. package/dist/web/assets/Home-B8YfhZ3c.js +1 -0
  6. package/dist/web/assets/Home-Di2qsylF.css +1 -0
  7. package/dist/web/assets/PluginManager-BD7QUZbU.js +1 -0
  8. package/dist/web/assets/PluginManager-ROyoZ-6m.css +1 -0
  9. package/dist/web/assets/ProjectList-C1fQb9OW.css +1 -0
  10. package/dist/web/assets/ProjectList-DRb1DuHV.js +1 -0
  11. package/dist/web/assets/SessionList-BGJWyneI.css +1 -0
  12. package/dist/web/assets/SessionList-lZ0LKzfT.js +1 -0
  13. package/dist/web/assets/SkillManager-C1xG5B4Q.js +1 -0
  14. package/dist/web/assets/SkillManager-D7pd-d_P.css +1 -0
  15. package/dist/web/assets/Terminal-DGNJeVtc.css +1 -0
  16. package/dist/web/assets/Terminal-DksBo_lM.js +1 -0
  17. package/dist/web/assets/WorkspaceManager-Burx7XOo.js +1 -0
  18. package/dist/web/assets/WorkspaceManager-CrwgQgmP.css +1 -0
  19. package/dist/web/assets/icons-kcfLIMBB.js +1 -0
  20. package/dist/web/assets/index-Ufv5rCa5.css +1 -0
  21. package/dist/web/assets/index-lAkrRC3h.js +2 -0
  22. package/dist/web/assets/markdown-BfC0goYb.css +10 -0
  23. package/dist/web/assets/markdown-C9MYpaSi.js +1 -0
  24. package/dist/web/assets/naive-ui-CSrLusZZ.js +1 -0
  25. package/dist/web/assets/{vendors-D2HHw_aW.js → vendors-CO3Upi1d.js} +2 -2
  26. package/dist/web/assets/vue-vendor-DqyWIXEb.js +45 -0
  27. package/dist/web/assets/xterm-6GBZ9nXN.css +32 -0
  28. package/dist/web/assets/xterm-BJzAjXCH.js +13 -0
  29. package/dist/web/index.html +8 -6
  30. package/package.json +4 -2
  31. package/src/commands/channels.js +48 -1
  32. package/src/commands/cli-type.js +4 -2
  33. package/src/commands/daemon.js +81 -12
  34. package/src/commands/doctor.js +10 -9
  35. package/src/commands/list.js +1 -1
  36. package/src/commands/logs.js +6 -4
  37. package/src/commands/port-config.js +24 -4
  38. package/src/commands/proxy-control.js +12 -6
  39. package/src/commands/search.js +1 -1
  40. package/src/commands/security.js +3 -2
  41. package/src/commands/stats.js +226 -52
  42. package/src/commands/switch.js +1 -1
  43. package/src/commands/toggle-proxy.js +31 -6
  44. package/src/commands/update.js +97 -0
  45. package/src/commands/workspace.js +1 -1
  46. package/src/config/default.js +39 -2
  47. package/src/config/loader.js +74 -8
  48. package/src/config/paths.js +105 -33
  49. package/src/index.js +64 -3
  50. package/src/plugins/constants.js +3 -2
  51. package/src/plugins/plugin-api.js +1 -1
  52. package/src/reset-config.js +4 -2
  53. package/src/server/api/agents.js +57 -14
  54. package/src/server/api/channels.js +112 -33
  55. package/src/server/api/codex-channels.js +111 -18
  56. package/src/server/api/codex-proxy.js +14 -8
  57. package/src/server/api/commands.js +71 -18
  58. package/src/server/api/config-export.js +0 -6
  59. package/src/server/api/config-registry.js +11 -3
  60. package/src/server/api/config.js +376 -5
  61. package/src/server/api/convert.js +133 -0
  62. package/src/server/api/dashboard.js +22 -6
  63. package/src/server/api/gemini-channels.js +107 -18
  64. package/src/server/api/gemini-proxy.js +14 -8
  65. package/src/server/api/gemini-sessions.js +1 -1
  66. package/src/server/api/health-check.js +4 -3
  67. package/src/server/api/mcp.js +3 -3
  68. package/src/server/api/opencode-channels.js +419 -0
  69. package/src/server/api/opencode-projects.js +99 -0
  70. package/src/server/api/opencode-proxy.js +198 -0
  71. package/src/server/api/opencode-sessions.js +403 -0
  72. package/src/server/api/opencode-statistics.js +57 -0
  73. package/src/server/api/plugins.js +66 -19
  74. package/src/server/api/prompts.js +2 -2
  75. package/src/server/api/proxy.js +7 -4
  76. package/src/server/api/sessions.js +3 -0
  77. package/src/server/api/skills.js +69 -18
  78. package/src/server/api/workspaces.js +78 -6
  79. package/src/server/codex-proxy-server.js +30 -18
  80. package/src/server/dev-server.js +1 -1
  81. package/src/server/gemini-proxy-server.js +15 -3
  82. package/src/server/index.js +165 -58
  83. package/src/server/opencode-proxy-server.js +4375 -0
  84. package/src/server/proxy-server.js +27 -18
  85. package/src/server/services/agents-service.js +61 -24
  86. package/src/server/services/channel-scheduler.js +9 -5
  87. package/src/server/services/channels.js +64 -37
  88. package/src/server/services/codex-channels.js +56 -43
  89. package/src/server/services/codex-settings-manager.js +271 -49
  90. package/src/server/services/codex-statistics-service.js +2 -2
  91. package/src/server/services/commands-service.js +84 -25
  92. package/src/server/services/config-export-service.js +7 -45
  93. package/src/server/services/config-registry-service.js +63 -17
  94. package/src/server/services/config-sync-manager.js +160 -7
  95. package/src/server/services/config-templates-service.js +204 -51
  96. package/src/server/services/env-checker.js +26 -12
  97. package/src/server/services/env-manager.js +126 -18
  98. package/src/server/services/favorites.js +5 -3
  99. package/src/server/services/gemini-channels.js +33 -44
  100. package/src/server/services/gemini-statistics-service.js +2 -2
  101. package/src/server/services/mcp-service.js +350 -9
  102. package/src/server/services/model-detector.js +707 -221
  103. package/src/server/services/network-access.js +80 -0
  104. package/src/server/services/opencode-channels.js +206 -0
  105. package/src/server/services/opencode-gateway-converter.js +639 -0
  106. package/src/server/services/opencode-sessions.js +663 -0
  107. package/src/server/services/opencode-settings-manager.js +342 -0
  108. package/src/server/services/opencode-statistics-service.js +255 -0
  109. package/src/server/services/plugins-service.js +479 -22
  110. package/src/server/services/prompts-service.js +53 -11
  111. package/src/server/services/proxy-runtime.js +1 -1
  112. package/src/server/services/repo-scanner-base.js +1 -1
  113. package/src/server/services/security-config.js +1 -1
  114. package/src/server/services/session-cache.js +1 -1
  115. package/src/server/services/skill-service.js +300 -46
  116. package/src/server/services/speed-test.js +464 -186
  117. package/src/server/services/statistics-service.js +2 -2
  118. package/src/server/services/terminal-commands.js +10 -3
  119. package/src/server/services/terminal-config.js +1 -1
  120. package/src/server/services/ui-config.js +1 -1
  121. package/src/server/services/workspace-service.js +57 -100
  122. package/src/server/websocket-server.js +132 -3
  123. package/src/ui/menu.js +49 -40
  124. package/src/utils/port-helper.js +22 -8
  125. package/src/utils/session.js +5 -4
  126. package/dist/web/assets/icons-CO_2OFES.js +0 -1
  127. package/dist/web/assets/index-DI8QOi-E.js +0 -14
  128. package/dist/web/assets/index-uLHGdeZh.css +0 -41
  129. package/dist/web/assets/naive-ui-B1re3c-e.js +0 -1
  130. package/dist/web/assets/vue-vendor-6JaYHOiI.js +0 -44
  131. package/src/server/api/oauth.js +0 -294
  132. package/src/server/api/permissions.js +0 -385
  133. package/src/server/config/oauth-providers.js +0 -68
  134. package/src/server/services/oauth-callback-server.js +0 -284
  135. package/src/server/services/oauth-service.js +0 -378
  136. package/src/server/services/oauth-token-storage.js +0 -135
  137. package/src/server/services/permission-templates-service.js +0 -308
@@ -1,11 +1,7 @@
1
1
  /**
2
2
  * Commands 服务
3
3
  *
4
- * 管理 Claude Code 自定义命令的 CRUD 操作
5
- * 命令目录:
6
- * - 用户级: ~/.claude/commands/
7
- * - 项目级: .claude/commands/
8
- *
4
+ * 管理 Claude/OpenCode 自定义命令的 CRUD 操作
9
5
  * 支持从 GitHub 仓库扫描和安装命令
10
6
  */
11
7
 
@@ -13,6 +9,7 @@ const fs = require('fs');
13
9
  const path = require('path');
14
10
  const os = require('os');
15
11
  const { RepoScannerBase } = require('./repo-scanner-base');
12
+ const { NATIVE_PATHS } = require('../../config/paths');
16
13
  const {
17
14
  parseCommandContent,
18
15
  detectCommandFormat,
@@ -21,11 +18,35 @@ const {
21
18
  parseFrontmatter
22
19
  } = require('./format-converter');
23
20
 
24
- // 命令目录路径
25
- const USER_COMMANDS_DIR = path.join(os.homedir(), '.claude', 'commands');
26
-
27
21
  // 默认仓库源
28
22
  const DEFAULT_REPOS = [];
23
+ const SUPPORTED_PLATFORMS = ['claude', 'opencode'];
24
+ const OPENCODE_CONFIG_DIR = NATIVE_PATHS.opencode.config;
25
+
26
+ const PLATFORM_CONFIG = {
27
+ claude: {
28
+ userCommandsDir: path.join(os.homedir(), '.claude', 'commands'),
29
+ projectCommandsDir: (projectPath) => path.join(projectPath, '.claude', 'commands'),
30
+ repoType: 'commands'
31
+ },
32
+ opencode: {
33
+ userCommandsDir: path.join(OPENCODE_CONFIG_DIR, 'commands'),
34
+ legacyUserCommandsDir: path.join(OPENCODE_CONFIG_DIR, 'command'),
35
+ projectCommandsDir: (projectPath) => {
36
+ const modern = path.join(projectPath, '.opencode', 'commands');
37
+ const legacy = path.join(projectPath, '.opencode', 'command');
38
+ if (fs.existsSync(legacy) && !fs.existsSync(modern)) {
39
+ return legacy;
40
+ }
41
+ return modern;
42
+ },
43
+ repoType: 'opencode-commands'
44
+ }
45
+ };
46
+
47
+ function normalizePlatform(platform) {
48
+ return SUPPORTED_PLATFORMS.includes(platform) ? platform : 'claude';
49
+ }
29
50
 
30
51
  /**
31
52
  * 确保目录存在
@@ -60,6 +81,9 @@ function generateCommandFrontmatter(data) {
60
81
  if (data.agent) {
61
82
  lines.push(`agent: ${data.agent}`);
62
83
  }
84
+ if (typeof data.subtask === 'boolean') {
85
+ lines.push(`subtask: ${data.subtask}`);
86
+ }
63
87
 
64
88
  lines.push('---');
65
89
  return lines.join('\n');
@@ -105,6 +129,9 @@ function scanCommandsDir(dir, basePath, scope) {
105
129
  description: frontmatter.description || '',
106
130
  allowedTools: frontmatter['allowed-tools'] || '',
107
131
  argumentHint: frontmatter['argument-hint'] || '',
132
+ agent: frontmatter.agent || '',
133
+ model: frontmatter.model || '',
134
+ subtask: frontmatter.subtask || '',
108
135
  body,
109
136
  fullContent: content,
110
137
  updatedAt: fs.statSync(fullPath).mtime.getTime()
@@ -125,10 +152,10 @@ function scanCommandsDir(dir, basePath, scope) {
125
152
  * Commands 仓库扫描器
126
153
  */
127
154
  class CommandsRepoScanner extends RepoScannerBase {
128
- constructor() {
155
+ constructor(platform, installDir) {
129
156
  super({
130
- type: 'commands',
131
- installDir: USER_COMMANDS_DIR,
157
+ type: PLATFORM_CONFIG[platform]?.repoType || 'commands',
158
+ installDir,
132
159
  markerFile: null, // 直接扫描 .md 文件
133
160
  fileExtension: '.md',
134
161
  defaultRepos: DEFAULT_REPOS
@@ -159,6 +186,9 @@ class CommandsRepoScanner extends RepoScannerBase {
159
186
  description: frontmatter.description || '',
160
187
  allowedTools: frontmatter['allowed-tools'] || '',
161
188
  argumentHint: frontmatter['argument-hint'] || '',
189
+ agent: frontmatter.agent || '',
190
+ model: frontmatter.model || '',
191
+ subtask: frontmatter.subtask || '',
162
192
  body,
163
193
  fullContent: content,
164
194
  installed: this.isInstalled(relativePath),
@@ -208,12 +238,28 @@ class CommandsRepoScanner extends RepoScannerBase {
208
238
  * Commands 服务类
209
239
  */
210
240
  class CommandsService {
211
- constructor() {
212
- this.userCommandsDir = USER_COMMANDS_DIR;
213
- this.repoScanner = new CommandsRepoScanner();
241
+ constructor(platform = 'claude') {
242
+ this.platform = normalizePlatform(platform);
243
+ const config = PLATFORM_CONFIG[this.platform];
244
+
245
+ this.userCommandsDir = config.userCommandsDir;
246
+ if (this.platform === 'opencode') {
247
+ const legacyUserDir = config.legacyUserCommandsDir;
248
+ if (legacyUserDir && fs.existsSync(legacyUserDir) && !fs.existsSync(this.userCommandsDir)) {
249
+ this.userCommandsDir = legacyUserDir;
250
+ }
251
+ }
252
+
253
+ this.projectCommandsDir = config.projectCommandsDir;
254
+ this.repoScanner = new CommandsRepoScanner(this.platform, this.userCommandsDir);
214
255
  ensureDir(this.userCommandsDir);
215
256
  }
216
257
 
258
+ getProjectCommandsDir(projectPath) {
259
+ if (!projectPath) return null;
260
+ return this.projectCommandsDir(projectPath);
261
+ }
262
+
217
263
  /**
218
264
  * 获取所有命令列表
219
265
  * @param {string} projectPath - 项目路径(可选,用于获取项目级命令)
@@ -227,7 +273,7 @@ class CommandsService {
227
273
 
228
274
  // 获取项目级命令(如果提供了项目路径)
229
275
  if (projectPath) {
230
- const projectCommandsDir = path.join(projectPath, '.claude', 'commands');
276
+ const projectCommandsDir = this.getProjectCommandsDir(projectPath);
231
277
  const projectCommands = scanCommandsDir(projectCommandsDir, projectCommandsDir, 'project');
232
278
  commands.push(...projectCommands);
233
279
  }
@@ -295,7 +341,7 @@ class CommandsService {
295
341
  getCommand(name, scope, projectPath = null, namespace = null) {
296
342
  const baseDir = scope === 'user'
297
343
  ? this.userCommandsDir
298
- : path.join(projectPath, '.claude', 'commands');
344
+ : this.getProjectCommandsDir(projectPath);
299
345
 
300
346
  const relativePath = namespace
301
347
  ? path.join(namespace, `${name}.md`)
@@ -319,6 +365,9 @@ class CommandsService {
319
365
  description: frontmatter.description || '',
320
366
  allowedTools: frontmatter['allowed-tools'] || '',
321
367
  argumentHint: frontmatter['argument-hint'] || '',
368
+ agent: frontmatter.agent || '',
369
+ model: frontmatter.model || '',
370
+ subtask: frontmatter.subtask || '',
322
371
  body,
323
372
  fullContent: content,
324
373
  updatedAt: fs.statSync(fullPath).mtime.getTime()
@@ -328,7 +377,7 @@ class CommandsService {
328
377
  /**
329
378
  * 创建命令
330
379
  */
331
- createCommand({ name, scope, projectPath, namespace, description, allowedTools, argumentHint, body }) {
380
+ createCommand({ name, scope, projectPath, namespace, description, allowedTools, argumentHint, agent, model, subtask, body }) {
332
381
  if (!name || !name.trim()) {
333
382
  throw new Error('命令名称不能为空');
334
383
  }
@@ -340,7 +389,7 @@ class CommandsService {
340
389
 
341
390
  const baseDir = scope === 'user'
342
391
  ? this.userCommandsDir
343
- : path.join(projectPath, '.claude', 'commands');
392
+ : this.getProjectCommandsDir(projectPath);
344
393
 
345
394
  const targetDir = namespace ? path.join(baseDir, namespace) : baseDir;
346
395
  ensureDir(targetDir);
@@ -355,8 +404,13 @@ class CommandsService {
355
404
  // 生成文件内容
356
405
  const frontmatterData = {};
357
406
  if (description) frontmatterData.description = description;
358
- if (allowedTools) frontmatterData['allowed-tools'] = allowedTools;
359
- if (argumentHint) frontmatterData['argument-hint'] = argumentHint;
407
+ if (this.platform !== 'opencode') {
408
+ if (allowedTools) frontmatterData['allowed-tools'] = allowedTools;
409
+ if (argumentHint) frontmatterData['argument-hint'] = argumentHint;
410
+ }
411
+ if (agent) frontmatterData.agent = agent;
412
+ if (model) frontmatterData.model = model;
413
+ if (typeof subtask === 'boolean') frontmatterData.subtask = subtask;
360
414
 
361
415
  let content = '';
362
416
  if (Object.keys(frontmatterData).length > 0) {
@@ -372,10 +426,10 @@ class CommandsService {
372
426
  /**
373
427
  * 更新命令
374
428
  */
375
- updateCommand({ name, scope, projectPath, namespace, description, allowedTools, argumentHint, body }) {
429
+ updateCommand({ name, scope, projectPath, namespace, description, allowedTools, argumentHint, agent, model, subtask, body }) {
376
430
  const baseDir = scope === 'user'
377
431
  ? this.userCommandsDir
378
- : path.join(projectPath, '.claude', 'commands');
432
+ : this.getProjectCommandsDir(projectPath);
379
433
 
380
434
  const relativePath = namespace
381
435
  ? path.join(namespace, `${name}.md`)
@@ -390,8 +444,13 @@ class CommandsService {
390
444
  // 生成文件内容
391
445
  const frontmatterData = {};
392
446
  if (description) frontmatterData.description = description;
393
- if (allowedTools) frontmatterData['allowed-tools'] = allowedTools;
394
- if (argumentHint) frontmatterData['argument-hint'] = argumentHint;
447
+ if (this.platform !== 'opencode') {
448
+ if (allowedTools) frontmatterData['allowed-tools'] = allowedTools;
449
+ if (argumentHint) frontmatterData['argument-hint'] = argumentHint;
450
+ }
451
+ if (agent) frontmatterData.agent = agent;
452
+ if (model) frontmatterData.model = model;
453
+ if (typeof subtask === 'boolean') frontmatterData.subtask = subtask;
395
454
 
396
455
  let content = '';
397
456
  if (Object.keys(frontmatterData).length > 0) {
@@ -410,7 +469,7 @@ class CommandsService {
410
469
  deleteCommand(name, scope, projectPath = null, namespace = null) {
411
470
  const baseDir = scope === 'user'
412
471
  ? this.userCommandsDir
413
- : path.join(projectPath, '.claude', 'commands');
472
+ : this.getProjectCommandsDir(projectPath);
414
473
 
415
474
  const relativePath = namespace
416
475
  ? path.join(namespace, `${name}.md`)
@@ -1,13 +1,12 @@
1
1
  /**
2
2
  * 配置导出/导入服务
3
- * 支持权限模板、配置模板、频道配置的导出与导入
3
+ * 支持配置模板、频道配置的导出与导入
4
4
  */
5
5
 
6
6
  const fs = require('fs');
7
7
  const path = require('path');
8
8
  const os = require('os');
9
9
  const AdmZip = require('adm-zip');
10
- const permissionTemplatesService = require('./permission-templates-service');
11
10
  const configTemplatesService = require('./config-templates-service');
12
11
  const channelsService = require('./channels');
13
12
  const { AgentsService } = require('./agents-service');
@@ -19,7 +18,7 @@ const CONFIG_VERSION = '1.2.0';
19
18
  const SKILL_FILE_ENCODING = 'base64';
20
19
  const SKILL_IGNORE_DIRS = new Set(['.git']);
21
20
  const SKILL_IGNORE_FILES = new Set(['.DS_Store']);
22
- const CC_TOOL_DIR = path.join(os.homedir(), '.claude', 'cc-tool');
21
+ const CC_TOOL_DIR = path.join(os.homedir(), '.cc-tool');
23
22
  const LEGACY_CC_TOOL_DIR = path.join(os.homedir(), '.cc-tool');
24
23
  const CLAUDE_SETTINGS_PATH = path.join(os.homedir(), '.claude', 'settings.json');
25
24
  const LEGACY_PLUGINS_DIR = path.join(LEGACY_CC_TOOL_DIR, 'plugins', 'installed');
@@ -106,8 +105,8 @@ function writeTextFileAbsolute(filePath, content, overwrite, options = {}) {
106
105
 
107
106
  function getConfigFilePath() {
108
107
  try {
109
- const loaderPath = require.resolve('../../config/loader');
110
- return path.resolve(path.dirname(loaderPath), '../../config.json');
108
+ const { getConfigFilePath: resolveConfigPath } = require('../../config/loader');
109
+ return typeof resolveConfigPath === 'function' ? resolveConfigPath() : null;
111
110
  } catch (err) {
112
111
  return null;
113
112
  }
@@ -121,7 +120,7 @@ function buildExportReadme(exportData) {
121
120
  版本: ${exportData.version || CONFIG_VERSION}
122
121
 
123
122
  ## 📦 包含内容
124
- - 权限模板、配置模板、频道配置、工作区、收藏
123
+ - 配置模板、频道配置、工作区、收藏
125
124
  - Agents / Skills / Commands / Rules
126
125
  - 插件 (Plugins)
127
126
  - MCP 服务器配置
@@ -436,17 +435,12 @@ function writeTextFile(baseDir, relativePath, content, overwrite) {
436
435
  */
437
436
  function exportAllConfigs() {
438
437
  try {
439
- // 获取所有权限模板(只导出自定义模板)
440
- const allPermissionTemplates = permissionTemplatesService.getAllTemplates();
441
- const customPermissionTemplates = allPermissionTemplates.filter(t => !t.isBuiltin);
442
-
443
438
  // 获取所有配置模板(只导出自定义模板)
444
439
  const allConfigTemplates = configTemplatesService.getAllTemplates();
445
440
  const customConfigTemplates = allConfigTemplates.filter(t => !t.isBuiltin);
446
441
 
447
442
  // 获取所有频道配置
448
- const channelsData = channelsService.getAllChannels();
449
- const channels = channelsData?.channels || [];
443
+ const channels = channelsService.getAllChannels() || [];
450
444
 
451
445
  // 获取工作区配置
452
446
  const workspaceService = require('./workspace-service');
@@ -549,7 +543,6 @@ function exportAllConfigs() {
549
543
  version: CONFIG_VERSION,
550
544
  exportedAt: new Date().toISOString(),
551
545
  data: {
552
- permissionTemplates: customPermissionTemplates,
553
546
  configTemplates: customConfigTemplates,
554
547
  channels: channels || [],
555
548
  workspaces: workspaces || { workspaces: [] },
@@ -617,7 +610,6 @@ function exportAllConfigsZip() {
617
610
  function importConfigs(importData, options = {}) {
618
611
  const { overwrite = true } = options; // 默认覆盖模式
619
612
  const results = {
620
- permissionTemplates: { success: 0, failed: 0, skipped: 0 },
621
613
  configTemplates: { success: 0, failed: 0, skipped: 0 },
622
614
  channels: { success: 0, failed: 0, skipped: 0 },
623
615
  workspaces: { success: 0, failed: 0, skipped: 0 },
@@ -645,7 +637,6 @@ function importConfigs(importData, options = {}) {
645
637
  }
646
638
 
647
639
  const {
648
- permissionTemplates = [],
649
640
  configTemplates = [],
650
641
  channels = [],
651
642
  workspaces = null,
@@ -665,33 +656,6 @@ function importConfigs(importData, options = {}) {
665
656
  claudeHooks = null
666
657
  } = importData.data;
667
658
 
668
- // 导入权限模板
669
- for (const template of permissionTemplates) {
670
- try {
671
- const existing = permissionTemplatesService.getTemplateById(template.id);
672
-
673
- if (existing && !overwrite) {
674
- results.permissionTemplates.skipped++;
675
- continue;
676
- }
677
-
678
- if (existing && overwrite) {
679
- permissionTemplatesService.updateTemplate(template.id, template);
680
- } else {
681
- const newTemplate = {
682
- ...template,
683
- isBuiltin: false,
684
- importedAt: new Date().toISOString()
685
- };
686
- permissionTemplatesService.createTemplate(newTemplate);
687
- }
688
- results.permissionTemplates.success++;
689
- } catch (err) {
690
- console.error(`[ConfigImport] 导入权限模板失败: ${template.name}`, err);
691
- results.permissionTemplates.failed++;
692
- }
693
- }
694
-
695
659
  // 导入配置模板
696
660
  for (const template of configTemplates) {
697
661
  try {
@@ -722,8 +686,7 @@ function importConfigs(importData, options = {}) {
722
686
  // 导入频道配置
723
687
  for (const channel of channels) {
724
688
  try {
725
- const channelsData = channelsService.getAllChannels();
726
- const existingChannels = channelsData?.channels || [];
689
+ const existingChannels = channelsService.getAllChannels() || [];
727
690
  const existing = existingChannels.find(c => c.id === channel.id);
728
691
 
729
692
  if (existing && !overwrite) {
@@ -1264,7 +1227,6 @@ function generateImportSummary(results) {
1264
1227
  const parts = [];
1265
1228
 
1266
1229
  const types = [
1267
- { key: 'permissionTemplates', label: '权限模板' },
1268
1230
  { key: 'configTemplates', label: '配置模板' },
1269
1231
  { key: 'channels', label: '频道' },
1270
1232
  { key: 'workspaces', label: '工作区' },
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * Config Registry Service
3
3
  *
4
- * Manages a unified config registry at ~/.claude/cc-tool/config-registry.json
4
+ * Manages a unified config registry at ~/.cc-tool/config-registry.json
5
5
  * that tracks skills, commands, agents, rules with enable/disable and per-platform support.
6
6
  *
7
- * Storage directories: ~/.claude/cc-tool/configs/{skills,commands,agents,rules}/
7
+ * Storage directories: ~/.cc-tool/configs/{skills,commands,agents,rules}/
8
8
  */
9
9
 
10
10
  const fs = require('fs');
@@ -12,7 +12,7 @@ const path = require('path');
12
12
  const os = require('os');
13
13
 
14
14
  // Configuration paths
15
- const CC_TOOL_DIR = path.join(os.homedir(), '.claude', 'cc-tool');
15
+ const CC_TOOL_DIR = path.join(os.homedir(), '.cc-tool');
16
16
  const REGISTRY_FILE = path.join(CC_TOOL_DIR, 'config-registry.json');
17
17
  const CONFIGS_DIR = path.join(CC_TOOL_DIR, 'configs');
18
18
 
@@ -27,6 +27,37 @@ const CLAUDE_DIRS = {
27
27
 
28
28
  // Valid config types
29
29
  const CONFIG_TYPES = ['skills', 'commands', 'agents', 'rules', 'plugins'];
30
+ const SUPPORTED_PLATFORMS = ['claude', 'codex', 'opencode'];
31
+
32
+ const PLATFORM_SUPPORT = {
33
+ skills: { claude: true, codex: true, opencode: true },
34
+ commands: { claude: true, codex: true, opencode: true },
35
+ agents: { claude: true, codex: false, opencode: true },
36
+ rules: { claude: true, codex: false, opencode: false },
37
+ plugins: { claude: true, codex: false, opencode: true }
38
+ };
39
+
40
+ function normalizePlatforms(type, platforms = {}) {
41
+ const support = PLATFORM_SUPPORT[type] || {};
42
+ const normalized = {
43
+ claude: !!platforms.claude,
44
+ codex: !!platforms.codex,
45
+ opencode: !!platforms.opencode
46
+ };
47
+
48
+ for (const platform of SUPPORTED_PLATFORMS) {
49
+ if (support[platform] === false) {
50
+ normalized[platform] = false;
51
+ }
52
+ }
53
+
54
+ // Default to Claude enabled when no platform explicitly configured
55
+ if (!platforms || Object.keys(platforms).length === 0) {
56
+ normalized.claude = true;
57
+ }
58
+
59
+ return normalized;
60
+ }
30
61
 
31
62
  // Default registry structure
32
63
  const DEFAULT_REGISTRY = {
@@ -87,6 +118,13 @@ class ConfigRegistryService {
87
118
  if (!data[type]) {
88
119
  data[type] = {};
89
120
  }
121
+
122
+ for (const [name, item] of Object.entries(data[type])) {
123
+ if (!item || typeof item !== 'object') {
124
+ continue;
125
+ }
126
+ data[type][name].platforms = normalizePlatforms(type, item.platforms);
127
+ }
90
128
  }
91
129
 
92
130
  return data;
@@ -147,12 +185,12 @@ class ConfigRegistryService {
147
185
 
148
186
  const existing = registry[type][name];
149
187
  const entry = {
188
+ ...data,
150
189
  enabled: data.enabled !== undefined ? data.enabled : true,
151
- platforms: data.platforms || { claude: true, codex: false },
190
+ platforms: normalizePlatforms(type, data.platforms),
152
191
  createdAt: existing?.createdAt || now,
153
192
  updatedAt: now,
154
- source: data.source || 'local',
155
- ...data
193
+ source: data.source || 'local'
156
194
  };
157
195
 
158
196
  registry[type][name] = entry;
@@ -244,7 +282,7 @@ class ConfigRegistryService {
244
282
  * Toggle platform support for an item
245
283
  * @param {string} type - Config type
246
284
  * @param {string} name - Item name/key
247
- * @param {string} platform - Platform name (claude, codex)
285
+ * @param {string} platform - Platform name (claude, codex, opencode)
248
286
  * @param {boolean} enabled - New platform status
249
287
  * @returns {Object} Updated entry
250
288
  */
@@ -253,10 +291,14 @@ class ConfigRegistryService {
253
291
  throw new Error(`Invalid config type: ${type}`);
254
292
  }
255
293
 
256
- if (!['claude', 'codex'].includes(platform)) {
294
+ if (!SUPPORTED_PLATFORMS.includes(platform)) {
257
295
  throw new Error(`Invalid platform: ${platform}`);
258
296
  }
259
297
 
298
+ if (PLATFORM_SUPPORT[type] && PLATFORM_SUPPORT[type][platform] === false) {
299
+ throw new Error(`Platform "${platform}" is not supported for ${type}`);
300
+ }
301
+
260
302
  const registry = this._readRegistry();
261
303
  const entry = registry[type][name];
262
304
 
@@ -265,7 +307,7 @@ class ConfigRegistryService {
265
307
  }
266
308
 
267
309
  if (!entry.platforms) {
268
- entry.platforms = { claude: true, codex: false };
310
+ entry.platforms = normalizePlatforms(type, {});
269
311
  }
270
312
 
271
313
  entry.platforms[platform] = enabled;
@@ -353,7 +395,7 @@ class ConfigRegistryService {
353
395
  // Add to registry
354
396
  registry.skills[name] = {
355
397
  enabled: true,
356
- platforms: { claude: true, codex: false },
398
+ platforms: normalizePlatforms('skills', { claude: true }),
357
399
  createdAt: new Date().toISOString(),
358
400
  updatedAt: new Date().toISOString(),
359
401
  source: 'imported'
@@ -409,7 +451,7 @@ class ConfigRegistryService {
409
451
  // Add to registry
410
452
  registry.plugins[name] = {
411
453
  enabled: true,
412
- platforms: { claude: true, codex: false },
454
+ platforms: normalizePlatforms('plugins', { claude: true }),
413
455
  createdAt: new Date().toISOString(),
414
456
  updatedAt: new Date().toISOString(),
415
457
  source: 'imported'
@@ -473,7 +515,7 @@ class ConfigRegistryService {
473
515
  // Add to registry
474
516
  registry[type][name] = {
475
517
  enabled: true,
476
- platforms: { claude: true, codex: false },
518
+ platforms: normalizePlatforms(type, { claude: true }),
477
519
  createdAt: new Date().toISOString(),
478
520
  updatedAt: new Date().toISOString(),
479
521
  source: 'imported'
@@ -523,7 +565,8 @@ class ConfigRegistryService {
523
565
  byType: {},
524
566
  byPlatform: {
525
567
  claude: 0,
526
- codex: 0
568
+ codex: 0,
569
+ opencode: 0
527
570
  }
528
571
  };
529
572
 
@@ -534,13 +577,15 @@ class ConfigRegistryService {
534
577
  enabled: items.filter(i => i.enabled).length,
535
578
  disabled: items.filter(i => !i.enabled).length,
536
579
  claude: items.filter(i => i.platforms?.claude).length,
537
- codex: items.filter(i => i.platforms?.codex).length
580
+ codex: items.filter(i => i.platforms?.codex).length,
581
+ opencode: items.filter(i => i.platforms?.opencode).length
538
582
  };
539
583
 
540
584
  stats.byType[type] = typeStats;
541
585
  stats.total += typeStats.total;
542
586
  stats.byPlatform.claude += typeStats.claude;
543
587
  stats.byPlatform.codex += typeStats.codex;
588
+ stats.byPlatform.opencode += typeStats.opencode;
544
589
  }
545
590
 
546
591
  return stats;
@@ -660,7 +705,7 @@ class ConfigRegistryService {
660
705
  if (!registry.skills[name]) {
661
706
  registry.skills[name] = {
662
707
  enabled: true,
663
- platforms: { claude: true, codex: false },
708
+ platforms: normalizePlatforms('skills', { claude: true }),
664
709
  createdAt: new Date().toISOString(),
665
710
  updatedAt: new Date().toISOString(),
666
711
  source: 'synced'
@@ -697,7 +742,7 @@ class ConfigRegistryService {
697
742
  if (!registry.plugins[name]) {
698
743
  registry.plugins[name] = {
699
744
  enabled: true,
700
- platforms: { claude: true, codex: false },
745
+ platforms: normalizePlatforms('plugins', { claude: true }),
701
746
  createdAt: new Date().toISOString(),
702
747
  updatedAt: new Date().toISOString(),
703
748
  source: 'synced'
@@ -739,7 +784,7 @@ class ConfigRegistryService {
739
784
  if (!registry[type][name]) {
740
785
  registry[type][name] = {
741
786
  enabled: true,
742
- platforms: { claude: true, codex: false },
787
+ platforms: normalizePlatforms(type, { claude: true }),
743
788
  createdAt: new Date().toISOString(),
744
789
  updatedAt: new Date().toISOString(),
745
790
  source: 'synced'
@@ -757,6 +802,7 @@ class ConfigRegistryService {
757
802
  module.exports = {
758
803
  ConfigRegistryService,
759
804
  CONFIG_TYPES,
805
+ SUPPORTED_PLATFORMS,
760
806
  CONFIGS_DIR,
761
807
  REGISTRY_FILE
762
808
  };