mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
Add extensions enable command (#7042)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
This commit is contained in:
@@ -10,6 +10,7 @@ import { uninstallCommand } from './extensions/uninstall.js';
|
|||||||
import { listCommand } from './extensions/list.js';
|
import { listCommand } from './extensions/list.js';
|
||||||
import { updateCommand } from './extensions/update.js';
|
import { updateCommand } from './extensions/update.js';
|
||||||
import { disableCommand } from './extensions/disable.js';
|
import { disableCommand } from './extensions/disable.js';
|
||||||
|
import { enableCommand } from './extensions/enable.js';
|
||||||
|
|
||||||
export const extensionsCommand: CommandModule = {
|
export const extensionsCommand: CommandModule = {
|
||||||
command: 'extensions <command>',
|
command: 'extensions <command>',
|
||||||
@@ -21,6 +22,7 @@ export const extensionsCommand: CommandModule = {
|
|||||||
.command(listCommand)
|
.command(listCommand)
|
||||||
.command(updateCommand)
|
.command(updateCommand)
|
||||||
.command(disableCommand)
|
.command(disableCommand)
|
||||||
|
.command(enableCommand)
|
||||||
.demandCommand(1, 'You need at least one command before continuing.')
|
.demandCommand(1, 'You need at least one command before continuing.')
|
||||||
.version(false),
|
.version(false),
|
||||||
handler: () => {
|
handler: () => {
|
||||||
|
|||||||
59
packages/cli/src/commands/extensions/enable.ts
Normal file
59
packages/cli/src/commands/extensions/enable.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright 2025 Google LLC
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { type CommandModule } from 'yargs';
|
||||||
|
import { FatalConfigError, getErrorMessage } from '@google/gemini-cli-core';
|
||||||
|
import { enableExtension } from '../../config/extension.js';
|
||||||
|
import { SettingScope } from '../../config/settings.js';
|
||||||
|
|
||||||
|
interface EnableArgs {
|
||||||
|
name: string;
|
||||||
|
scope?: SettingScope;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function handleEnable(args: EnableArgs) {
|
||||||
|
try {
|
||||||
|
const scopes = args.scope
|
||||||
|
? [args.scope]
|
||||||
|
: [SettingScope.User, SettingScope.Workspace];
|
||||||
|
enableExtension(args.name, scopes);
|
||||||
|
if (args.scope) {
|
||||||
|
console.log(
|
||||||
|
`Extension "${args.name}" successfully enabled for scope "${args.scope}".`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
`Extension "${args.name}" successfully enabled in all scopes.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new FatalConfigError(getErrorMessage(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enableCommand: CommandModule = {
|
||||||
|
command: 'disable [--scope] <name>',
|
||||||
|
describe: 'Enables an extension.',
|
||||||
|
builder: (yargs) =>
|
||||||
|
yargs
|
||||||
|
.positional('name', {
|
||||||
|
describe: 'The name of the extension to enable.',
|
||||||
|
type: 'string',
|
||||||
|
})
|
||||||
|
.option('scope', {
|
||||||
|
describe:
|
||||||
|
'The scope to enable the extenison in. If not set, will be enabled in all scopes.',
|
||||||
|
type: 'string',
|
||||||
|
choices: [SettingScope.User, SettingScope.Workspace],
|
||||||
|
})
|
||||||
|
.check((_argv) => true),
|
||||||
|
handler: async (argv) => {
|
||||||
|
await handleEnable({
|
||||||
|
name: argv['name'] as string,
|
||||||
|
scope: argv['scope'] as SettingScope,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -13,12 +13,16 @@ import {
|
|||||||
INSTALL_METADATA_FILENAME,
|
INSTALL_METADATA_FILENAME,
|
||||||
annotateActiveExtensions,
|
annotateActiveExtensions,
|
||||||
disableExtension,
|
disableExtension,
|
||||||
|
enableExtension,
|
||||||
installExtension,
|
installExtension,
|
||||||
loadExtensions,
|
loadExtensions,
|
||||||
uninstallExtension,
|
uninstallExtension,
|
||||||
updateExtension,
|
updateExtension,
|
||||||
} from './extension.js';
|
} from './extension.js';
|
||||||
import { type MCPServerConfig } from '@google/gemini-cli-core';
|
import {
|
||||||
|
type GeminiCLIExtension,
|
||||||
|
type MCPServerConfig,
|
||||||
|
} from '@google/gemini-cli-core';
|
||||||
import { execSync } from 'node:child_process';
|
import { execSync } from 'node:child_process';
|
||||||
import { SettingScope, loadSettings } from './settings.js';
|
import { SettingScope, loadSettings } from './settings.js';
|
||||||
import { type SimpleGit, simpleGit } from 'simple-git';
|
import { type SimpleGit, simpleGit } from 'simple-git';
|
||||||
@@ -576,3 +580,65 @@ describe('disableExtension', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('enableExtension', () => {
|
||||||
|
let tempWorkspaceDir: string;
|
||||||
|
let tempHomeDir: string;
|
||||||
|
let userExtensionsDir: string;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tempWorkspaceDir = fs.mkdtempSync(
|
||||||
|
path.join(os.tmpdir(), 'gemini-cli-test-workspace-'),
|
||||||
|
);
|
||||||
|
tempHomeDir = fs.mkdtempSync(
|
||||||
|
path.join(os.tmpdir(), 'gemini-cli-test-home-'),
|
||||||
|
);
|
||||||
|
userExtensionsDir = path.join(tempHomeDir, '.gemini', 'extensions');
|
||||||
|
vi.mocked(os.homedir).mockReturnValue(tempHomeDir);
|
||||||
|
vi.spyOn(process, 'cwd').mockReturnValue(tempWorkspaceDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
fs.rmSync(tempWorkspaceDir, { recursive: true, force: true });
|
||||||
|
fs.rmSync(tempHomeDir, { recursive: true, force: true });
|
||||||
|
fs.rmSync(userExtensionsDir, { recursive: true, force: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
const getActiveExtensions = (): GeminiCLIExtension[] => {
|
||||||
|
const extensions = loadExtensions(tempWorkspaceDir);
|
||||||
|
const activeExtensions = annotateActiveExtensions(
|
||||||
|
extensions,
|
||||||
|
[],
|
||||||
|
tempWorkspaceDir,
|
||||||
|
);
|
||||||
|
return activeExtensions.filter((e) => e.isActive);
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should enable an extension at the user scope', () => {
|
||||||
|
createExtension(userExtensionsDir, 'ext1', '1.0.0');
|
||||||
|
disableExtension('ext1', SettingScope.User);
|
||||||
|
let activeExtensions = getActiveExtensions();
|
||||||
|
expect(activeExtensions).toHaveLength(0);
|
||||||
|
|
||||||
|
enableExtension('ext1', [SettingScope.User]);
|
||||||
|
activeExtensions = getActiveExtensions();
|
||||||
|
expect(activeExtensions).toHaveLength(1);
|
||||||
|
expect(activeExtensions[0].name).toBe('ext1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should enable an extension at the workspace scope', () => {
|
||||||
|
createExtension(userExtensionsDir, 'ext1', '1.0.0');
|
||||||
|
disableExtension('ext1', SettingScope.Workspace);
|
||||||
|
let activeExtensions = getActiveExtensions();
|
||||||
|
expect(activeExtensions).toHaveLength(0);
|
||||||
|
|
||||||
|
enableExtension('ext1', [SettingScope.Workspace]);
|
||||||
|
activeExtensions = getActiveExtensions();
|
||||||
|
expect(activeExtensions).toHaveLength(1);
|
||||||
|
expect(activeExtensions[0].name).toBe('ext1');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -475,6 +475,10 @@ export function disableExtension(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function enableExtension(name: string, scopes: SettingScope[]) {
|
||||||
|
removeFromDisabledExtensions(name, scopes);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes an extension from the list of disabled extensions.
|
* Removes an extension from the list of disabled extensions.
|
||||||
* @param name The name of the extension to remove.
|
* @param name The name of the extension to remove.
|
||||||
|
|||||||
Reference in New Issue
Block a user