mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-20 08:47:44 +00:00
prefactor(commands): Command Service Prefactor for Extensible Commands (#4511)
This commit is contained in:
@@ -5,13 +5,14 @@
|
||||
*/
|
||||
|
||||
import { getCliVersion } from '../../utils/version.js';
|
||||
import { SlashCommand } from './types.js';
|
||||
import { CommandKind, SlashCommand } from './types.js';
|
||||
import process from 'node:process';
|
||||
import { MessageType, type HistoryItemAbout } from '../types.js';
|
||||
|
||||
export const aboutCommand: SlashCommand = {
|
||||
name: 'about',
|
||||
description: 'show version info',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context) => {
|
||||
const osVersion = process.platform;
|
||||
let sandboxEnv = 'no sandbox';
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { OpenDialogActionReturn, SlashCommand } from './types.js';
|
||||
import { CommandKind, OpenDialogActionReturn, SlashCommand } from './types.js';
|
||||
|
||||
export const authCommand: SlashCommand = {
|
||||
name: 'auth',
|
||||
description: 'change the auth method',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (_context, _args): OpenDialogActionReturn => ({
|
||||
type: 'dialog',
|
||||
dialog: 'auth',
|
||||
|
||||
@@ -6,7 +6,11 @@
|
||||
|
||||
import open from 'open';
|
||||
import process from 'node:process';
|
||||
import { type CommandContext, type SlashCommand } from './types.js';
|
||||
import {
|
||||
type CommandContext,
|
||||
type SlashCommand,
|
||||
CommandKind,
|
||||
} from './types.js';
|
||||
import { MessageType } from '../types.js';
|
||||
import { GIT_COMMIT_INFO } from '../../generated/git-commit.js';
|
||||
import { formatMemoryUsage } from '../utils/formatters.js';
|
||||
@@ -15,6 +19,7 @@ import { getCliVersion } from '../../utils/version.js';
|
||||
export const bugCommand: SlashCommand = {
|
||||
name: 'bug',
|
||||
description: 'submit a bug report',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context: CommandContext, args?: string): Promise<void> => {
|
||||
const bugDescription = (args || '').trim();
|
||||
const { config } = context.services;
|
||||
|
||||
@@ -5,7 +5,12 @@
|
||||
*/
|
||||
|
||||
import * as fsPromises from 'fs/promises';
|
||||
import { CommandContext, SlashCommand, MessageActionReturn } from './types.js';
|
||||
import {
|
||||
CommandContext,
|
||||
SlashCommand,
|
||||
MessageActionReturn,
|
||||
CommandKind,
|
||||
} from './types.js';
|
||||
import path from 'path';
|
||||
import { HistoryItemWithoutId, MessageType } from '../types.js';
|
||||
|
||||
@@ -54,6 +59,7 @@ const getSavedChatTags = async (
|
||||
const listCommand: SlashCommand = {
|
||||
name: 'list',
|
||||
description: 'List saved conversation checkpoints',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context): Promise<MessageActionReturn> => {
|
||||
const chatDetails = await getSavedChatTags(context, false);
|
||||
if (chatDetails.length === 0) {
|
||||
@@ -81,6 +87,7 @@ const saveCommand: SlashCommand = {
|
||||
name: 'save',
|
||||
description:
|
||||
'Save the current conversation as a checkpoint. Usage: /chat save <tag>',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context, args): Promise<MessageActionReturn> => {
|
||||
const tag = args.trim();
|
||||
if (!tag) {
|
||||
@@ -122,9 +129,10 @@ const saveCommand: SlashCommand = {
|
||||
|
||||
const resumeCommand: SlashCommand = {
|
||||
name: 'resume',
|
||||
altName: 'load',
|
||||
altNames: ['load'],
|
||||
description:
|
||||
'Resume a conversation from a checkpoint. Usage: /chat resume <tag>',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context, args) => {
|
||||
const tag = args.trim();
|
||||
if (!tag) {
|
||||
@@ -193,5 +201,6 @@ const resumeCommand: SlashCommand = {
|
||||
export const chatCommand: SlashCommand = {
|
||||
name: 'chat',
|
||||
description: 'Manage conversation history.',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
subCommands: [listCommand, saveCommand, resumeCommand],
|
||||
};
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
*/
|
||||
|
||||
import { uiTelemetryService } from '@google/gemini-cli-core';
|
||||
import { SlashCommand } from './types.js';
|
||||
import { CommandKind, SlashCommand } from './types.js';
|
||||
|
||||
export const clearCommand: SlashCommand = {
|
||||
name: 'clear',
|
||||
description: 'clear the screen and conversation history',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context, _args) => {
|
||||
const geminiClient = context.services.config?.getGeminiClient();
|
||||
|
||||
|
||||
@@ -5,12 +5,13 @@
|
||||
*/
|
||||
|
||||
import { HistoryItemCompression, MessageType } from '../types.js';
|
||||
import { SlashCommand } from './types.js';
|
||||
import { CommandKind, SlashCommand } from './types.js';
|
||||
|
||||
export const compressCommand: SlashCommand = {
|
||||
name: 'compress',
|
||||
altName: 'summarize',
|
||||
altNames: ['summarize'],
|
||||
description: 'Compresses the context by replacing it with a summary.',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context) => {
|
||||
const { ui } = context;
|
||||
if (ui.pendingItem) {
|
||||
|
||||
@@ -5,11 +5,16 @@
|
||||
*/
|
||||
|
||||
import { copyToClipboard } from '../utils/commandUtils.js';
|
||||
import { SlashCommand, SlashCommandActionReturn } from './types.js';
|
||||
import {
|
||||
CommandKind,
|
||||
SlashCommand,
|
||||
SlashCommandActionReturn,
|
||||
} from './types.js';
|
||||
|
||||
export const copyCommand: SlashCommand = {
|
||||
name: 'copy',
|
||||
description: 'Copy the last result or code snippet to clipboard',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context, _args): Promise<SlashCommandActionReturn | void> => {
|
||||
const chat = await context.services.config?.getGeminiClient()?.getChat();
|
||||
const history = chat?.getHistory();
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { type SlashCommand } from './types.js';
|
||||
import { CommandKind, type SlashCommand } from './types.js';
|
||||
|
||||
export const corgiCommand: SlashCommand = {
|
||||
name: 'corgi',
|
||||
description: 'Toggles corgi mode.',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (context, _args) => {
|
||||
context.ui.toggleCorgiMode();
|
||||
},
|
||||
|
||||
@@ -6,12 +6,17 @@
|
||||
|
||||
import open from 'open';
|
||||
import process from 'node:process';
|
||||
import { type CommandContext, type SlashCommand } from './types.js';
|
||||
import {
|
||||
type CommandContext,
|
||||
type SlashCommand,
|
||||
CommandKind,
|
||||
} from './types.js';
|
||||
import { MessageType } from '../types.js';
|
||||
|
||||
export const docsCommand: SlashCommand = {
|
||||
name: 'docs',
|
||||
description: 'open full Gemini CLI documentation in your browser',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context: CommandContext): Promise<void> => {
|
||||
const docsUrl = 'https://goo.gle/gemini-cli-docs';
|
||||
|
||||
|
||||
@@ -4,11 +4,16 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { type OpenDialogActionReturn, type SlashCommand } from './types.js';
|
||||
import {
|
||||
CommandKind,
|
||||
type OpenDialogActionReturn,
|
||||
type SlashCommand,
|
||||
} from './types.js';
|
||||
|
||||
export const editorCommand: SlashCommand = {
|
||||
name: 'editor',
|
||||
description: 'set external editor preference',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (): OpenDialogActionReturn => ({
|
||||
type: 'dialog',
|
||||
dialog: 'editor',
|
||||
|
||||
@@ -4,12 +4,17 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { type CommandContext, type SlashCommand } from './types.js';
|
||||
import {
|
||||
type CommandContext,
|
||||
type SlashCommand,
|
||||
CommandKind,
|
||||
} from './types.js';
|
||||
import { MessageType } from '../types.js';
|
||||
|
||||
export const extensionsCommand: SlashCommand = {
|
||||
name: 'extensions',
|
||||
description: 'list active extensions',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context: CommandContext): Promise<void> => {
|
||||
const activeExtensions = context.services.config
|
||||
?.getExtensions()
|
||||
|
||||
@@ -32,9 +32,9 @@ describe('helpCommand', () => {
|
||||
});
|
||||
|
||||
it("should also be triggered by its alternative name '?'", () => {
|
||||
// This test is more conceptual. The routing of altName to the command
|
||||
// This test is more conceptual. The routing of altNames to the command
|
||||
// is handled by the slash command processor, but we can assert the
|
||||
// altName is correctly defined on the command object itself.
|
||||
expect(helpCommand.altName).toBe('?');
|
||||
// altNames is correctly defined on the command object itself.
|
||||
expect(helpCommand.altNames).toContain('?');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,12 +4,13 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { OpenDialogActionReturn, SlashCommand } from './types.js';
|
||||
import { CommandKind, OpenDialogActionReturn, SlashCommand } from './types.js';
|
||||
|
||||
export const helpCommand: SlashCommand = {
|
||||
name: 'help',
|
||||
altName: '?',
|
||||
altNames: ['?'],
|
||||
description: 'for help on gemini-cli',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (_context, _args): OpenDialogActionReturn => {
|
||||
console.debug('Opening help UI ...');
|
||||
return {
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
CommandContext,
|
||||
SlashCommand,
|
||||
SlashCommandActionReturn,
|
||||
CommandKind,
|
||||
} from './types.js';
|
||||
import * as child_process from 'child_process';
|
||||
import * as process from 'process';
|
||||
@@ -48,10 +49,12 @@ export const ideCommand = (config: Config | null): SlashCommand | null => {
|
||||
return {
|
||||
name: 'ide',
|
||||
description: 'manage IDE integration',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
subCommands: [
|
||||
{
|
||||
name: 'status',
|
||||
description: 'check status of IDE integration',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (_context: CommandContext): SlashCommandActionReturn => {
|
||||
const status = getMCPServerStatus(IDE_SERVER_NAME);
|
||||
const discoveryState = getMCPDiscoveryState();
|
||||
@@ -89,6 +92,7 @@ export const ideCommand = (config: Config | null): SlashCommand | null => {
|
||||
{
|
||||
name: 'install',
|
||||
description: 'install required VS Code companion extension',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context) => {
|
||||
if (!isVSCodeInstalled()) {
|
||||
context.ui.addItem(
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
SlashCommand,
|
||||
SlashCommandActionReturn,
|
||||
CommandContext,
|
||||
CommandKind,
|
||||
} from './types.js';
|
||||
import {
|
||||
DiscoveredMCPTool,
|
||||
@@ -229,6 +230,7 @@ const getMcpStatus = async (
|
||||
export const mcpCommand: SlashCommand = {
|
||||
name: 'mcp',
|
||||
description: 'list configured MCP servers and tools',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context: CommandContext, args: string) => {
|
||||
const lowerCaseArgs = args.toLowerCase().split(/\s+/).filter(Boolean);
|
||||
|
||||
|
||||
@@ -6,15 +6,21 @@
|
||||
|
||||
import { getErrorMessage } from '@google/gemini-cli-core';
|
||||
import { MessageType } from '../types.js';
|
||||
import { SlashCommand, SlashCommandActionReturn } from './types.js';
|
||||
import {
|
||||
CommandKind,
|
||||
SlashCommand,
|
||||
SlashCommandActionReturn,
|
||||
} from './types.js';
|
||||
|
||||
export const memoryCommand: SlashCommand = {
|
||||
name: 'memory',
|
||||
description: 'Commands for interacting with memory.',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
subCommands: [
|
||||
{
|
||||
name: 'show',
|
||||
description: 'Show the current memory contents.',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context) => {
|
||||
const memoryContent = context.services.config?.getUserMemory() || '';
|
||||
const fileCount = context.services.config?.getGeminiMdFileCount() || 0;
|
||||
@@ -36,6 +42,7 @@ export const memoryCommand: SlashCommand = {
|
||||
{
|
||||
name: 'add',
|
||||
description: 'Add content to the memory.',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (context, args): SlashCommandActionReturn | void => {
|
||||
if (!args || args.trim() === '') {
|
||||
return {
|
||||
@@ -63,6 +70,7 @@ export const memoryCommand: SlashCommand = {
|
||||
{
|
||||
name: 'refresh',
|
||||
description: 'Refresh the memory from the source.',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context) => {
|
||||
context.ui.addItem(
|
||||
{
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { OpenDialogActionReturn, SlashCommand } from './types.js';
|
||||
import { CommandKind, OpenDialogActionReturn, SlashCommand } from './types.js';
|
||||
|
||||
export const privacyCommand: SlashCommand = {
|
||||
name: 'privacy',
|
||||
description: 'display the privacy notice',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (): OpenDialogActionReturn => ({
|
||||
type: 'dialog',
|
||||
dialog: 'privacy',
|
||||
|
||||
@@ -5,12 +5,13 @@
|
||||
*/
|
||||
|
||||
import { formatDuration } from '../utils/formatters.js';
|
||||
import { type SlashCommand } from './types.js';
|
||||
import { CommandKind, type SlashCommand } from './types.js';
|
||||
|
||||
export const quitCommand: SlashCommand = {
|
||||
name: 'quit',
|
||||
altName: 'exit',
|
||||
altNames: ['exit'],
|
||||
description: 'exit the cli',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (context) => {
|
||||
const now = Date.now();
|
||||
const { sessionStartTime } = context.session.stats;
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
type CommandContext,
|
||||
type SlashCommand,
|
||||
type SlashCommandActionReturn,
|
||||
CommandKind,
|
||||
} from './types.js';
|
||||
import { Config } from '@google/gemini-cli-core';
|
||||
|
||||
@@ -149,6 +150,7 @@ export const restoreCommand = (config: Config | null): SlashCommand | null => {
|
||||
name: 'restore',
|
||||
description:
|
||||
'Restore a tool call. This will reset the conversation and file history to the state it was in when the tool call was suggested',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: restoreAction,
|
||||
completion,
|
||||
};
|
||||
|
||||
@@ -6,12 +6,17 @@
|
||||
|
||||
import { MessageType, HistoryItemStats } from '../types.js';
|
||||
import { formatDuration } from '../utils/formatters.js';
|
||||
import { type CommandContext, type SlashCommand } from './types.js';
|
||||
import {
|
||||
type CommandContext,
|
||||
type SlashCommand,
|
||||
CommandKind,
|
||||
} from './types.js';
|
||||
|
||||
export const statsCommand: SlashCommand = {
|
||||
name: 'stats',
|
||||
altName: 'usage',
|
||||
altNames: ['usage'],
|
||||
description: 'check session stats. Usage: /stats [model|tools]',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (context: CommandContext) => {
|
||||
const now = new Date();
|
||||
const { sessionStartTime } = context.session.stats;
|
||||
@@ -38,6 +43,7 @@ export const statsCommand: SlashCommand = {
|
||||
{
|
||||
name: 'model',
|
||||
description: 'Show model-specific usage statistics.',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (context: CommandContext) => {
|
||||
context.ui.addItem(
|
||||
{
|
||||
@@ -50,6 +56,7 @@ export const statsCommand: SlashCommand = {
|
||||
{
|
||||
name: 'tools',
|
||||
description: 'Show tool-specific usage statistics.',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (context: CommandContext) => {
|
||||
context.ui.addItem(
|
||||
{
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { OpenDialogActionReturn, SlashCommand } from './types.js';
|
||||
import { CommandKind, OpenDialogActionReturn, SlashCommand } from './types.js';
|
||||
|
||||
export const themeCommand: SlashCommand = {
|
||||
name: 'theme',
|
||||
description: 'change the theme',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (_context, _args): OpenDialogActionReturn => ({
|
||||
type: 'dialog',
|
||||
dialog: 'theme',
|
||||
|
||||
@@ -4,12 +4,17 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { type CommandContext, type SlashCommand } from './types.js';
|
||||
import {
|
||||
type CommandContext,
|
||||
type SlashCommand,
|
||||
CommandKind,
|
||||
} from './types.js';
|
||||
import { MessageType } from '../types.js';
|
||||
|
||||
export const toolsCommand: SlashCommand = {
|
||||
name: 'tools',
|
||||
description: 'list available Gemini CLI tools',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context: CommandContext, args?: string): Promise<void> => {
|
||||
const subCommand = args?.trim();
|
||||
|
||||
|
||||
@@ -106,11 +106,18 @@ export type SlashCommandActionReturn =
|
||||
| OpenDialogActionReturn
|
||||
| LoadHistoryActionReturn;
|
||||
|
||||
export enum CommandKind {
|
||||
BUILT_IN = 'built-in',
|
||||
FILE = 'file',
|
||||
}
|
||||
|
||||
// The standardized contract for any command in the system.
|
||||
export interface SlashCommand {
|
||||
name: string;
|
||||
altName?: string;
|
||||
description?: string;
|
||||
altNames?: string[];
|
||||
description: string;
|
||||
|
||||
kind: CommandKind;
|
||||
|
||||
// The action to run. Optional for parent commands that only group sub-commands.
|
||||
action?: (
|
||||
|
||||
Reference in New Issue
Block a user