fix(cli): list installed extensions when update target missing (#17082)

This commit is contained in:
Tu Shaokun
2026-01-29 10:13:40 +08:00
committed by GitHub
parent 9bb175a506
commit a91f4e692a
2 changed files with 54 additions and 3 deletions

View File

@@ -24,6 +24,7 @@ import { ExtensionUpdateState } from '../../ui/state/extensions.js';
// Mock dependencies
const emitConsoleLog = vi.hoisted(() => vi.fn());
const emitFeedback = vi.hoisted(() => vi.fn());
const debugLogger = vi.hoisted(() => ({
log: vi.fn((message, ...args) => {
emitConsoleLog('log', format(message, ...args));
@@ -40,6 +41,7 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
...actual,
coreEvents: {
emitConsoleLog,
emitFeedback,
},
debugLogger,
};
@@ -84,6 +86,42 @@ describe('extensions update command', () => {
});
describe('handleUpdate', () => {
it('should list installed extensions when requested extension is not found', async () => {
const mockCwd = vi.spyOn(process, 'cwd').mockReturnValue('/test/dir');
const extensions = [
{ name: 'ext1', version: '1.0.0' },
{ name: 'ext2', version: '2.0.0' },
];
mockExtensionManager.prototype.loadExtensions = vi
.fn()
.mockResolvedValue(extensions);
await handleUpdate({ name: 'missing-extension' });
expect(emitFeedback).toHaveBeenCalledWith(
'error',
'Extension "missing-extension" not found.\n\nInstalled extensions:\next1 (1.0.0)\next2 (2.0.0)\n\nRun "gemini extensions list" for details.',
);
expect(mockUpdateExtension).not.toHaveBeenCalled();
mockCwd.mockRestore();
});
it('should log a helpful message when no extensions are installed and requested extension is not found', async () => {
const mockCwd = vi.spyOn(process, 'cwd').mockReturnValue('/test/dir');
mockExtensionManager.prototype.loadExtensions = vi
.fn()
.mockResolvedValue([]);
await handleUpdate({ name: 'missing-extension' });
expect(emitFeedback).toHaveBeenCalledWith(
'error',
'Extension "missing-extension" not found.\n\nNo extensions installed.',
);
expect(mockUpdateExtension).not.toHaveBeenCalled();
mockCwd.mockRestore();
});
it.each([
{
state: ExtensionUpdateState.UPDATE_AVAILABLE,

View File

@@ -14,7 +14,7 @@ import {
import { checkForExtensionUpdate } from '../../config/extensions/github.js';
import { getErrorMessage } from '../../utils/errors.js';
import { ExtensionUpdateState } from '../../ui/state/extensions.js';
import { debugLogger } from '@google/gemini-cli-core';
import { coreEvents, debugLogger } from '@google/gemini-cli-core';
import { ExtensionManager } from '../../config/extension-manager.js';
import { requestConsentNonInteractive } from '../../config/extensions/consent.js';
import { loadSettings } from '../../config/settings.js';
@@ -46,7 +46,21 @@ export async function handleUpdate(args: UpdateArgs) {
(extension) => extension.name === args.name,
);
if (!extension) {
debugLogger.log(`Extension "${args.name}" not found.`);
if (extensions.length === 0) {
coreEvents.emitFeedback(
'error',
`Extension "${args.name}" not found.\n\nNo extensions installed.`,
);
return;
}
const installedExtensions = extensions
.map((extension) => `${extension.name} (${extension.version})`)
.join('\n');
coreEvents.emitFeedback(
'error',
`Extension "${args.name}" not found.\n\nInstalled extensions:\n${installedExtensions}\n\nRun "gemini extensions list" for details.`,
);
return;
}
if (!extension.installMetadata) {
@@ -63,7 +77,6 @@ export async function handleUpdate(args: UpdateArgs) {
debugLogger.log(`Extension "${args.name}" is already up to date.`);
return;
}
// TODO(chrstnb): we should list extensions if the requested extension is not installed.
const updatedExtensionInfo = (await updateExtension(
extension,
extensionManager,