mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-21 01:07:46 +00:00
feat(i18n): Add Internationalization Support for UI and LLM Output (#1058)
This commit is contained in:
@@ -8,10 +8,13 @@ import type { SlashCommand } from './types.js';
|
||||
import { CommandKind } from './types.js';
|
||||
import { MessageType, type HistoryItemAbout } from '../types.js';
|
||||
import { getExtendedSystemInfo } from '../../utils/systemInfo.js';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
export const aboutCommand: SlashCommand = {
|
||||
name: 'about',
|
||||
description: 'show version info',
|
||||
get description() {
|
||||
return t('show version info');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context) => {
|
||||
const systemInfo = await getExtendedSystemInfo(context);
|
||||
|
||||
@@ -9,15 +9,20 @@ import {
|
||||
type SlashCommand,
|
||||
type OpenDialogActionReturn,
|
||||
} from './types.js';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
export const agentsCommand: SlashCommand = {
|
||||
name: 'agents',
|
||||
description: 'Manage subagents for specialized task delegation.',
|
||||
get description() {
|
||||
return t('Manage subagents for specialized task delegation.');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
subCommands: [
|
||||
{
|
||||
name: 'manage',
|
||||
description: 'Manage existing subagents (view, edit, delete).',
|
||||
get description() {
|
||||
return t('Manage existing subagents (view, edit, delete).');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (): OpenDialogActionReturn => ({
|
||||
type: 'dialog',
|
||||
@@ -26,7 +31,9 @@ export const agentsCommand: SlashCommand = {
|
||||
},
|
||||
{
|
||||
name: 'create',
|
||||
description: 'Create a new subagent with guided setup.',
|
||||
get description() {
|
||||
return t('Create a new subagent with guided setup.');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (): OpenDialogActionReturn => ({
|
||||
type: 'dialog',
|
||||
|
||||
@@ -10,10 +10,13 @@ import type {
|
||||
OpenDialogActionReturn,
|
||||
} from './types.js';
|
||||
import { CommandKind } from './types.js';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
export const approvalModeCommand: SlashCommand = {
|
||||
name: 'approval-mode',
|
||||
description: 'View or change the approval mode for tool usage',
|
||||
get description() {
|
||||
return t('View or change the approval mode for tool usage');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (
|
||||
_context: CommandContext,
|
||||
|
||||
@@ -6,10 +6,13 @@
|
||||
|
||||
import type { OpenDialogActionReturn, SlashCommand } from './types.js';
|
||||
import { CommandKind } from './types.js';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
export const authCommand: SlashCommand = {
|
||||
name: 'auth',
|
||||
description: 'change the auth method',
|
||||
get description() {
|
||||
return t('change the auth method');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (_context, _args): OpenDialogActionReturn => ({
|
||||
type: 'dialog',
|
||||
|
||||
@@ -16,10 +16,13 @@ import {
|
||||
getSystemInfoFields,
|
||||
getFieldValue,
|
||||
} from '../../utils/systemInfoFields.js';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
export const bugCommand: SlashCommand = {
|
||||
name: 'bug',
|
||||
description: 'submit a bug report',
|
||||
get description() {
|
||||
return t('submit a bug report');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context: CommandContext, args?: string): Promise<void> => {
|
||||
const bugDescription = (args || '').trim();
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
import * as fsPromises from 'node:fs/promises';
|
||||
import React from 'react';
|
||||
import { Text } from 'ink';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import type {
|
||||
CommandContext,
|
||||
SlashCommand,
|
||||
@@ -20,6 +19,7 @@ import path from 'node:path';
|
||||
import type { HistoryItemWithoutId } from '../types.js';
|
||||
import { MessageType } from '../types.js';
|
||||
import type { Content } from '@google/genai';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
interface ChatDetail {
|
||||
name: string;
|
||||
@@ -67,7 +67,9 @@ const getSavedChatTags = async (
|
||||
|
||||
const listCommand: SlashCommand = {
|
||||
name: 'list',
|
||||
description: 'List saved conversation checkpoints',
|
||||
get description() {
|
||||
return t('List saved conversation checkpoints');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context): Promise<MessageActionReturn> => {
|
||||
const chatDetails = await getSavedChatTags(context, false);
|
||||
@@ -75,7 +77,7 @@ const listCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: 'No saved conversation checkpoints found.',
|
||||
content: t('No saved conversation checkpoints found.'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -83,7 +85,7 @@ const listCommand: SlashCommand = {
|
||||
...chatDetails.map((chat) => chat.name.length),
|
||||
);
|
||||
|
||||
let message = 'List of saved conversations:\n\n';
|
||||
let message = t('List of saved conversations:') + '\n\n';
|
||||
for (const chat of chatDetails) {
|
||||
const paddedName = chat.name.padEnd(maxNameLength, ' ');
|
||||
const isoString = chat.mtime.toISOString();
|
||||
@@ -91,7 +93,7 @@ const listCommand: SlashCommand = {
|
||||
const formattedDate = match ? `${match[1]} ${match[2]}` : 'Invalid Date';
|
||||
message += ` - ${paddedName} (saved on ${formattedDate})\n`;
|
||||
}
|
||||
message += `\nNote: Newest last, oldest first`;
|
||||
message += `\n${t('Note: Newest last, oldest first')}`;
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
@@ -102,8 +104,11 @@ const listCommand: SlashCommand = {
|
||||
|
||||
const saveCommand: SlashCommand = {
|
||||
name: 'save',
|
||||
description:
|
||||
'Save the current conversation as a checkpoint. Usage: /chat save <tag>',
|
||||
get description() {
|
||||
return t(
|
||||
'Save the current conversation as a checkpoint. Usage: /chat save <tag>',
|
||||
);
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context, args): Promise<SlashCommandActionReturn | void> => {
|
||||
const tag = args.trim();
|
||||
@@ -111,7 +116,7 @@ const saveCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'Missing tag. Usage: /chat save <tag>',
|
||||
content: t('Missing tag. Usage: /chat save <tag>'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -126,9 +131,12 @@ const saveCommand: SlashCommand = {
|
||||
prompt: React.createElement(
|
||||
Text,
|
||||
null,
|
||||
'A checkpoint with the tag ',
|
||||
React.createElement(Text, { color: theme.text.accent }, tag),
|
||||
' already exists. Do you want to overwrite it?',
|
||||
t(
|
||||
'A checkpoint with the tag {{tag}} already exists. Do you want to overwrite it?',
|
||||
{
|
||||
tag,
|
||||
},
|
||||
),
|
||||
),
|
||||
originalInvocation: {
|
||||
raw: context.invocation?.raw || `/chat save ${tag}`,
|
||||
@@ -142,7 +150,7 @@ const saveCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'No chat client available to save conversation.',
|
||||
content: t('No chat client available to save conversation.'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -152,13 +160,15 @@ const saveCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: `Conversation checkpoint saved with tag: ${decodeTagName(tag)}.`,
|
||||
content: t('Conversation checkpoint saved with tag: {{tag}}.', {
|
||||
tag: decodeTagName(tag),
|
||||
}),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: 'No conversation found to save.',
|
||||
content: t('No conversation found to save.'),
|
||||
};
|
||||
}
|
||||
},
|
||||
@@ -167,8 +177,11 @@ const saveCommand: SlashCommand = {
|
||||
const resumeCommand: SlashCommand = {
|
||||
name: 'resume',
|
||||
altNames: ['load'],
|
||||
description:
|
||||
'Resume a conversation from a checkpoint. Usage: /chat resume <tag>',
|
||||
get description() {
|
||||
return t(
|
||||
'Resume a conversation from a checkpoint. Usage: /chat resume <tag>',
|
||||
);
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context, args) => {
|
||||
const tag = args.trim();
|
||||
@@ -176,7 +189,7 @@ const resumeCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'Missing tag. Usage: /chat resume <tag>',
|
||||
content: t('Missing tag. Usage: /chat resume <tag>'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -188,7 +201,9 @@ const resumeCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: `No saved checkpoint found with tag: ${decodeTagName(tag)}.`,
|
||||
content: t('No saved checkpoint found with tag: {{tag}}.', {
|
||||
tag: decodeTagName(tag),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -237,7 +252,9 @@ const resumeCommand: SlashCommand = {
|
||||
|
||||
const deleteCommand: SlashCommand = {
|
||||
name: 'delete',
|
||||
description: 'Delete a conversation checkpoint. Usage: /chat delete <tag>',
|
||||
get description() {
|
||||
return t('Delete a conversation checkpoint. Usage: /chat delete <tag>');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context, args): Promise<MessageActionReturn> => {
|
||||
const tag = args.trim();
|
||||
@@ -245,7 +262,7 @@ const deleteCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'Missing tag. Usage: /chat delete <tag>',
|
||||
content: t('Missing tag. Usage: /chat delete <tag>'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -257,13 +274,17 @@ const deleteCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: `Conversation checkpoint '${decodeTagName(tag)}' has been deleted.`,
|
||||
content: t("Conversation checkpoint '{{tag}}' has been deleted.", {
|
||||
tag: decodeTagName(tag),
|
||||
}),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: `Error: No checkpoint found with tag '${decodeTagName(tag)}'.`,
|
||||
content: t("Error: No checkpoint found with tag '{{tag}}'.", {
|
||||
tag: decodeTagName(tag),
|
||||
}),
|
||||
};
|
||||
}
|
||||
},
|
||||
@@ -309,8 +330,11 @@ export function serializeHistoryToMarkdown(history: Content[]): string {
|
||||
|
||||
const shareCommand: SlashCommand = {
|
||||
name: 'share',
|
||||
description:
|
||||
'Share the current conversation to a markdown or json file. Usage: /chat share <file>',
|
||||
get description() {
|
||||
return t(
|
||||
'Share the current conversation to a markdown or json file. Usage: /chat share <file>',
|
||||
);
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context, args): Promise<MessageActionReturn> => {
|
||||
let filePathArg = args.trim();
|
||||
@@ -324,7 +348,7 @@ const shareCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'Invalid file format. Only .md and .json are supported.',
|
||||
content: t('Invalid file format. Only .md and .json are supported.'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -333,7 +357,7 @@ const shareCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'No chat client available to share conversation.',
|
||||
content: t('No chat client available to share conversation.'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -346,7 +370,7 @@ const shareCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: 'No conversation found to share.',
|
||||
content: t('No conversation found to share.'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -362,14 +386,18 @@ const shareCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: `Conversation shared to ${filePath}`,
|
||||
content: t('Conversation shared to {{filePath}}', {
|
||||
filePath,
|
||||
}),
|
||||
};
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : String(err);
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: `Error sharing conversation: ${errorMessage}`,
|
||||
content: t('Error sharing conversation: {{error}}', {
|
||||
error: errorMessage,
|
||||
}),
|
||||
};
|
||||
}
|
||||
},
|
||||
@@ -377,7 +405,9 @@ const shareCommand: SlashCommand = {
|
||||
|
||||
export const chatCommand: SlashCommand = {
|
||||
name: 'chat',
|
||||
description: 'Manage conversation history.',
|
||||
get description() {
|
||||
return t('Manage conversation history.');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
subCommands: [
|
||||
listCommand,
|
||||
|
||||
@@ -7,21 +7,24 @@
|
||||
import { uiTelemetryService } from '@qwen-code/qwen-code-core';
|
||||
import type { SlashCommand } from './types.js';
|
||||
import { CommandKind } from './types.js';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
export const clearCommand: SlashCommand = {
|
||||
name: 'clear',
|
||||
description: 'clear the screen and conversation history',
|
||||
get description() {
|
||||
return t('clear the screen and conversation history');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context, _args) => {
|
||||
const geminiClient = context.services.config?.getGeminiClient();
|
||||
|
||||
if (geminiClient) {
|
||||
context.ui.setDebugMessage('Clearing terminal and resetting chat.');
|
||||
context.ui.setDebugMessage(t('Clearing terminal and resetting chat.'));
|
||||
// If resetChat fails, the exception will propagate and halt the command,
|
||||
// which is the correct behavior to signal a failure to the user.
|
||||
await geminiClient.resetChat();
|
||||
} else {
|
||||
context.ui.setDebugMessage('Clearing terminal.');
|
||||
context.ui.setDebugMessage(t('Clearing terminal.'));
|
||||
}
|
||||
|
||||
uiTelemetryService.setLastPromptTokenCount(0);
|
||||
|
||||
@@ -8,11 +8,14 @@ import type { HistoryItemCompression } from '../types.js';
|
||||
import { MessageType } from '../types.js';
|
||||
import type { SlashCommand } from './types.js';
|
||||
import { CommandKind } from './types.js';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
export const compressCommand: SlashCommand = {
|
||||
name: 'compress',
|
||||
altNames: ['summarize'],
|
||||
description: 'Compresses the context by replacing it with a summary.',
|
||||
get description() {
|
||||
return t('Compresses the context by replacing it with a summary.');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context) => {
|
||||
const { ui } = context;
|
||||
@@ -20,7 +23,7 @@ export const compressCommand: SlashCommand = {
|
||||
ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Already compressing, wait for previous request to complete',
|
||||
text: t('Already compressing, wait for previous request to complete'),
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
@@ -60,7 +63,7 @@ export const compressCommand: SlashCommand = {
|
||||
ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Failed to compress chat history.',
|
||||
text: t('Failed to compress chat history.'),
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
@@ -69,9 +72,9 @@ export const compressCommand: SlashCommand = {
|
||||
ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: `Failed to compress chat history: ${
|
||||
e instanceof Error ? e.message : String(e)
|
||||
}`,
|
||||
text: t('Failed to compress chat history: {{error}}', {
|
||||
error: e instanceof Error ? e.message : String(e),
|
||||
}),
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
|
||||
@@ -7,10 +7,13 @@
|
||||
import { copyToClipboard } from '../utils/commandUtils.js';
|
||||
import type { SlashCommand, SlashCommandActionReturn } from './types.js';
|
||||
import { CommandKind } from './types.js';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
export const copyCommand: SlashCommand = {
|
||||
name: 'copy',
|
||||
description: 'Copy the last result or code snippet to clipboard',
|
||||
get description() {
|
||||
return t('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();
|
||||
|
||||
@@ -10,6 +10,7 @@ import { MessageType } from '../types.js';
|
||||
import * as os from 'node:os';
|
||||
import * as path from 'node:path';
|
||||
import { loadServerHierarchicalMemory } from '@qwen-code/qwen-code-core';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
export function expandHomeDir(p: string): string {
|
||||
if (!p) {
|
||||
@@ -27,13 +28,18 @@ export function expandHomeDir(p: string): string {
|
||||
export const directoryCommand: SlashCommand = {
|
||||
name: 'directory',
|
||||
altNames: ['dir'],
|
||||
description: 'Manage workspace directories',
|
||||
get description() {
|
||||
return t('Manage workspace directories');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
subCommands: [
|
||||
{
|
||||
name: 'add',
|
||||
description:
|
||||
'Add directories to the workspace. Use comma to separate multiple paths',
|
||||
get description() {
|
||||
return t(
|
||||
'Add directories to the workspace. Use comma to separate multiple paths',
|
||||
);
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context: CommandContext, args: string) => {
|
||||
const {
|
||||
@@ -46,7 +52,7 @@ export const directoryCommand: SlashCommand = {
|
||||
addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Configuration is not available.',
|
||||
text: t('Configuration is not available.'),
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
@@ -63,7 +69,7 @@ export const directoryCommand: SlashCommand = {
|
||||
addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Please provide at least one path to add.',
|
||||
text: t('Please provide at least one path to add.'),
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
@@ -74,8 +80,9 @@ export const directoryCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message' as const,
|
||||
messageType: 'error' as const,
|
||||
content:
|
||||
content: t(
|
||||
'The /directory add command is not supported in restrictive sandbox profiles. Please use --include-directories when starting the session instead.',
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -88,7 +95,12 @@ export const directoryCommand: SlashCommand = {
|
||||
added.push(pathToAdd.trim());
|
||||
} catch (e) {
|
||||
const error = e as Error;
|
||||
errors.push(`Error adding '${pathToAdd.trim()}': ${error.message}`);
|
||||
errors.push(
|
||||
t("Error adding '{{path}}': {{error}}", {
|
||||
path: pathToAdd.trim(),
|
||||
error: error.message,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,12 +129,21 @@ export const directoryCommand: SlashCommand = {
|
||||
addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Successfully added GEMINI.md files from the following directories if there are:\n- ${added.join('\n- ')}`,
|
||||
text: t(
|
||||
'Successfully added GEMINI.md files from the following directories if there are:\n- {{directories}}',
|
||||
{
|
||||
directories: added.join('\n- '),
|
||||
},
|
||||
),
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
} catch (error) {
|
||||
errors.push(`Error refreshing memory: ${(error as Error).message}`);
|
||||
errors.push(
|
||||
t('Error refreshing memory: {{error}}', {
|
||||
error: (error as Error).message,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (added.length > 0) {
|
||||
@@ -133,7 +154,9 @@ export const directoryCommand: SlashCommand = {
|
||||
addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Successfully added directories:\n- ${added.join('\n- ')}`,
|
||||
text: t('Successfully added directories:\n- {{directories}}', {
|
||||
directories: added.join('\n- '),
|
||||
}),
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
@@ -150,7 +173,9 @@ export const directoryCommand: SlashCommand = {
|
||||
},
|
||||
{
|
||||
name: 'show',
|
||||
description: 'Show all directories in the workspace',
|
||||
get description() {
|
||||
return t('Show all directories in the workspace');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context: CommandContext) => {
|
||||
const {
|
||||
@@ -161,7 +186,7 @@ export const directoryCommand: SlashCommand = {
|
||||
addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Configuration is not available.',
|
||||
text: t('Configuration is not available.'),
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
@@ -173,7 +198,9 @@ export const directoryCommand: SlashCommand = {
|
||||
addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Current workspace directories:\n${directoryList}`,
|
||||
text: t('Current workspace directories:\n{{directories}}', {
|
||||
directories: directoryList,
|
||||
}),
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
|
||||
@@ -12,19 +12,28 @@ import {
|
||||
CommandKind,
|
||||
} from './types.js';
|
||||
import { MessageType } from '../types.js';
|
||||
import { t, getCurrentLanguage } from '../../i18n/index.js';
|
||||
|
||||
export const docsCommand: SlashCommand = {
|
||||
name: 'docs',
|
||||
description: 'open full Qwen Code documentation in your browser',
|
||||
get description() {
|
||||
return t('open full Qwen Code documentation in your browser');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context: CommandContext): Promise<void> => {
|
||||
const docsUrl = 'https://qwenlm.github.io/qwen-code-docs/en';
|
||||
const langPath = getCurrentLanguage()?.startsWith('zh') ? 'zh' : 'en';
|
||||
const docsUrl = `https://qwenlm.github.io/qwen-code-docs/${langPath}`;
|
||||
|
||||
if (process.env['SANDBOX'] && process.env['SANDBOX'] !== 'sandbox-exec') {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Please open the following URL in your browser to view the documentation:\n${docsUrl}`,
|
||||
text: t(
|
||||
'Please open the following URL in your browser to view the documentation:\n{{url}}',
|
||||
{
|
||||
url: docsUrl,
|
||||
},
|
||||
),
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
@@ -32,7 +41,9 @@ export const docsCommand: SlashCommand = {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Opening documentation in your browser: ${docsUrl}`,
|
||||
text: t('Opening documentation in your browser: {{url}}', {
|
||||
url: docsUrl,
|
||||
}),
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
|
||||
@@ -9,10 +9,13 @@ import {
|
||||
type OpenDialogActionReturn,
|
||||
type SlashCommand,
|
||||
} from './types.js';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
export const editorCommand: SlashCommand = {
|
||||
name: 'editor',
|
||||
description: 'set external editor preference',
|
||||
get description() {
|
||||
return t('set external editor preference');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (): OpenDialogActionReturn => ({
|
||||
type: 'dialog',
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
type SlashCommand,
|
||||
CommandKind,
|
||||
} from './types.js';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
async function listAction(context: CommandContext) {
|
||||
context.ui.addItem(
|
||||
@@ -131,14 +132,18 @@ async function updateAction(context: CommandContext, args: string) {
|
||||
|
||||
const listExtensionsCommand: SlashCommand = {
|
||||
name: 'list',
|
||||
description: 'List active extensions',
|
||||
get description() {
|
||||
return t('List active extensions');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: listAction,
|
||||
};
|
||||
|
||||
const updateExtensionsCommand: SlashCommand = {
|
||||
name: 'update',
|
||||
description: 'Update extensions. Usage: update <extension-names>|--all',
|
||||
get description() {
|
||||
return t('Update extensions. Usage: update <extension-names>|--all');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: updateAction,
|
||||
completion: async (context, partialArg) => {
|
||||
@@ -158,7 +163,9 @@ const updateExtensionsCommand: SlashCommand = {
|
||||
|
||||
export const extensionsCommand: SlashCommand = {
|
||||
name: 'extensions',
|
||||
description: 'Manage extensions',
|
||||
get description() {
|
||||
return t('Manage extensions');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
subCommands: [listExtensionsCommand, updateExtensionsCommand],
|
||||
action: (context, args) =>
|
||||
|
||||
@@ -7,12 +7,15 @@
|
||||
import type { SlashCommand } from './types.js';
|
||||
import { CommandKind } from './types.js';
|
||||
import { MessageType, type HistoryItemHelp } from '../types.js';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
export const helpCommand: SlashCommand = {
|
||||
name: 'help',
|
||||
altNames: ['?'],
|
||||
kind: CommandKind.BUILT_IN,
|
||||
description: 'for help on Qwen Code',
|
||||
get description() {
|
||||
return t('for help on Qwen Code');
|
||||
},
|
||||
action: async (context) => {
|
||||
const helpItem: Omit<HistoryItemHelp, 'id'> = {
|
||||
type: MessageType.HELP,
|
||||
|
||||
@@ -26,6 +26,7 @@ import type {
|
||||
} from './types.js';
|
||||
import { CommandKind } from './types.js';
|
||||
import { SettingScope } from '../../config/settings.js';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
function getIdeStatusMessage(ideClient: IdeClient): {
|
||||
messageType: 'info' | 'error';
|
||||
@@ -138,27 +139,35 @@ export const ideCommand = async (): Promise<SlashCommand> => {
|
||||
if (!currentIDE) {
|
||||
return {
|
||||
name: 'ide',
|
||||
description: 'manage IDE integration',
|
||||
get description() {
|
||||
return t('manage IDE integration');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (): SlashCommandActionReturn =>
|
||||
({
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: `IDE integration is not supported in your current environment. To use this feature, run Qwen Code in one of these supported IDEs: VS Code or VS Code forks.`,
|
||||
content: t(
|
||||
'IDE integration is not supported in your current environment. To use this feature, run Qwen Code in one of these supported IDEs: VS Code or VS Code forks.',
|
||||
),
|
||||
}) as const,
|
||||
};
|
||||
}
|
||||
|
||||
const ideSlashCommand: SlashCommand = {
|
||||
name: 'ide',
|
||||
description: 'manage IDE integration',
|
||||
get description() {
|
||||
return t('manage IDE integration');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
subCommands: [],
|
||||
};
|
||||
|
||||
const statusCommand: SlashCommand = {
|
||||
name: 'status',
|
||||
description: 'check status of IDE integration',
|
||||
get description() {
|
||||
return t('check status of IDE integration');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (): Promise<SlashCommandActionReturn> => {
|
||||
const { messageType, content } =
|
||||
@@ -173,7 +182,12 @@ export const ideCommand = async (): Promise<SlashCommand> => {
|
||||
|
||||
const installCommand: SlashCommand = {
|
||||
name: 'install',
|
||||
description: `install required IDE companion for ${ideClient.getDetectedIdeDisplayName()}`,
|
||||
get description() {
|
||||
const ideName = ideClient.getDetectedIdeDisplayName() ?? 'IDE';
|
||||
return t('install required IDE companion for {{ideName}}', {
|
||||
ideName,
|
||||
});
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context) => {
|
||||
const installer = getIdeInstaller(currentIDE);
|
||||
@@ -246,7 +260,9 @@ export const ideCommand = async (): Promise<SlashCommand> => {
|
||||
|
||||
const enableCommand: SlashCommand = {
|
||||
name: 'enable',
|
||||
description: 'enable IDE integration',
|
||||
get description() {
|
||||
return t('enable IDE integration');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context: CommandContext) => {
|
||||
context.services.settings.setValue(
|
||||
@@ -268,7 +284,9 @@ export const ideCommand = async (): Promise<SlashCommand> => {
|
||||
|
||||
const disableCommand: SlashCommand = {
|
||||
name: 'disable',
|
||||
description: 'disable IDE integration',
|
||||
get description() {
|
||||
return t('disable IDE integration');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context: CommandContext) => {
|
||||
context.services.settings.setValue(
|
||||
|
||||
@@ -15,10 +15,13 @@ import { getCurrentGeminiMdFilename } from '@qwen-code/qwen-code-core';
|
||||
import { CommandKind } from './types.js';
|
||||
import { Text } from 'ink';
|
||||
import React from 'react';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
export const initCommand: SlashCommand = {
|
||||
name: 'init',
|
||||
description: 'Analyzes the project and creates a tailored QWEN.md file.',
|
||||
get description() {
|
||||
return t('Analyzes the project and creates a tailored QWEN.md file.');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (
|
||||
context: CommandContext,
|
||||
@@ -28,7 +31,7 @@ export const initCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'Configuration not available.',
|
||||
content: t('Configuration not available.'),
|
||||
};
|
||||
}
|
||||
const targetDir = context.services.config.getTargetDir();
|
||||
|
||||
458
packages/cli/src/ui/commands/languageCommand.ts
Normal file
458
packages/cli/src/ui/commands/languageCommand.ts
Normal file
@@ -0,0 +1,458 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import type {
|
||||
SlashCommand,
|
||||
CommandContext,
|
||||
SlashCommandActionReturn,
|
||||
MessageActionReturn,
|
||||
} from './types.js';
|
||||
import { CommandKind } from './types.js';
|
||||
import { SettingScope } from '../../config/settings.js';
|
||||
import {
|
||||
setLanguageAsync,
|
||||
getCurrentLanguage,
|
||||
type SupportedLanguage,
|
||||
t,
|
||||
} from '../../i18n/index.js';
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import { Storage } from '@qwen-code/qwen-code-core';
|
||||
|
||||
const LLM_OUTPUT_LANGUAGE_RULE_FILENAME = 'output-language.md';
|
||||
|
||||
/**
|
||||
* Generates the LLM output language rule template based on the language name.
|
||||
*/
|
||||
function generateLlmOutputLanguageRule(language: string): string {
|
||||
return `# ⚠️ CRITICAL: ${language} Output Language Rule - HIGHEST PRIORITY ⚠️
|
||||
|
||||
## 🚨 MANDATORY RULE - NO EXCEPTIONS 🚨
|
||||
|
||||
**YOU MUST RESPOND IN ${language.toUpperCase()} FOR EVERY SINGLE OUTPUT, REGARDLESS OF THE USER'S INPUT LANGUAGE.**
|
||||
|
||||
This is a **NON-NEGOTIABLE** requirement. Even if the user writes in English, says "hi", asks a simple question, or explicitly requests another language, **YOU MUST ALWAYS RESPOND IN ${language.toUpperCase()}.**
|
||||
|
||||
## What Must Be in ${language}
|
||||
|
||||
**EVERYTHING** you output: conversation replies, tool call descriptions, success/error messages, generated file content (comments, documentation), and all explanatory text.
|
||||
|
||||
**Tool outputs**: All descriptive text from \`read_file\`, \`write_file\`, \`codebase_search\`, \`run_terminal_cmd\`, \`todo_write\`, \`web_search\`, etc. MUST be in ${language}.
|
||||
|
||||
## Examples
|
||||
|
||||
### ✅ CORRECT:
|
||||
- User says "hi" → Respond in ${language} (e.g., "Bonjour" if ${language} is French)
|
||||
- Tool result → "已成功读取文件 config.json" (if ${language} is Chinese)
|
||||
- Error → "无法找到指定的文件" (if ${language} is Chinese)
|
||||
|
||||
### ❌ WRONG:
|
||||
- User says "hi" → "Hello" in English
|
||||
- Tool result → "Successfully read file" in English
|
||||
- Error → "File not found" in English
|
||||
|
||||
## Notes
|
||||
|
||||
- Code elements (variable/function names, syntax) can remain in English
|
||||
- Comments, documentation, and all other text MUST be in ${language}
|
||||
|
||||
**THIS RULE IS ACTIVE NOW. ALL OUTPUTS MUST BE IN ${language.toUpperCase()}. NO EXCEPTIONS.**
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the path to the LLM output language rule file.
|
||||
*/
|
||||
function getLlmOutputLanguageRulePath(): string {
|
||||
return path.join(
|
||||
Storage.getGlobalQwenDir(),
|
||||
LLM_OUTPUT_LANGUAGE_RULE_FILENAME,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current LLM output language from the rule file if it exists.
|
||||
*/
|
||||
function getCurrentLlmOutputLanguage(): string | null {
|
||||
const filePath = getLlmOutputLanguageRulePath();
|
||||
if (fs.existsSync(filePath)) {
|
||||
try {
|
||||
const content = fs.readFileSync(filePath, 'utf-8');
|
||||
// Extract language name from the first line (e.g., "# Chinese Response Rules" -> "Chinese")
|
||||
const match = content.match(/^#\s+(.+?)\s+Response Rules/i);
|
||||
if (match) {
|
||||
return match[1];
|
||||
}
|
||||
} catch {
|
||||
// Ignore errors
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the UI language and persists it to settings.
|
||||
*/
|
||||
async function setUiLanguage(
|
||||
context: CommandContext,
|
||||
lang: SupportedLanguage,
|
||||
): Promise<MessageActionReturn> {
|
||||
const { services } = context;
|
||||
const { settings } = services;
|
||||
|
||||
if (!services.config) {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: t('Configuration not available.'),
|
||||
};
|
||||
}
|
||||
|
||||
// Set language in i18n system (async to support JS translation files)
|
||||
await setLanguageAsync(lang);
|
||||
|
||||
// Persist to settings (user scope)
|
||||
if (settings && typeof settings.setValue === 'function') {
|
||||
try {
|
||||
settings.setValue(SettingScope.User, 'general.language', lang);
|
||||
} catch (error) {
|
||||
console.warn('Failed to save language setting:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Reload commands to update their descriptions with the new language
|
||||
context.ui.reloadCommands();
|
||||
|
||||
// Map language codes to friendly display names
|
||||
const langDisplayNames: Record<SupportedLanguage, string> = {
|
||||
zh: '中文(zh-CN)',
|
||||
en: 'English(en-US)',
|
||||
};
|
||||
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: t('UI language changed to {{lang}}', {
|
||||
lang: langDisplayNames[lang],
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the LLM output language rule file.
|
||||
*/
|
||||
function generateLlmOutputLanguageRuleFile(
|
||||
language: string,
|
||||
): Promise<MessageActionReturn> {
|
||||
try {
|
||||
const filePath = getLlmOutputLanguageRulePath();
|
||||
const content = generateLlmOutputLanguageRule(language);
|
||||
|
||||
// Ensure directory exists
|
||||
const dir = path.dirname(filePath);
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
|
||||
// Write file (overwrite if exists)
|
||||
fs.writeFileSync(filePath, content, 'utf-8');
|
||||
|
||||
return Promise.resolve({
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: [
|
||||
t('LLM output language rule file generated at {{path}}', {
|
||||
path: filePath,
|
||||
}),
|
||||
'',
|
||||
t('Please restart the application for the changes to take effect.'),
|
||||
].join('\n'),
|
||||
});
|
||||
} catch (error) {
|
||||
return Promise.resolve({
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: t(
|
||||
'Failed to generate LLM output language rule file: {{error}}',
|
||||
{
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
},
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const languageCommand: SlashCommand = {
|
||||
name: 'language',
|
||||
get description() {
|
||||
return t('View or change the language setting');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (
|
||||
context: CommandContext,
|
||||
args: string,
|
||||
): Promise<SlashCommandActionReturn> => {
|
||||
const { services } = context;
|
||||
|
||||
if (!services.config) {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: t('Configuration not available.'),
|
||||
};
|
||||
}
|
||||
|
||||
const trimmedArgs = args.trim();
|
||||
|
||||
// If no arguments, show current language settings and usage
|
||||
if (!trimmedArgs) {
|
||||
const currentUiLang = getCurrentLanguage();
|
||||
const currentLlmLang = getCurrentLlmOutputLanguage();
|
||||
const message = [
|
||||
t('Current UI language: {{lang}}', { lang: currentUiLang }),
|
||||
currentLlmLang
|
||||
? t('Current LLM output language: {{lang}}', { lang: currentLlmLang })
|
||||
: t('LLM output language not set'),
|
||||
'',
|
||||
t('Available subcommands:'),
|
||||
` /language ui [zh-CN|en-US] - ${t('Set UI language')}`,
|
||||
` /language output <language> - ${t('Set LLM output language')}`,
|
||||
].join('\n');
|
||||
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: message,
|
||||
};
|
||||
}
|
||||
|
||||
// Parse subcommand
|
||||
const parts = trimmedArgs.split(/\s+/);
|
||||
const subcommand = parts[0].toLowerCase();
|
||||
|
||||
if (subcommand === 'ui') {
|
||||
// Handle /language ui [zh-CN|en-US]
|
||||
if (parts.length === 1) {
|
||||
// Show UI language subcommand help
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: [
|
||||
t('Set UI language'),
|
||||
'',
|
||||
t('Usage: /language ui [zh-CN|en-US]'),
|
||||
'',
|
||||
t('Available options:'),
|
||||
t(' - zh-CN: Simplified Chinese'),
|
||||
t(' - en-US: English'),
|
||||
'',
|
||||
t(
|
||||
'To request additional UI language packs, please open an issue on GitHub.',
|
||||
),
|
||||
].join('\n'),
|
||||
};
|
||||
}
|
||||
|
||||
const langArg = parts[1].toLowerCase();
|
||||
let targetLang: SupportedLanguage | null = null;
|
||||
|
||||
if (langArg === 'en' || langArg === 'english' || langArg === 'en-us') {
|
||||
targetLang = 'en';
|
||||
} else if (
|
||||
langArg === 'zh' ||
|
||||
langArg === 'chinese' ||
|
||||
langArg === '中文' ||
|
||||
langArg === 'zh-cn'
|
||||
) {
|
||||
targetLang = 'zh';
|
||||
} else {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: t('Invalid language. Available: en-US, zh-CN'),
|
||||
};
|
||||
}
|
||||
|
||||
return setUiLanguage(context, targetLang);
|
||||
} else if (subcommand === 'output') {
|
||||
// Handle /language output <language>
|
||||
if (parts.length === 1) {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: [
|
||||
t('Set LLM output language'),
|
||||
'',
|
||||
t('Usage: /language output <language>'),
|
||||
` ${t('Example: /language output 中文')}`,
|
||||
].join('\n'),
|
||||
};
|
||||
}
|
||||
|
||||
// Join all parts after "output" as the language name
|
||||
const language = parts.slice(1).join(' ');
|
||||
return generateLlmOutputLanguageRuleFile(language);
|
||||
} else {
|
||||
// Backward compatibility: treat as UI language
|
||||
const langArg = trimmedArgs.toLowerCase();
|
||||
let targetLang: SupportedLanguage | null = null;
|
||||
|
||||
if (langArg === 'en' || langArg === 'english' || langArg === 'en-us') {
|
||||
targetLang = 'en';
|
||||
} else if (
|
||||
langArg === 'zh' ||
|
||||
langArg === 'chinese' ||
|
||||
langArg === '中文' ||
|
||||
langArg === 'zh-cn'
|
||||
) {
|
||||
targetLang = 'zh';
|
||||
} else {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: [
|
||||
t('Invalid command. Available subcommands:'),
|
||||
' - /language ui [zh-CN|en-US] - ' + t('Set UI language'),
|
||||
' - /language output <language> - ' + t('Set LLM output language'),
|
||||
].join('\n'),
|
||||
};
|
||||
}
|
||||
|
||||
return setUiLanguage(context, targetLang);
|
||||
}
|
||||
},
|
||||
subCommands: [
|
||||
{
|
||||
name: 'ui',
|
||||
get description() {
|
||||
return t('Set UI language');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (
|
||||
context: CommandContext,
|
||||
args: string,
|
||||
): Promise<MessageActionReturn> => {
|
||||
const trimmedArgs = args.trim();
|
||||
if (!trimmedArgs) {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: [
|
||||
t('Set UI language'),
|
||||
'',
|
||||
t('Usage: /language ui [zh-CN|en-US]'),
|
||||
'',
|
||||
t('Available options:'),
|
||||
t(' - zh-CN: Simplified Chinese'),
|
||||
t(' - en-US: English'),
|
||||
'',
|
||||
t(
|
||||
'To request additional UI language packs, please open an issue on GitHub.',
|
||||
),
|
||||
].join('\n'),
|
||||
};
|
||||
}
|
||||
|
||||
const langArg = trimmedArgs.toLowerCase();
|
||||
let targetLang: SupportedLanguage | null = null;
|
||||
|
||||
if (langArg === 'en' || langArg === 'english' || langArg === 'en-us') {
|
||||
targetLang = 'en';
|
||||
} else if (
|
||||
langArg === 'zh' ||
|
||||
langArg === 'chinese' ||
|
||||
langArg === '中文' ||
|
||||
langArg === 'zh-cn'
|
||||
) {
|
||||
targetLang = 'zh';
|
||||
} else {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: t('Invalid language. Available: en-US, zh-CN'),
|
||||
};
|
||||
}
|
||||
|
||||
return setUiLanguage(context, targetLang);
|
||||
},
|
||||
subCommands: [
|
||||
{
|
||||
name: 'zh-CN',
|
||||
altNames: ['zh', 'chinese', '中文'],
|
||||
get description() {
|
||||
return t('Set UI language to Simplified Chinese (zh-CN)');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (
|
||||
context: CommandContext,
|
||||
args: string,
|
||||
): Promise<MessageActionReturn> => {
|
||||
if (args.trim().length > 0) {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: t(
|
||||
'Language subcommands do not accept additional arguments.',
|
||||
),
|
||||
};
|
||||
}
|
||||
return setUiLanguage(context, 'zh');
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'en-US',
|
||||
altNames: ['en', 'english'],
|
||||
get description() {
|
||||
return t('Set UI language to English (en-US)');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (
|
||||
context: CommandContext,
|
||||
args: string,
|
||||
): Promise<MessageActionReturn> => {
|
||||
if (args.trim().length > 0) {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: t(
|
||||
'Language subcommands do not accept additional arguments.',
|
||||
),
|
||||
};
|
||||
}
|
||||
return setUiLanguage(context, 'en');
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'output',
|
||||
get description() {
|
||||
return t('Set LLM output language');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (
|
||||
context: CommandContext,
|
||||
args: string,
|
||||
): Promise<MessageActionReturn> => {
|
||||
const trimmedArgs = args.trim();
|
||||
if (!trimmedArgs) {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: [
|
||||
t('Set LLM output language'),
|
||||
'',
|
||||
t('Usage: /language output <language>'),
|
||||
` ${t('Example: /language output 中文')}`,
|
||||
` ${t('Example: /language output English')}`,
|
||||
` ${t('Example: /language output 日本語')}`,
|
||||
].join('\n'),
|
||||
};
|
||||
}
|
||||
|
||||
return generateLlmOutputLanguageRuleFile(trimmedArgs);
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -24,10 +24,13 @@ import {
|
||||
} from '@qwen-code/qwen-code-core';
|
||||
import { appEvents, AppEvent } from '../../utils/events.js';
|
||||
import { MessageType, type HistoryItemMcpStatus } from '../types.js';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
const authCommand: SlashCommand = {
|
||||
name: 'auth',
|
||||
description: 'Authenticate with an OAuth-enabled MCP server',
|
||||
get description() {
|
||||
return t('Authenticate with an OAuth-enabled MCP server');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (
|
||||
context: CommandContext,
|
||||
@@ -40,7 +43,7 @@ const authCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'Config not loaded.',
|
||||
content: t('Config not loaded.'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -56,14 +59,14 @@ const authCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: 'No MCP servers configured with OAuth authentication.',
|
||||
content: t('No MCP servers configured with OAuth authentication.'),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: `MCP servers with OAuth authentication:\n${oauthServers.map((s) => ` - ${s}`).join('\n')}\n\nUse /mcp auth <server-name> to authenticate.`,
|
||||
content: `${t('MCP servers with OAuth authentication:')}\n${oauthServers.map((s) => ` - ${s}`).join('\n')}\n\n${t('Use /mcp auth <server-name> to authenticate.')}`,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -72,7 +75,7 @@ const authCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: `MCP server '${serverName}' not found.`,
|
||||
content: t("MCP server '{{name}}' not found.", { name: serverName }),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -89,7 +92,12 @@ const authCommand: SlashCommand = {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: 'info',
|
||||
text: `Starting OAuth authentication for MCP server '${serverName}'...`,
|
||||
text: t(
|
||||
"Starting OAuth authentication for MCP server '{{name}}'...",
|
||||
{
|
||||
name: serverName,
|
||||
},
|
||||
),
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
@@ -111,7 +119,12 @@ const authCommand: SlashCommand = {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: 'info',
|
||||
text: `✅ Successfully authenticated with MCP server '${serverName}'!`,
|
||||
text: t(
|
||||
"Successfully authenticated and refreshed tools for '{{name}}'.",
|
||||
{
|
||||
name: serverName,
|
||||
},
|
||||
),
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
@@ -122,7 +135,9 @@ const authCommand: SlashCommand = {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: 'info',
|
||||
text: `Re-discovering tools from '${serverName}'...`,
|
||||
text: t("Re-discovering tools from '{{name}}'...", {
|
||||
name: serverName,
|
||||
}),
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
@@ -140,13 +155,24 @@ const authCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: `Successfully authenticated and refreshed tools for '${serverName}'.`,
|
||||
content: t(
|
||||
"Successfully authenticated and refreshed tools for '{{name}}'.",
|
||||
{
|
||||
name: serverName,
|
||||
},
|
||||
),
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: `Failed to authenticate with MCP server '${serverName}': ${getErrorMessage(error)}`,
|
||||
content: t(
|
||||
"Failed to authenticate with MCP server '{{name}}': {{error}}",
|
||||
{
|
||||
name: serverName,
|
||||
error: getErrorMessage(error),
|
||||
},
|
||||
),
|
||||
};
|
||||
} finally {
|
||||
appEvents.removeListener(AppEvent.OauthDisplayMessage, displayListener);
|
||||
@@ -165,7 +191,9 @@ const authCommand: SlashCommand = {
|
||||
|
||||
const listCommand: SlashCommand = {
|
||||
name: 'list',
|
||||
description: 'List configured MCP servers and tools',
|
||||
get description() {
|
||||
return t('List configured MCP servers and tools');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (
|
||||
context: CommandContext,
|
||||
@@ -176,7 +204,7 @@ const listCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'Config not loaded.',
|
||||
content: t('Config not loaded.'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -185,7 +213,7 @@ const listCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'Could not retrieve tool registry.',
|
||||
content: t('Could not retrieve tool registry.'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -276,7 +304,9 @@ const listCommand: SlashCommand = {
|
||||
|
||||
const refreshCommand: SlashCommand = {
|
||||
name: 'refresh',
|
||||
description: 'Restarts MCP servers.',
|
||||
get description() {
|
||||
return t('Restarts MCP servers.');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (
|
||||
context: CommandContext,
|
||||
@@ -286,7 +316,7 @@ const refreshCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'Config not loaded.',
|
||||
content: t('Config not loaded.'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -295,14 +325,14 @@ const refreshCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'Could not retrieve tool registry.',
|
||||
content: t('Could not retrieve tool registry.'),
|
||||
};
|
||||
}
|
||||
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: 'info',
|
||||
text: 'Restarting MCP servers...',
|
||||
text: t('Restarting MCP servers...'),
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
@@ -324,8 +354,11 @@ const refreshCommand: SlashCommand = {
|
||||
|
||||
export const mcpCommand: SlashCommand = {
|
||||
name: 'mcp',
|
||||
description:
|
||||
'list configured MCP servers and tools, or authenticate with OAuth-enabled servers',
|
||||
get description() {
|
||||
return t(
|
||||
'list configured MCP servers and tools, or authenticate with OAuth-enabled servers',
|
||||
);
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
subCommands: [listCommand, authCommand, refreshCommand],
|
||||
// Default action when no subcommand is provided
|
||||
|
||||
@@ -15,15 +15,20 @@ import fs from 'fs/promises';
|
||||
import { MessageType } from '../types.js';
|
||||
import type { SlashCommand, SlashCommandActionReturn } from './types.js';
|
||||
import { CommandKind } from './types.js';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
export const memoryCommand: SlashCommand = {
|
||||
name: 'memory',
|
||||
description: 'Commands for interacting with memory.',
|
||||
get description() {
|
||||
return t('Commands for interacting with memory.');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
subCommands: [
|
||||
{
|
||||
name: 'show',
|
||||
description: 'Show the current memory contents.',
|
||||
get description() {
|
||||
return t('Show the current memory contents.');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context) => {
|
||||
const memoryContent = context.services.config?.getUserMemory() || '';
|
||||
@@ -31,8 +36,8 @@ export const memoryCommand: SlashCommand = {
|
||||
|
||||
const messageContent =
|
||||
memoryContent.length > 0
|
||||
? `Current memory content from ${fileCount} file(s):\n\n---\n${memoryContent}\n---`
|
||||
: 'Memory is currently empty.';
|
||||
? `${t('Current memory content from {{count}} file(s):', { count: String(fileCount) })}\n\n---\n${memoryContent}\n---`
|
||||
: t('Memory is currently empty.');
|
||||
|
||||
context.ui.addItem(
|
||||
{
|
||||
@@ -45,7 +50,9 @@ export const memoryCommand: SlashCommand = {
|
||||
subCommands: [
|
||||
{
|
||||
name: '--project',
|
||||
description: 'Show project-level memory contents.',
|
||||
get description() {
|
||||
return t('Show project-level memory contents.');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context) => {
|
||||
try {
|
||||
@@ -57,8 +64,14 @@ export const memoryCommand: SlashCommand = {
|
||||
|
||||
const messageContent =
|
||||
memoryContent.trim().length > 0
|
||||
? `Project memory content from ${projectMemoryPath}:\n\n---\n${memoryContent}\n---`
|
||||
: 'Project memory is currently empty.';
|
||||
? t(
|
||||
'Project memory content from {{path}}:\n\n---\n{{content}}\n---',
|
||||
{
|
||||
path: projectMemoryPath,
|
||||
content: memoryContent,
|
||||
},
|
||||
)
|
||||
: t('Project memory is currently empty.');
|
||||
|
||||
context.ui.addItem(
|
||||
{
|
||||
@@ -71,7 +84,9 @@ export const memoryCommand: SlashCommand = {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: 'Project memory file not found or is currently empty.',
|
||||
text: t(
|
||||
'Project memory file not found or is currently empty.',
|
||||
),
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
@@ -80,7 +95,9 @@ export const memoryCommand: SlashCommand = {
|
||||
},
|
||||
{
|
||||
name: '--global',
|
||||
description: 'Show global memory contents.',
|
||||
get description() {
|
||||
return t('Show global memory contents.');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context) => {
|
||||
try {
|
||||
@@ -96,8 +113,10 @@ export const memoryCommand: SlashCommand = {
|
||||
|
||||
const messageContent =
|
||||
globalMemoryContent.trim().length > 0
|
||||
? `Global memory content:\n\n---\n${globalMemoryContent}\n---`
|
||||
: 'Global memory is currently empty.';
|
||||
? t('Global memory content:\n\n---\n{{content}}\n---', {
|
||||
content: globalMemoryContent,
|
||||
})
|
||||
: t('Global memory is currently empty.');
|
||||
|
||||
context.ui.addItem(
|
||||
{
|
||||
@@ -110,7 +129,9 @@ export const memoryCommand: SlashCommand = {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: 'Global memory file not found or is currently empty.',
|
||||
text: t(
|
||||
'Global memory file not found or is currently empty.',
|
||||
),
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
@@ -121,16 +142,20 @@ export const memoryCommand: SlashCommand = {
|
||||
},
|
||||
{
|
||||
name: 'add',
|
||||
description:
|
||||
'Add content to the memory. Use --global for global memory or --project for project memory.',
|
||||
get description() {
|
||||
return t(
|
||||
'Add content to the memory. Use --global for global memory or --project for project memory.',
|
||||
);
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (context, args): SlashCommandActionReturn | void => {
|
||||
if (!args || args.trim() === '') {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content:
|
||||
content: t(
|
||||
'Usage: /memory add [--global|--project] <text to remember>',
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -150,8 +175,9 @@ export const memoryCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content:
|
||||
content: t(
|
||||
'Usage: /memory add [--global|--project] <text to remember>',
|
||||
),
|
||||
};
|
||||
} else {
|
||||
// No scope specified, will be handled by the tool
|
||||
@@ -162,8 +188,9 @@ export const memoryCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content:
|
||||
content: t(
|
||||
'Usage: /memory add [--global|--project] <text to remember>',
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -171,7 +198,10 @@ export const memoryCommand: SlashCommand = {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Attempting to save to memory ${scopeText}: "${fact}"`,
|
||||
text: t('Attempting to save to memory {{scope}}: "{{fact}}"', {
|
||||
scope: scopeText,
|
||||
fact,
|
||||
}),
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
@@ -185,21 +215,25 @@ export const memoryCommand: SlashCommand = {
|
||||
subCommands: [
|
||||
{
|
||||
name: '--project',
|
||||
description: 'Add content to project-level memory.',
|
||||
get description() {
|
||||
return t('Add content to project-level memory.');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (context, args): SlashCommandActionReturn | void => {
|
||||
if (!args || args.trim() === '') {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'Usage: /memory add --project <text to remember>',
|
||||
content: t('Usage: /memory add --project <text to remember>'),
|
||||
};
|
||||
}
|
||||
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Attempting to save to project memory: "${args.trim()}"`,
|
||||
text: t('Attempting to save to project memory: "{{text}}"', {
|
||||
text: args.trim(),
|
||||
}),
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
@@ -213,21 +247,25 @@ export const memoryCommand: SlashCommand = {
|
||||
},
|
||||
{
|
||||
name: '--global',
|
||||
description: 'Add content to global memory.',
|
||||
get description() {
|
||||
return t('Add content to global memory.');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (context, args): SlashCommandActionReturn | void => {
|
||||
if (!args || args.trim() === '') {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'Usage: /memory add --global <text to remember>',
|
||||
content: t('Usage: /memory add --global <text to remember>'),
|
||||
};
|
||||
}
|
||||
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Attempting to save to global memory: "${args.trim()}"`,
|
||||
text: t('Attempting to save to global memory: "{{text}}"', {
|
||||
text: args.trim(),
|
||||
}),
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
@@ -243,13 +281,15 @@ export const memoryCommand: SlashCommand = {
|
||||
},
|
||||
{
|
||||
name: 'refresh',
|
||||
description: 'Refresh the memory from the source.',
|
||||
get description() {
|
||||
return t('Refresh the memory from the source.');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context) => {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: 'Refreshing memory from source files...',
|
||||
text: t('Refreshing memory from source files...'),
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
|
||||
@@ -12,10 +12,13 @@ import type {
|
||||
} from './types.js';
|
||||
import { CommandKind } from './types.js';
|
||||
import { getAvailableModelsForAuthType } from '../models/availableModels.js';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
export const modelCommand: SlashCommand = {
|
||||
name: 'model',
|
||||
description: 'Switch the model for this session',
|
||||
get description() {
|
||||
return t('Switch the model for this session');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (
|
||||
context: CommandContext,
|
||||
@@ -36,7 +39,7 @@ export const modelCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'Content generator configuration not available.',
|
||||
content: t('Content generator configuration not available.'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -45,7 +48,7 @@ export const modelCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'Authentication type not available.',
|
||||
content: t('Authentication type not available.'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -55,7 +58,12 @@ export const modelCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: `No models available for the current authentication type (${authType}).`,
|
||||
content: t(
|
||||
'No models available for the current authentication type ({{authType}}).',
|
||||
{
|
||||
authType,
|
||||
},
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -6,10 +6,13 @@
|
||||
|
||||
import type { OpenDialogActionReturn, SlashCommand } from './types.js';
|
||||
import { CommandKind } from './types.js';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
export const permissionsCommand: SlashCommand = {
|
||||
name: 'permissions',
|
||||
description: 'Manage folder trust settings',
|
||||
get description() {
|
||||
return t('Manage folder trust settings');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (): OpenDialogActionReturn => ({
|
||||
type: 'dialog',
|
||||
|
||||
@@ -6,10 +6,13 @@
|
||||
|
||||
import { formatDuration } from '../utils/formatters.js';
|
||||
import { CommandKind, type SlashCommand } from './types.js';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
export const quitConfirmCommand: SlashCommand = {
|
||||
name: 'quit-confirm',
|
||||
description: 'Show quit confirmation dialog',
|
||||
get description() {
|
||||
return t('Show quit confirmation dialog');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (context) => {
|
||||
const now = Date.now();
|
||||
@@ -37,7 +40,9 @@ export const quitConfirmCommand: SlashCommand = {
|
||||
export const quitCommand: SlashCommand = {
|
||||
name: 'quit',
|
||||
altNames: ['exit'],
|
||||
description: 'exit the cli',
|
||||
get description() {
|
||||
return t('exit the cli');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (context) => {
|
||||
const now = Date.now();
|
||||
|
||||
@@ -6,10 +6,13 @@
|
||||
|
||||
import type { OpenDialogActionReturn, SlashCommand } from './types.js';
|
||||
import { CommandKind } from './types.js';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
export const settingsCommand: SlashCommand = {
|
||||
name: 'settings',
|
||||
description: 'View and edit Qwen Code settings',
|
||||
get description() {
|
||||
return t('View and edit Qwen Code settings');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (_context, _args): OpenDialogActionReturn => ({
|
||||
type: 'dialog',
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
import type { SlashCommand, SlashCommandActionReturn } from './types.js';
|
||||
import { CommandKind } from './types.js';
|
||||
import { getUrlOpenCommand } from '../../ui/utils/commandUtils.js';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
export const GITHUB_WORKFLOW_PATHS = [
|
||||
'gemini-dispatch/gemini-dispatch.yml',
|
||||
@@ -91,7 +92,9 @@ export async function updateGitignore(gitRepoRoot: string): Promise<void> {
|
||||
|
||||
export const setupGithubCommand: SlashCommand = {
|
||||
name: 'setup-github',
|
||||
description: 'Set up GitHub Actions',
|
||||
get description() {
|
||||
return t('Set up GitHub Actions');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (
|
||||
context: CommandContext,
|
||||
|
||||
@@ -12,11 +12,14 @@ import {
|
||||
type SlashCommand,
|
||||
CommandKind,
|
||||
} from './types.js';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
export const statsCommand: SlashCommand = {
|
||||
name: 'stats',
|
||||
altNames: ['usage'],
|
||||
description: 'check session stats. Usage: /stats [model|tools]',
|
||||
get description() {
|
||||
return t('check session stats. Usage: /stats [model|tools]');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (context: CommandContext) => {
|
||||
const now = new Date();
|
||||
@@ -25,7 +28,7 @@ export const statsCommand: SlashCommand = {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Session start time is unavailable, cannot calculate stats.',
|
||||
text: t('Session start time is unavailable, cannot calculate stats.'),
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
@@ -43,7 +46,9 @@ export const statsCommand: SlashCommand = {
|
||||
subCommands: [
|
||||
{
|
||||
name: 'model',
|
||||
description: 'Show model-specific usage statistics.',
|
||||
get description() {
|
||||
return t('Show model-specific usage statistics.');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (context: CommandContext) => {
|
||||
context.ui.addItem(
|
||||
@@ -56,7 +61,9 @@ export const statsCommand: SlashCommand = {
|
||||
},
|
||||
{
|
||||
name: 'tools',
|
||||
description: 'Show tool-specific usage statistics.',
|
||||
get description() {
|
||||
return t('Show tool-specific usage statistics.');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (context: CommandContext) => {
|
||||
context.ui.addItem(
|
||||
|
||||
@@ -13,11 +13,15 @@ import {
|
||||
} from './types.js';
|
||||
import { getProjectSummaryPrompt } from '@qwen-code/qwen-code-core';
|
||||
import type { HistoryItemSummary } from '../types.js';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
export const summaryCommand: SlashCommand = {
|
||||
name: 'summary',
|
||||
description:
|
||||
'Generate a project summary and save it to .qwen/PROJECT_SUMMARY.md',
|
||||
get description() {
|
||||
return t(
|
||||
'Generate a project summary and save it to .qwen/PROJECT_SUMMARY.md',
|
||||
);
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context): Promise<SlashCommandActionReturn> => {
|
||||
const { config } = context.services;
|
||||
@@ -26,7 +30,7 @@ export const summaryCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'Config not loaded.',
|
||||
content: t('Config not loaded.'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -35,7 +39,7 @@ export const summaryCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'No chat client available to generate summary.',
|
||||
content: t('No chat client available to generate summary.'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -44,15 +48,18 @@ export const summaryCommand: SlashCommand = {
|
||||
ui.addItem(
|
||||
{
|
||||
type: 'error' as const,
|
||||
text: 'Already generating summary, wait for previous request to complete',
|
||||
text: t(
|
||||
'Already generating summary, wait for previous request to complete',
|
||||
),
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content:
|
||||
content: t(
|
||||
'Already generating summary, wait for previous request to complete',
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -65,7 +72,7 @@ export const summaryCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: 'No conversation found to summarize.',
|
||||
content: t('No conversation found to summarize.'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -171,9 +178,12 @@ export const summaryCommand: SlashCommand = {
|
||||
ui.addItem(
|
||||
{
|
||||
type: 'error' as const,
|
||||
text: `❌ Failed to generate project context summary: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`,
|
||||
text: `❌ ${t(
|
||||
'Failed to generate project context summary: {{error}}',
|
||||
{
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
},
|
||||
)}`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
@@ -181,9 +191,9 @@ export const summaryCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: `Failed to generate project context summary: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`,
|
||||
content: t('Failed to generate project context summary: {{error}}', {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
}),
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import type { MessageActionReturn, SlashCommand } from './types.js';
|
||||
import { CommandKind } from './types.js';
|
||||
import { terminalSetup } from '../utils/terminalSetup.js';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
/**
|
||||
* Command to configure terminal keybindings for multiline input support.
|
||||
@@ -16,8 +17,11 @@ import { terminalSetup } from '../utils/terminalSetup.js';
|
||||
*/
|
||||
export const terminalSetupCommand: SlashCommand = {
|
||||
name: 'terminal-setup',
|
||||
description:
|
||||
'Configure terminal keybindings for multiline input (VS Code, Cursor, Windsurf, Trae)',
|
||||
get description() {
|
||||
return t(
|
||||
'Configure terminal keybindings for multiline input (VS Code, Cursor, Windsurf, Trae)',
|
||||
);
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
|
||||
action: async (): Promise<MessageActionReturn> => {
|
||||
@@ -27,7 +31,8 @@ export const terminalSetupCommand: SlashCommand = {
|
||||
let content = result.message;
|
||||
if (result.requiresRestart) {
|
||||
content +=
|
||||
'\n\nPlease restart your terminal for the changes to take effect.';
|
||||
'\n\n' +
|
||||
t('Please restart your terminal for the changes to take effect.');
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -38,7 +43,9 @@ export const terminalSetupCommand: SlashCommand = {
|
||||
} catch (error) {
|
||||
return {
|
||||
type: 'message',
|
||||
content: `Failed to configure terminal: ${error}`,
|
||||
content: t('Failed to configure terminal: {{error}}', {
|
||||
error: String(error),
|
||||
}),
|
||||
messageType: 'error',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,10 +6,13 @@
|
||||
|
||||
import type { OpenDialogActionReturn, SlashCommand } from './types.js';
|
||||
import { CommandKind } from './types.js';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
export const themeCommand: SlashCommand = {
|
||||
name: 'theme',
|
||||
description: 'change the theme',
|
||||
get description() {
|
||||
return t('change the theme');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (_context, _args): OpenDialogActionReturn => ({
|
||||
type: 'dialog',
|
||||
|
||||
@@ -10,10 +10,13 @@ import {
|
||||
CommandKind,
|
||||
} from './types.js';
|
||||
import { MessageType, type HistoryItemToolsList } from '../types.js';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
export const toolsCommand: SlashCommand = {
|
||||
name: 'tools',
|
||||
description: 'list available Qwen Code tools. Usage: /tools [desc]',
|
||||
get description() {
|
||||
return t('list available Qwen Code tools. Usage: /tools [desc]');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context: CommandContext, args?: string): Promise<void> => {
|
||||
const subCommand = args?.trim();
|
||||
@@ -29,7 +32,7 @@ export const toolsCommand: SlashCommand = {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Could not retrieve tool registry.',
|
||||
text: t('Could not retrieve tool registry.'),
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
|
||||
@@ -6,10 +6,13 @@
|
||||
|
||||
import type { SlashCommand } from './types.js';
|
||||
import { CommandKind } from './types.js';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
export const vimCommand: SlashCommand = {
|
||||
name: 'vim',
|
||||
description: 'toggle vim mode on/off',
|
||||
get description() {
|
||||
return t('toggle vim mode on/off');
|
||||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context, _args) => {
|
||||
const newVimState = await context.ui.toggleVimEnabled();
|
||||
|
||||
Reference in New Issue
Block a user