coding-tool-x 3.3.6 → 3.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.
@@ -16,7 +16,7 @@ const { CommandsService } = require('./commands-service');
16
16
  const { SkillService } = require('./skill-service');
17
17
  const { PATHS, NATIVE_PATHS } = require('../../config/paths');
18
18
 
19
- const CONFIG_VERSION = '1.2.0';
19
+ const CONFIG_VERSION = '1.3.0';
20
20
  const SKILL_FILE_ENCODING = 'base64';
21
21
  const SKILL_IGNORE_DIRS = new Set(['.git']);
22
22
  const SKILL_IGNORE_FILES = new Set(['.DS_Store']);
@@ -44,6 +44,41 @@ const CC_PROMPTS_PATH = path.join(CC_TOOL_DIR, 'prompts.json');
44
44
  const CC_SECURITY_PATH = path.join(CC_TOOL_DIR, 'security.json');
45
45
  const LEGACY_UI_CONFIG_PATH = path.join(LEGACY_CC_TOOL_DIR, 'ui-config.json');
46
46
  const LEGACY_NOTIFY_HOOK_PATH = path.join(LEGACY_CC_TOOL_DIR, 'notify-hook.js');
47
+ const GEMINI_SETTINGS_PATH = path.join(path.dirname(NATIVE_PATHS.gemini.env), 'settings.json');
48
+ const AGENT_PLATFORMS = ['claude', 'codex', 'opencode'];
49
+ const COMMAND_PLATFORMS = ['claude', 'opencode'];
50
+ const SKILL_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode'];
51
+
52
+ function getOpenCodeConfigPaths() {
53
+ try {
54
+ const { CONFIG_PATHS } = require('./opencode-settings-manager');
55
+ return CONFIG_PATHS || {};
56
+ } catch (err) {
57
+ return {};
58
+ }
59
+ }
60
+
61
+ function getNativeConfigSpecs() {
62
+ const openCodeConfigPaths = getOpenCodeConfigPaths();
63
+ return {
64
+ claude: {
65
+ settings: { path: NATIVE_PATHS.claude.settings, format: 'json' }
66
+ },
67
+ codex: {
68
+ config: { path: NATIVE_PATHS.codex.config, format: 'text' },
69
+ auth: { path: NATIVE_PATHS.codex.auth, format: 'json', mode: 0o600 }
70
+ },
71
+ gemini: {
72
+ env: { path: NATIVE_PATHS.gemini.env, format: 'text', mode: 0o600 },
73
+ settings: { path: GEMINI_SETTINGS_PATH, format: 'json' }
74
+ },
75
+ opencode: {
76
+ opencodeJsonc: { path: openCodeConfigPaths.opencodec, format: 'text' },
77
+ opencodeJson: { path: openCodeConfigPaths.opencode, format: 'text' },
78
+ configJson: { path: openCodeConfigPaths.config, format: 'text' }
79
+ }
80
+ };
81
+ }
47
82
 
