diff --git a/packages/cli/src/commands/extensions/consent.test.ts b/packages/cli/src/commands/extensions/consent.test.ts index 7d48a7c8c..da41ec04c 100644 --- a/packages/cli/src/commands/extensions/consent.test.ts +++ b/packages/cli/src/commands/extensions/consent.test.ts @@ -35,6 +35,7 @@ describe('extensionConsentString', () => { const config: ExtensionConfig = { name: 'test-extension', version: '1.0.0', + commands: [], }; const result = extensionConsentString(config); @@ -209,6 +210,7 @@ describe('requestConsentOrFail', () => { await requestConsentOrFail(mockRequestConsent, { extensionConfig: { name: 'test-extension', version: '1.0.0' }, + originSource: 'QwenCode', }); expect(mockRequestConsent).toHaveBeenCalled(); @@ -220,6 +222,7 @@ describe('requestConsentOrFail', () => { await expect( requestConsentOrFail(mockRequestConsent, { extensionConfig: { name: 'test-extension', version: '1.0.0' }, + originSource: 'QwenCode', }), ).rejects.toThrow('Installation cancelled for "test-extension".'); }); @@ -233,6 +236,7 @@ describe('requestConsentOrFail', () => { await requestConsentOrFail(mockRequestConsent, { extensionConfig, previousExtensionConfig: extensionConfig, + originSource: 'QwenCode', }); expect(mockRequestConsent).not.toHaveBeenCalled(); @@ -246,6 +250,7 @@ describe('requestConsentOrFail', () => { commands: ['command1'], previousExtensionConfig: { name: 'test-extension', version: '1.0.0' }, previousCommands: [], + originSource: 'QwenCode', }); expect(mockRequestConsent).toHaveBeenCalled(); diff --git a/packages/cli/src/commands/extensions/consent.ts b/packages/cli/src/commands/extensions/consent.ts index cfff6e5b7..0f2321075 100644 --- a/packages/cli/src/commands/extensions/consent.ts +++ b/packages/cli/src/commands/extensions/consent.ts @@ -148,8 +148,17 @@ export function extensionConsentString( commands: string[] = [], skills: SkillConfig[] = [], subagents: SubagentConfig[] = [], + originSource: string = 'QwenCode', ): string { const output: string[] = []; + if (originSource !== 'QwenCode') { + output.push( + t( + 'You are installing an extension from {{originSource}}. Some features may not work perfectly with Qwen Code.', + { originSource }, + ), + ); + } const mcpServerEntries = Object.entries(extensionConfig.mcpServers || {}); output.push( t('Installing extension "{{name}}".', { name: extensionConfig.name }), @@ -221,6 +230,7 @@ export const requestConsentOrFail = async ( if (!options) return; const { extensionConfig, + originSource = 'QwenCode', commands = [], skills = [], subagents = [], @@ -234,6 +244,7 @@ export const requestConsentOrFail = async ( commands, skills, subagents, + originSource, ); if (previousExtensionConfig) { const previousExtensionConsent = extensionConsentString( @@ -241,6 +252,7 @@ export const requestConsentOrFail = async ( previousCommands, previousSkills, previousSubagents, + originSource, ); if (previousExtensionConsent === extensionConsent) { return; diff --git a/packages/cli/src/i18n/locales/de.js b/packages/cli/src/i18n/locales/de.js index a4f0597d8..772e40a89 100644 --- a/packages/cli/src/i18n/locales/de.js +++ b/packages/cli/src/i18n/locales/de.js @@ -425,6 +425,8 @@ export default { 'Diese Erweiterung wird folgende Unteragenten installieren:', 'Installation cancelled for "{{name}}".': 'Installation von "{{name}}" abgebrochen.', + 'You are installing an extension from {{originSource}}. Some features may not work perfectly with Qwen Code.': + 'Sie installieren eine Erweiterung von {{originSource}}. Einige Funktionen funktionieren möglicherweise nicht perfekt mit Qwen Code.', '--ref and --auto-update are not applicable for marketplace extensions.': '--ref und --auto-update sind nicht anwendbar für Marketplace-Erweiterungen.', 'Extension "{{name}}" installed successfully and enabled.': diff --git a/packages/cli/src/i18n/locales/en.js b/packages/cli/src/i18n/locales/en.js index 367fc5fe6..be76025b7 100644 --- a/packages/cli/src/i18n/locales/en.js +++ b/packages/cli/src/i18n/locales/en.js @@ -439,6 +439,8 @@ export default { 'This extension will install the following subagents:', 'Installation cancelled for "{{name}}".': 'Installation cancelled for "{{name}}".', + 'You are installing an extension from {{originSource}}. Some features may not work perfectly with Qwen Code.': + 'You are installing an extension from {{originSource}}. Some features may not work perfectly with Qwen Code.', '--ref and --auto-update are not applicable for marketplace extensions.': '--ref and --auto-update are not applicable for marketplace extensions.', 'Extension "{{name}}" installed successfully and enabled.': diff --git a/packages/cli/src/i18n/locales/ja.js b/packages/cli/src/i18n/locales/ja.js index 201d1ee3d..2cfad0700 100644 --- a/packages/cli/src/i18n/locales/ja.js +++ b/packages/cli/src/i18n/locales/ja.js @@ -326,6 +326,8 @@ export default { 'List active extensions': '有効な拡張機能を一覧表示', 'Update extensions. Usage: update |--all': '拡張機能を更新。使い方: update <拡張機能名>|--all', + 'You are installing an extension from {{originSource}}. Some features may not work perfectly with Qwen Code.': + '{{originSource}} から拡張機能をインストールしています。一部の機能は Qwen Code で完全に動作しない可能性があります。', 'manage IDE integration': 'IDE連携を管理', 'check status of IDE integration': 'IDE連携の状態を確認', 'install required IDE companion for {{ideName}}': diff --git a/packages/cli/src/i18n/locales/pt.js b/packages/cli/src/i18n/locales/pt.js index 40410ce61..62e81def1 100644 --- a/packages/cli/src/i18n/locales/pt.js +++ b/packages/cli/src/i18n/locales/pt.js @@ -454,6 +454,8 @@ export default { 'Esta extensão instalará os seguintes subagentes:', 'Installation cancelled for "{{name}}".': 'Instalação cancelada para "{{name}}".', + 'You are installing an extension from {{originSource}}. Some features may not work perfectly with Qwen Code.': + 'Você está instalando uma extensão de {{originSource}}. Alguns recursos podem não funcionar perfeitamente com o Qwen Code.', '--ref and --auto-update are not applicable for marketplace extensions.': '--ref e --auto-update não são aplicáveis para extensões de marketplace.', 'Extension "{{name}}" installed successfully and enabled.': diff --git a/packages/cli/src/i18n/locales/ru.js b/packages/cli/src/i18n/locales/ru.js index a6b97ed41..46778ecac 100644 --- a/packages/cli/src/i18n/locales/ru.js +++ b/packages/cli/src/i18n/locales/ru.js @@ -444,6 +444,8 @@ export default { 'This extension will install the following subagents:': 'Это расширение установит следующие подагенты:', 'Installation cancelled for "{{name}}".': 'Установка "{{name}}" отменена.', + 'You are installing an extension from {{originSource}}. Some features may not work perfectly with Qwen Code.': + 'Вы устанавливаете расширение от {{originSource}}. Некоторые функции могут работать не идеально с Qwen Code.', '--ref and --auto-update are not applicable for marketplace extensions.': '--ref и --auto-update неприменимы для расширений из маркетплейса.', 'Extension "{{name}}" installed successfully and enabled.': diff --git a/packages/cli/src/i18n/locales/zh.js b/packages/cli/src/i18n/locales/zh.js index 1fb300f5d..d488f9028 100644 --- a/packages/cli/src/i18n/locales/zh.js +++ b/packages/cli/src/i18n/locales/zh.js @@ -421,6 +421,8 @@ export default { 'This extension will install the following subagents:': '此扩展将安装以下子智能体:', 'Installation cancelled for "{{name}}".': '已取消安装 "{{name}}"。', + 'You are installing an extension from {{originSource}}. Some features may not work perfectly with Qwen Code.': + '您正在安装来自 {{originSource}} 的扩展。某些功能可能无法完美兼容 Qwen Code。', '--ref and --auto-update are not applicable for marketplace extensions.': '--ref 和 --auto-update 不适用于市场扩展。', 'Extension "{{name}}" installed successfully and enabled.': diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts index 800e9d21a..0b48bd3f0 100644 --- a/packages/core/src/config/config.ts +++ b/packages/core/src/config/config.ts @@ -206,9 +206,12 @@ export interface GitCoAuthorSettings { email?: string; } +export type ExtensionOriginSource = 'QwenCode' | 'Claude' | 'Gemini'; + export interface ExtensionInstallMetadata { source: string; type: 'git' | 'local' | 'link' | 'github-release' | 'marketplace'; + originSource?: ExtensionOriginSource; releaseTag?: string; // Only present for github-release installs. ref?: string; autoUpdate?: boolean; diff --git a/packages/core/src/extension/claude-converter.ts b/packages/core/src/extension/claude-converter.ts index 84dab93cf..66703203d 100644 --- a/packages/core/src/extension/claude-converter.ts +++ b/packages/core/src/extension/claude-converter.ts @@ -699,6 +699,7 @@ async function resolvePluginSource( const installMetadata: ExtensionInstallMetadata = { source, type: 'git', + originSource: 'Claude', }; try { await downloadFromGitHubRelease(installMetadata, pluginDir); diff --git a/packages/core/src/extension/extensionManager.ts b/packages/core/src/extension/extensionManager.ts index 72ffdb3df..d229aa0d0 100644 --- a/packages/core/src/extension/extensionManager.ts +++ b/packages/core/src/extension/extensionManager.ts @@ -55,7 +55,10 @@ import type { ExtensionSetting, ResolvedExtensionSetting, } from './extensionSettings.js'; -import type { TelemetrySettings } from '../config/config.js'; +import type { + ExtensionOriginSource, + TelemetrySettings, +} from '../config/config.js'; import { logExtensionUpdateEvent } from '../telemetry/loggers.js'; import { ExtensionDisableEvent, @@ -133,6 +136,7 @@ export enum ExtensionUpdateState { export type ExtensionRequestOptions = { extensionConfig: ExtensionConfig; + originSource: ExtensionOriginSource; commands?: string[]; skills?: SkillConfig[]; subagents?: SubagentConfig[]; @@ -243,21 +247,24 @@ async function loadCommandsFromDir(dir: string): Promise { async function convertGeminiOrClaudeExtension( extensionDir: string, pluginName?: string, -) { +): Promise<{ extensionDir: string; originSource: ExtensionOriginSource }> { let newExtensionDir = extensionDir; + let originSource: ExtensionOriginSource = 'QwenCode'; const configFilePath = path.join(extensionDir, EXTENSIONS_CONFIG_FILENAME); if (fs.existsSync(configFilePath)) { newExtensionDir = extensionDir; } else if (isGeminiExtensionConfig(extensionDir)) { newExtensionDir = (await convertGeminiExtensionPackage(extensionDir)) .convertedDir; + originSource = 'Gemini'; } else if (pluginName) { newExtensionDir = ( await convertClaudePluginPackage(extensionDir, pluginName) ).convertedDir; + originSource = 'Claude'; } // Claude plugin conversion not yet implemented - return newExtensionDir; + return { extensionDir: newExtensionDir, originSource }; } // ============================================================================ @@ -801,10 +808,15 @@ export class ExtensionManager { } try { - localSourcePath = await convertGeminiOrClaudeExtension( - localSourcePath, - installMetadata.pluginName, - ); + const { extensionDir, originSource } = + await convertGeminiOrClaudeExtension( + localSourcePath, + installMetadata.pluginName, + ); + + localSourcePath = extensionDir; + installMetadata.originSource = originSource; + newExtensionConfig = this.loadExtensionConfig({ extensionDir: localSourcePath, workspaceDir: currentDir, @@ -866,6 +878,7 @@ export class ExtensionManager { previousCommands, previousSkills, previousSubagents, + originSource: installMetadata.originSource, }); } else { await this.requestConsent({ @@ -877,6 +890,7 @@ export class ExtensionManager { previousCommands, previousSkills, previousSubagents, + originSource: installMetadata.originSource, }); } @@ -1074,6 +1088,7 @@ export class ExtensionManager { const installMetadata: ExtensionInstallMetadata = { source: extension.path, type: 'local', + originSource: extension.installMetadata?.originSource || 'QwenCode', }; await this.installExtension( installMetadata, diff --git a/packages/core/src/extension/marketplace.ts b/packages/core/src/extension/marketplace.ts index dec525579..e9e3cdd18 100644 --- a/packages/core/src/extension/marketplace.ts +++ b/packages/core/src/extension/marketplace.ts @@ -266,6 +266,7 @@ export async function parseInstallSource( if (marketplaceConfig) { installMetadata.type = 'marketplace'; installMetadata.marketplaceConfig = marketplaceConfig; + installMetadata.originSource = 'Claude'; } return installMetadata;