From 0641b1c095b89d750251a60499f8703851bd4fb7 Mon Sep 17 00:00:00 2001 From: christine betts Date: Mon, 25 Aug 2025 18:27:38 +0000 Subject: [PATCH] [extensions] Add extensions list command (#6879) Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- packages/cli/src/commands/extensions.tsx | 2 ++ packages/cli/src/commands/extensions/list.ts | 35 ++++++++++++++++++++ packages/cli/src/config/extension.ts | 27 +++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 packages/cli/src/commands/extensions/list.ts diff --git a/packages/cli/src/commands/extensions.tsx b/packages/cli/src/commands/extensions.tsx index ca86eb27..2cccde9c 100644 --- a/packages/cli/src/commands/extensions.tsx +++ b/packages/cli/src/commands/extensions.tsx @@ -7,6 +7,7 @@ import { CommandModule } from 'yargs'; import { installCommand } from './extensions/install.js'; import { uninstallCommand } from './extensions/uninstall.js'; +import { listCommand } from './extensions/list.js'; export const extensionsCommand: CommandModule = { command: 'extensions ', @@ -15,6 +16,7 @@ export const extensionsCommand: CommandModule = { yargs .command(installCommand) .command(uninstallCommand) + .command(listCommand) .demandCommand(1, 'You need at least one command before continuing.') .version(false), handler: () => { diff --git a/packages/cli/src/commands/extensions/list.ts b/packages/cli/src/commands/extensions/list.ts new file mode 100644 index 00000000..6086587d --- /dev/null +++ b/packages/cli/src/commands/extensions/list.ts @@ -0,0 +1,35 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { CommandModule } from 'yargs'; +import { loadUserExtensions, toOutputString } from '../../config/extension.js'; + +export async function handleList() { + try { + const extensions = loadUserExtensions(); + if (extensions.length === 0) { + console.log('No extensions installed.'); + return; + } + console.log( + extensions + .map((extension, _): string => toOutputString(extension)) + .join('\n\n'), + ); + } catch (error) { + console.error((error as Error).message); + process.exit(1); + } +} + +export const listCommand: CommandModule = { + command: 'list', + describe: 'Lists installed extensions.', + builder: (yargs) => yargs, + handler: async () => { + await handleList(); + }, +}; diff --git a/packages/cli/src/config/extension.ts b/packages/cli/src/config/extension.ts index d2c366f5..a0e50d35 100644 --- a/packages/cli/src/config/extension.ts +++ b/packages/cli/src/config/extension.ts @@ -354,3 +354,30 @@ export async function uninstallExtension(extensionName: string): Promise { force: true, }); } + +export function toOutputString(extension: Extension): string { + let output = `${extension.config.name} (${extension.config.version})`; + output += `\n Path: ${extension.path}`; + if (extension.installMetadata) { + output += `\n Source: ${extension.installMetadata.source}`; + } + if (extension.contextFiles.length > 0) { + output += `\n Context files:`; + extension.contextFiles.forEach((contextFile) => { + output += `\n ${contextFile}`; + }); + } + if (extension.config.mcpServers) { + output += `\n MCP servers:`; + Object.keys(extension.config.mcpServers).forEach((key) => { + output += `\n ${key}`; + }); + } + if (extension.config.excludeTools) { + output += `\n Excluded tools:`; + extension.config.excludeTools.forEach((tool) => { + output += `\n ${tool}`; + }); + } + return output; +}