48
83
  function ensureDir(dirPath) {
49
84
  if (!fs.existsSync(dirPath)) {
@@ -70,6 +105,39 @@ function readTextFileSafe(filePath) {
70
105
  }
71
106
  }
72
107
 
108
+ function readNativeConfigSnapshot(spec) {
109
+ if (!spec?.path || !fs.existsSync(spec.path)) {
110
+ return null;
111
+ }
112
+
113
+ try {
114
+ const rawContent = fs.readFileSync(spec.path, 'utf8');
115
+ if (spec.format === 'json') {
116
+ try {
117
+ return {
118
+ format: 'json',
119
+ fileName: path.basename(spec.path),
120
+ content: JSON.parse(rawContent)
121
+ };
122
+ } catch (err) {
123
+ return {
124
+ format: 'text',
125
+ fileName: path.basename(spec.path),
126
+ content: rawContent
127
+ };
128
+ }
129
+ }
130
+
131
+ return {
132
+ format: spec.format || 'text',
133
+ fileName: path.basename(spec.path),
134
+ content: rawContent
135
+ };
136
+ } catch (err) {
137
+ return null;
138
+ }
139
+ }
140
+
73
141
  function writeJsonFileAbsolute(filePath, data, overwrite, options = {}) {
74
142
  if (data === undefined) {
75
143
  return 'failed';
@@ -104,6 +172,19 @@ function writeTextFileAbsolute(filePath, content, overwrite, options = {}) {
104
172
  return 'success';
105
173
  }
106
174
 
175
+ function writeNativeConfigAbsolute(spec, entry, overwrite) {
176
+ if (!spec?.path || !entry || entry.content === undefined) {
177
+ return 'failed';
178
+ }
179
+
180
+ const format = entry.format || spec.format || 'text';
181
+ if (format === 'json' && entry.content && typeof entry.content === 'object') {
182
+ return writeJsonFileAbsolute(spec.path, entry.content, overwrite, { mode: spec.mode });
183
+ }
184
+
185
+ return writeTextFileAbsolute(spec.path, String(entry.content), overwrite, { mode: spec.mode });
186
+ }
187
+
107
188
  function getConfigFilePath() {
108
189
  try {
109
190
  const { getConfigFilePath: resolveConfigPath } = require('../../config/loader');
@@ -125,6 +206,7 @@ function buildExportReadme(exportData) {
125
206
  - Agents / Skills / Commands
126
207
  - 插件 (Plugins)
127
208
  - MCP 服务器配置
209
+ - 各平台原生配置(Claude / Codex / Gemini / OpenCode)
128
210
  - UI 配置(主题、面板显示、排序等)
129
211
  - Prompts 预设
130
212
  - 安全配置
@@ -184,6 +266,288 @@ function buildCommandContent(command) {
184
266
  return `${lines.join('\n')}${body}`;
185
267
  }
186
268
 
269
+ function buildAgentExportItem(agent, platform) {
270
+ return {
271
+ platform,
272
+ fileName: agent.fileName,
273
+ name: agent.name,
274
+ description: agent.description,
275
+ tools: agent.tools,
276
+ model: agent.model,
277
+ permissionMode: agent.permissionMode,
278
+ skills: agent.skills,
279
+ path: agent.path,
280
+ systemPrompt: agent.systemPrompt,
281
+ fullContent: agent.fullContent
282
+ };
283
+ }
284
+
285
+ function buildCommandExportItem(command, platform) {
286
+ return {
287
+ platform,
288
+ name: command.name,
289
+ namespace: command.namespace,
290
+ description: command.description,
291
+ allowedTools: command.allowedTools,
292
+ argumentHint: command.argumentHint,
293
+ path: command.path,
294
+ body: command.body,
295
+ fullContent: command.fullContent
296
+ };
297
+ }
298
+
299
+ function exportAgentsSnapshotByPlatform() {
300
+ return AGENT_PLATFORMS.reduce((result, platform) => {
301
+ try {
302
+ const agentsService = new AgentsService(platform);
303
+ const { agents: rawAgents = [] } = agentsService.listAgents();
304
+ result[platform] = rawAgents.map(agent => buildAgentExportItem(agent, platform));
305
+ } catch (err) {
306
+ console.warn(`[ConfigExport] Failed to export agents for ${platform}:`, err.message);
307
+ result[platform] = [];
308
+ }
309
+ return result;
310
+ }, {});
311
+ }
312
+
313
+ function exportCommandsSnapshotByPlatform() {
314
+ return COMMAND_PLATFORMS.reduce((result, platform) => {
315
+ try {
316
+ const commandsService = new CommandsService(platform);
317
+ const { commands: rawCommands = [] } = commandsService.listCommands();
318
+ result[platform] = rawCommands.map(command => buildCommandExportItem(command, platform));
319
+ } catch (err) {
320
+ console.warn(`[ConfigExport] Failed to export commands for ${platform}:`, err.message);
321
+ result[platform] = [];
322
+ }
323
+ return result;
324
+ }, {});
325
+ }
326
+
327
+ function normalizePlatformItems(legacyItems = [], byPlatform = {}, supportedPlatforms = [], defaultPlatform = 'claude') {
328
+ const result = Object.fromEntries(supportedPlatforms.map(platform => [platform, []]));
329
+ const structuredPlatforms = new Set();
330
+
331
+ for (const platform of supportedPlatforms) {
332
+ if (Array.isArray(byPlatform?.[platform])) {
333
+ result[platform] = byPlatform[platform].map(item => ({
334
+ ...item,
335
+ platform: supportedPlatforms.includes(item?.platform) ? item.platform : platform
336
+ }));
337
+ structuredPlatforms.add(platform);
338
+ }
339
+ }
340
+
341
+ if (!Array.isArray(legacyItems)) {
342
+ return result;
343
+ }
344
+
345
+ for (const item of legacyItems) {
346
+ const platform = supportedPlatforms.includes(item?.platform) ? item.platform : defaultPlatform;
347
+ if (structuredPlatforms.has(platform)) {
348
+ continue;
349
+ }
350
+ result[platform].push({ ...item, platform });
351
+ }
352
+
353
+ return result;
354
+ }
355
+
356
+ function resolveAgentRelativePath(agent, platform) {
357
+ const pathCandidates = [agent?.path, agent?.fileName]
358
+ .filter(value => typeof value === 'string' && value.trim())
359
+ .map(value => value.trim());
360
+
361
+ for (const candidate of pathCandidates) {
362
+ if (path.extname(candidate)) {
363
+ return candidate;
364
+ }
365
+ }
366
+
367
+ const baseName = agent?.fileName || agent?.name;
368
+ if (!baseName) {
369
+ return null;
370
+ }
371
+
372
+ return platform === 'codex' ? `${baseName}.toml` : `${baseName}.md`;
373
+ }
374
+
375
+ function resolveCommandRelativePath(command) {
376
+ if (typeof command?.path === 'string' && command.path.trim()) {
377
+ return command.path.trim();
378
+ }
379
+ if (command?.namespace) {
380
+ return path.join(command.namespace, `${command.name}.md`);
381
+ }
382
+ return command?.name ? `${command.name}.md` : null;
383
+ }
384
+
385
+ function pickPrimaryChannel(channels = []) {
386
+ if (!Array.isArray(channels) || channels.length === 0) {
387
+ return null;
388
+ }
389
+ return channels.find(channel => channel.enabled !== false) || channels[0] || null;
390
+ }
391
+
392
+ function findMatchingPersistedChannel(existingChannels = [], importedChannel = {}, extraMatchers = []) {
393
+ if (!Array.isArray(existingChannels) || existingChannels.length === 0 || !importedChannel) {
394
+ return null;
395
+ }
396
+
397
+ if (importedChannel.id) {
398
+ const exactMatch = existingChannels.find(channel => channel.id === importedChannel.id);
399
+ if (exactMatch) {
400
+ return exactMatch;
401
+ }
402
+ }
403
+
404
+ for (const matcher of extraMatchers) {
405
+ const matchedChannel = existingChannels.find(channel => matcher(channel, importedChannel));
406
+ if (matchedChannel) {
407
+ return matchedChannel;
408
+ }
409
+ }
410
+
411
+ return null;
412
+ }
413
+
414
+ function sanitizeOpenCodeProviderId(value) {
415
+ return String(value || '')
416
+ .trim()
417
+ .toLowerCase()
418
+ .replace(/[^a-z0-9-]/g, '-')
419
+ .replace(/-+/g, '-')
420
+ .replace(/^-|-$/g, '') || 'channel';
421
+ }
422
+
423
+ function buildOpenCodeNativeConfig(channels = []) {
424
+ const config = { provider: {} };
425
+ const usedProviderIds = new Set();
426
+ let defaultModelRef = '';
427
+
428
+ channels.forEach((channel) => {
429
+ const baseProviderId = sanitizeOpenCodeProviderId(channel.providerKey || channel.name);
430
+ let providerId = baseProviderId;
431
+ let suffix = 2;
432
+ while (usedProviderIds.has(providerId)) {
433
+ providerId = `${baseProviderId}-${suffix}`;
434
+ suffix += 1;
435
+ }
436
+ usedProviderIds.add(providerId);
437
+
438
+ const provider = {
439
+ npm: '@ai-sdk/openai-compatible',
440
+ name: channel.name || providerId,
441
+ options: {
442
+ baseURL: channel.baseUrl || '',
443
+ apiKey: channel.apiKey || ''
444
+ }
445
+ };
446
+
447
+ const models = {};
448
+ const allowedModels = Array.isArray(channel.allowedModels) ? channel.allowedModels : [];
449
+ const allModels = allowedModels.length > 0
450
+ ? allowedModels
451
+ : [channel.model].filter(Boolean);
452
+
453
+ allModels.forEach((modelId) => {
454
+ const normalizedModel = String(modelId || '').trim();
455
+ if (!normalizedModel) return;
456
+ models[normalizedModel] = { name: normalizedModel };
457
+ if (!defaultModelRef && (channel.enabled !== false || !pickPrimaryChannel(channels))) {
458
+ defaultModelRef = `${providerId}/${normalizedModel}`;
459
+ }
460
+ });
461
+
462
+ if (Object.keys(models).length > 0) {
463
+ provider.models = models;
464
+ }
465
+
466
+ config.provider[providerId] = provider;
467
+ });
468
+
469
+ const preferredChannel = pickPrimaryChannel(channels);
470
+ if (!defaultModelRef && preferredChannel?.model) {
471
+ const providerId = sanitizeOpenCodeProviderId(preferredChannel.providerKey || preferredChannel.name);
472
+ defaultModelRef = `${providerId}/${preferredChannel.model}`;
473
+ }
474
+
475
+ if (defaultModelRef) {
476
+ config.model = defaultModelRef;
477
+ }
478
+
479
+ return config;
480
+ }
481
+
482
+ function syncImportedChannelsToNativeConfigs(importChannelsByType, nativeConfigs, overwrite) {
483
+ const hasNativeConfigFor = (platform) => !!nativeConfigs?.[platform] && Object.keys(nativeConfigs[platform]).length > 0;
484
+
485
+ try {
486
+ if (!hasNativeConfigFor('claude')) {
487
+ const primaryClaudeChannel = pickPrimaryChannel(importChannelsByType.claude);
488
+ const persistedClaudeChannel = findMatchingPersistedChannel(
489
+ channelsService.getAllChannels?.() || [],
490
+ primaryClaudeChannel,
491
+ [
492
+ (channel, imported) => channel.name === imported.name && channel.baseUrl === imported.baseUrl
493
+ ]
494
+ );
495
+ if (persistedClaudeChannel?.id) {
496
+ ensureDir(path.dirname(NATIVE_PATHS.claude.settings));
497
+ channelsService.applyChannelToSettings(persistedClaudeChannel.id);
498
+ }
499
+ }
500
+ } catch (err) {
501
+ console.warn('[ConfigImport] Claude native sync fallback failed:', err.message);
502
+ }
503
+
504
+ try {
505
+ if (!hasNativeConfigFor('codex')) {
506
+ codexChannelsService.writeCodexConfigForMultiChannel(importChannelsByType.codex || []);
507
+ }
508
+ } catch (err) {
509
+ console.warn('[ConfigImport] Codex native sync fallback failed:', err.message);
510
+ }
511
+
512
+ try {
513
+ if (!hasNativeConfigFor('gemini')) {
514
+ const primaryGeminiChannel = pickPrimaryChannel(importChannelsByType.gemini);
515
+ const persistedGeminiChannel = findMatchingPersistedChannel(
516
+ geminiChannelsService.getChannels?.().channels || [],
517
+ primaryGeminiChannel,
518
+ [
519
+ (channel, imported) => channel.name === imported.name && channel.baseUrl === imported.baseUrl
520
+ ]
521
+ );
522
+ if (persistedGeminiChannel?.id) {
523
+ geminiChannelsService.applyChannelToSettings(persistedGeminiChannel.id);
524
+ }
525
+ }
526
+ } catch (err) {
527
+ console.warn('[ConfigImport] Gemini native sync fallback failed:', err.message);
528
+ }
529
+
530
+ try {
531
+ if (!hasNativeConfigFor('opencode')) {
532
+ const openCodeSpecs = getNativeConfigSpecs().opencode || {};
533
+ const preferredSpec = [
534
+ openCodeSpecs.opencodeJsonc,
535
+ openCodeSpecs.opencodeJson,
536
+ openCodeSpecs.configJson
537
+ ].find(spec => spec?.path && fs.existsSync(spec.path))
538
+ || openCodeSpecs.opencodeJson
539
+ || openCodeSpecs.configJson;
540
+
541
+ if (preferredSpec?.path) {
542
+ const payload = buildOpenCodeNativeConfig(importChannelsByType.opencode || []);
543
+ writeTextFileAbsolute(preferredSpec.path, JSON.stringify(payload, null, 2), overwrite);
544
+ }
545
+ }
546
+ } catch (err) {
547
+ console.warn('[ConfigImport] OpenCode native sync fallback failed:', err.message);
548
+ }
549
+ }
550
+
187
551
  function collectSkillFiles(baseDir) {
188
552
  const files = [];
189
553
  const stack = [baseDir];
@@ -277,8 +641,8 @@ function collectPluginFiles(pluginDir, basePath = '') {
277
641
  return files;
278
642
  }
279
643
 
280
- function exportSkillsSnapshot() {
281
- const skillService = new SkillService();
644
+ function exportSkillsSnapshot(platform = 'claude') {
645
+ const skillService = new SkillService(platform);
282
646
  const installedSkills = skillService.getInstalledSkills();
283
647
  const baseDir = skillService.installDir;
284
648
 
@@ -289,6 +653,7 @@ function exportSkillsSnapshot() {
289
653
  return null;
290
654
  }
291
655
  return {
656
+ platform,
292
657
  directory,
293
658
  name: skill.name || directory,
294
659
  description: skill.description || '',
@@ -297,6 +662,36 @@ function exportSkillsSnapshot() {
297
662
  }).filter(Boolean);
298
663
  }
299
664
 
665
+ function exportSkillsSnapshotByPlatform() {
666
+ return SKILL_PLATFORMS.reduce((result, platform) => {
667
+ try {
668
+ result[platform] = exportSkillsSnapshot(platform);
669
+ } catch (err) {
670
+ console.warn(`[ConfigExport] Failed to export skills for ${platform}:`, err.message);
671
+ result[platform] = [];
672
+ }
673
+ return result;
674
+ }, {});
675
+ }
676
+
677
+ function exportNativeConfigs() {
678
+ const specs = getNativeConfigSpecs();
679
+ return Object.entries(specs).reduce((result, [platform, platformSpecs]) => {
680
+ const exportedEntries = Object.entries(platformSpecs).reduce((entries, [key, spec]) => {
681
+ const snapshot = readNativeConfigSnapshot(spec);
682
+ if (snapshot) {
683
+ entries[key] = snapshot;
684
+ }
685
+ return entries;
686
+ }, {});
687
+
688
+ if (Object.keys(exportedEntries).length > 0) {
689
+ result[platform] = exportedEntries;
690
+ }
691
+ return result;
692
+ }, {});
693
+ }
694
+
300
695
  function exportLegacyPlugins() {
301
696
  const plugins = [];
302
697
 
@@ -447,38 +842,13 @@ function exportAllConfigs() {
447
842
  const favoritesService = require('./favorites');
448
843
  const favorites = favoritesService.loadFavorites();
449
844
 
450
- // 获取 Agents 配置
451
- const agentsService = new AgentsService();
452
- const { agents: rawAgents } = agentsService.listAgents();
453
- const agents = rawAgents.map(agent => ({
454
- fileName: agent.fileName,
455
- name: agent.name,
456
- description: agent.description,
457
- tools: agent.tools,
458
- model: agent.model,
459
- permissionMode: agent.permissionMode,
460
- skills: agent.skills,
461
- path: agent.path,
462
- systemPrompt: agent.systemPrompt,
463
- fullContent: agent.fullContent
464
- }));
465
-
466
- // 获取 Skills 配置
467
- const skills = exportSkillsSnapshot();
468
-
469
- // 获取 Commands 配置
470
- const commandsService = new CommandsService();
471
- const { commands: rawCommands } = commandsService.listCommands();
472
- const commands = rawCommands.map(command => ({
473
- name: command.name,
474
- namespace: command.namespace,
475
- description: command.description,
476
- allowedTools: command.allowedTools,
477
- argumentHint: command.argumentHint,
478
- path: command.path,
479
- body: command.body,
480
- fullContent: command.fullContent
481
- }));
845
+ // 获取 Agents / Skills / Commands 配置(多平台)
846
+ const agentsByPlatform = exportAgentsSnapshotByPlatform();
847
+ const skillsByPlatform = exportSkillsSnapshotByPlatform();
848
+ const commandsByPlatform = exportCommandsSnapshotByPlatform();
849
+ const agents = agentsByPlatform.claude || [];
850
+ const skills = skillsByPlatform.claude || [];
851
+ const commands = commandsByPlatform.claude || [];
482
852
 
483
853
  // 获取 MCP 配置
484
854
  const mcpService = require('./mcp-service');
@@ -486,6 +856,7 @@ function exportAllConfigs() {
486
856
 
487
857
  // 获取 Plugins 配置
488
858
  const plugins = exportPluginsSnapshot();
859
+ const nativeConfigs = exportNativeConfigs();
489
860
 
490
861
  // 读取 Markdown 配置文件
491
862
  const { PATHS } = require('../../config/paths');
@@ -530,8 +901,11 @@ function exportAllConfigs() {
530
901
  workspaces: workspaces || { workspaces: [] },
531
902
  favorites: favorites || { favorites: [] },
532
903
  agents: agents || [],
904
+ agentsByPlatform,
533
905
  skills: skills || [],
906
+ skillsByPlatform,
534
907
  commands: commands || [],
908
+ commandsByPlatform,
535
909
  mcpServers: mcpServers || [],
536
910
  plugins: plugins || [],
537
911
  markdownFiles: markdownFiles,
@@ -539,6 +913,7 @@ function exportAllConfigs() {
539
913
  prompts: prompts,
540
914
  security: security,
541
915
  appConfig: appConfig,
916
+ nativeConfigs,
542
917
  claudeHooks: claudeHooks
543
918
  }
544
919
  };
@@ -603,6 +978,7 @@ async function importConfigs(importData, options = {}) {
603
978
  prompts: { success: 0, failed: 0, skipped: 0 },
604
979
  security: { success: 0, failed: 0, skipped: 0 },
605
980
  appConfig: { success: 0, failed: 0, skipped: 0 },
981
+ nativeConfigs: { success: 0, failed: 0, skipped: 0 },
606
982
  claudeHooks: { success: 0, failed: 0, skipped: 0 }
607
983
  };
608
984
 
@@ -619,17 +995,25 @@ async function importConfigs(importData, options = {}) {
619
995
  workspaces = null,
620
996
  favorites = null,
621
997
  agents = [],
998
+ agentsByPlatform = {},
622
999
  skills = [],
1000
+ skillsByPlatform = {},
623
1001
  commands = [],
1002
+ commandsByPlatform = {},
624
1003
  mcpServers = [],
625
1004
  markdownFiles = {},
626
1005
  uiConfig = null,
627
1006
  prompts = null,
628
1007
  security = null,
629
1008
  appConfig = null,
1009
+ nativeConfigs = {},
630
1010
  claudeHooks = null
631
1011
  } = importData.data;
632
1012
 
1013
+ const importAgentsByPlatform = normalizePlatformItems(agents, agentsByPlatform, AGENT_PLATFORMS);
1014
+ const importSkillsByPlatform = normalizePlatformItems(skills, skillsByPlatform, SKILL_PLATFORMS);
1015
+ const importCommandsByPlatform = normalizePlatformItems(commands, commandsByPlatform, COMMAND_PLATFORMS);
1016
+
633
1017
  const hasTypedChannels = channelsByType && typeof channelsByType === 'object';
634
1018
  const importChannelsByType = {
635
1019
  claude: hasTypedChannels && Array.isArray(channelsByType.claude)
@@ -759,26 +1143,28 @@ async function importConfigs(importData, options = {}) {
759
1143
  }
760
1144
  }
761
1145
 
762
- // 导入 Agents
763
- if (agents && agents.length > 0) {
1146
+ // 导入 Agents(多平台)
1147
+ if (AGENT_PLATFORMS.some(platform => importAgentsByPlatform[platform]?.length > 0)) {
764
1148
  try {
765
- const agentsService = new AgentsService();
766
- const baseDir = agentsService.userAgentsDir;
767
-
768
- for (const agent of agents) {
769
- const relativePath = agent.path || agent.fileName || agent.name;
770
- const filePath = relativePath && relativePath.endsWith('.md')
771
- ? relativePath
772
- : (relativePath ? `${relativePath}.md` : null);
773
- const content = agent.fullContent || agent.content || buildAgentContent(agent);
774
-
775
- const status = filePath ? writeTextFile(baseDir, filePath, content, overwrite) : 'failed';
776
- if (status === 'success') {
777
- results.agents.success++;
778
- } else if (status === 'skipped') {
779
- results.agents.skipped++;
780
- } else {
781
- results.agents.failed++;
1149
+ for (const platform of AGENT_PLATFORMS) {
1150
+ const platformAgents = importAgentsByPlatform[platform] || [];
1151
+ if (platformAgents.length === 0) continue;
1152
+
1153
+ const agentsService = new AgentsService(platform);
1154
+ const baseDir = agentsService.userAgentsDir;
1155
+
1156
+ for (const agent of platformAgents) {
1157
+ const filePath = resolveAgentRelativePath(agent, platform);
1158
+ const content = agent.fullContent || agent.content || buildAgentContent(agent);
1159
+ const status = filePath ? writeTextFile(baseDir, filePath, content, overwrite) : 'failed';
1160
+
1161
+ if (status === 'success') {
1162
+ results.agents.success++;
1163
+ } else if (status === 'skipped') {
1164
+ results.agents.skipped++;
1165
+ } else {
1166
+ results.agents.failed++;
1167
+ }
782
1168
  }
783
1169
  }
784
1170
  } catch (err) {
@@ -786,56 +1172,61 @@ async function importConfigs(importData, options = {}) {
786
1172
  }
787
1173
  }
788
1174
 
789
- // 导入 Skills
790
- if (skills && skills.length > 0) {
1175
+ // 导入 Skills(多平台)
1176
+ if (SKILL_PLATFORMS.some(platform => importSkillsByPlatform[platform]?.length > 0)) {
791
1177
  try {
792
- const skillService = new SkillService();
793
- const baseDir = skillService.installDir;
794
- ensureDir(baseDir);
795
-
796
- for (const skill of skills) {
797
- const directory = skill.directory;
798
- const skillDir = resolveSafePath(baseDir, directory);
799
- if (!skillDir) {
800
- results.skills.failed++;
801
- continue;
802
- }
803
-
804
- if (fs.existsSync(skillDir)) {
805
- if (!overwrite) {
806
- results.skills.skipped++;
1178
+ for (const platform of SKILL_PLATFORMS) {
1179
+ const platformSkills = importSkillsByPlatform[platform] || [];
1180
+ if (platformSkills.length === 0) continue;
1181
+
1182
+ const skillService = new SkillService(platform);
1183
+ const baseDir = skillService.installDir;
1184
+ ensureDir(baseDir);
1185
+
1186
+ for (const skill of platformSkills) {
1187
+ const directory = skill.directory;
1188
+ const skillDir = resolveSafePath(baseDir, directory);
1189
+ if (!skillDir) {
1190
+ results.skills.failed++;
807
1191
  continue;
808
1192
  }
809
- fs.rmSync(skillDir, { recursive: true, force: true });
810
- }
811
-
812
- ensureDir(skillDir);
813
- const files = Array.isArray(skill.files) ? skill.files : [];
814
- let failed = false;
815
1193
 
816
- for (const file of files) {
817
- const filePath = resolveSafePath(skillDir, file.path);
818
- if (!filePath) {
819
- failed = true;
820
- break;
1194
+ if (fs.existsSync(skillDir)) {
1195
+ if (!overwrite) {
1196
+ results.skills.skipped++;
1197
+ continue;
1198
+ }
1199
+ fs.rmSync(skillDir, { recursive: true, force: true });
821
1200
  }
822
- ensureDir(path.dirname(filePath));
823
- try {
824
- if (file.encoding === SKILL_FILE_ENCODING) {
825
- fs.writeFileSync(filePath, Buffer.from(file.content || '', SKILL_FILE_ENCODING));
826
- } else {
827
- fs.writeFileSync(filePath, file.content || '', file.encoding || 'utf8');
1201
+
1202
+ ensureDir(skillDir);
1203
+ const files = Array.isArray(skill.files) ? skill.files : [];
1204
+ let failed = false;
1205
+
1206
+ for (const file of files) {
1207
+ const filePath = resolveSafePath(skillDir, file.path);
1208
+ if (!filePath) {
1209
+ failed = true;
1210
+ break;
1211
+ }
1212
+ ensureDir(path.dirname(filePath));
1213
+ try {
1214
+ if (file.encoding === SKILL_FILE_ENCODING) {
1215
+ fs.writeFileSync(filePath, Buffer.from(file.content || '', SKILL_FILE_ENCODING));
1216
+ } else {
1217
+ fs.writeFileSync(filePath, file.content || '', file.encoding || 'utf8');
1218
+ }
1219
+ } catch (err) {
1220
+ failed = true;
1221
+ break;
828
1222
  }
829
- } catch (err) {
830
- failed = true;
831
- break;
832
1223
  }
833
- }
834
1224
 
835
- if (failed || files.length === 0) {
836
- results.skills.failed++;
837
- } else {
838
- results.skills.success++;
1225
+ if (failed || files.length === 0) {
1226
+ results.skills.failed++;
1227
+ } else {
1228
+ results.skills.success++;
1229
+ }
839
1230
  }
840
1231
  }
841
1232
  } catch (err) {
@@ -980,27 +1371,28 @@ async function importConfigs(importData, options = {}) {
980
1371
  }
981
1372
  }
982
1373
 
983
- // 导入 Commands
984
- if (commands && commands.length > 0) {
1374
+ // 导入 Commands(多平台)
1375
+ if (COMMAND_PLATFORMS.some(platform => importCommandsByPlatform[platform]?.length > 0)) {
985
1376
  try {
986
- const commandsService = new CommandsService();
987
- const baseDir = commandsService.userCommandsDir;
988
-
989
- for (const command of commands) {
990
- const relativePath = command.path || (
991
- command.namespace
992
- ? path.join(command.namespace, `${command.name}.md`)
993
- : `${command.name}.md`
994
- );
995
- const content = command.fullContent || command.content || buildCommandContent(command);
996
-
997
- const status = relativePath ? writeTextFile(baseDir, relativePath, content, overwrite) : 'failed';
998
- if (status === 'success') {
999
- results.commands.success++;
1000
- } else if (status === 'skipped') {
1001
- results.commands.skipped++;
1002
- } else {
1003
- results.commands.failed++;
1377
+ for (const platform of COMMAND_PLATFORMS) {
1378
+ const platformCommands = importCommandsByPlatform[platform] || [];
1379
+ if (platformCommands.length === 0) continue;
1380
+
1381
+ const commandsService = new CommandsService(platform);
1382
+ const baseDir = commandsService.userCommandsDir;
1383
+
1384
+ for (const command of platformCommands) {
1385
+ const relativePath = resolveCommandRelativePath(command);
1386
+ const content = command.fullContent || command.content || buildCommandContent(command);
1387
+
1388
+ const status = relativePath ? writeTextFile(baseDir, relativePath, content, overwrite) : 'failed';
1389
+ if (status === 'success') {
1390
+ results.commands.success++;
1391
+ } else if (status === 'skipped') {
1392
+ results.commands.skipped++;
1393
+ } else {
1394
+ results.commands.failed++;
1395
+ }
1004
1396
  }
1005
1397
  }
1006
1398
  } catch (err) {
@@ -1073,6 +1465,12 @@ async function importConfigs(importData, options = {}) {
1073
1465
  try {
1074
1466
  const status = writeJsonFileAbsolute(CC_PROMPTS_PATH, prompts, overwrite);
1075
1467
  if (status === 'success') {
1468
+ const promptsService = require('./prompts-service');
1469
+ if (prompts.activePresetId && prompts.presets?.[prompts.activePresetId]) {
1470
+ await promptsService.activatePreset(prompts.activePresetId);
1471
+ } else if (overwrite) {
1472
+ await promptsService.deactivatePrompt();
1473
+ }
1076
1474
  results.prompts.success++;
1077
1475
  } else if (status === 'skipped') {
1078
1476
  results.prompts.skipped++;
@@ -1119,6 +1517,39 @@ async function importConfigs(importData, options = {}) {
1119
1517
  }
1120
1518
  }
1121
1519
 
1520
+ // 导入各平台原生配置
1521
+ if (nativeConfigs && typeof nativeConfigs === 'object' && Object.keys(nativeConfigs).length > 0) {
1522
+ const nativeConfigSpecs = getNativeConfigSpecs();
1523
+
1524
+ for (const [platform, platformEntries] of Object.entries(nativeConfigs)) {
1525
+ const platformSpecs = nativeConfigSpecs[platform];
1526
+ if (!platformSpecs || !platformEntries || typeof platformEntries !== 'object') {
1527
+ continue;
1528
+ }
1529
+
1530
+ for (const [key, entry] of Object.entries(platformEntries)) {
1531
+ const spec = platformSpecs[key];
1532
+ if (!spec) {
1533
+ continue;
1534
+ }
1535
+
1536
+ try {
1537
+ const status = writeNativeConfigAbsolute(spec, entry, overwrite);
1538
+ if (status === 'success') {
1539
+ results.nativeConfigs.success++;
1540
+ } else if (status === 'skipped') {
1541
+ results.nativeConfigs.skipped++;
1542
+ } else {
1543
+ results.nativeConfigs.failed++;
1544
+ }
1545
+ } catch (err) {
1546
+ console.error(`[ConfigImport] 导入 ${platform}.${key} 原生配置失败:`, err);
1547
+ results.nativeConfigs.failed++;
1548
+ }
1549
+ }
1550
+ }
1551
+ }
1552
+
1122
1553
  // 导入 Claude Hooks 配置
1123
1554
  if (claudeHooks && typeof claudeHooks === 'object') {
1124
1555
  let didWrite = false;
@@ -1172,6 +1603,8 @@ async function importConfigs(importData, options = {}) {
1172
1603
  }
1173
1604
  }
1174
1605
 
1606
+ syncImportedChannelsToNativeConfigs(importChannelsByType, nativeConfigs, overwrite);
1607
+
1175
1608
  return {
1176
1609
  success: true,
1177
1610
  results,
@@ -1208,6 +1641,7 @@ function generateImportSummary(results) {
1208
1641
  { key: 'prompts', label: 'Prompts' },
1209
1642
  { key: 'security', label: '安全配置' },
1210
1643
  { key: 'appConfig', label: '高级配置' },
1644
+ { key: 'nativeConfigs', label: '原生配置' },
1211
1645
  { key: 'claudeHooks', label: 'Claude Hooks' }
1212
1646
  ];
1213
1647