diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index a8e816a84..000000000 --- a/.eslintrc.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "root": true, - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": 2022, - "sourceType": "module" - }, - "plugins": ["@typescript-eslint"], - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "prettier" - ], - "rules": { - "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], - "@typescript-eslint/no-explicit-any": "warn", - "@typescript-eslint/explicit-module-boundary-types": "off" - }, - "env": { - "node": true, - "es2022": true - } -} diff --git a/apps/airiscode-cli/__tests__/utils.spec.ts b/apps/airiscode-cli/__tests__/utils.spec.ts index 3daa8f6cc..dd1ae9df3 100644 --- a/apps/airiscode-cli/__tests__/utils.spec.ts +++ b/apps/airiscode-cli/__tests__/utils.spec.ts @@ -1,13 +1,13 @@ -import { describe, it, expect, vi, beforeEach } from "vitest"; import * as fs from "node:fs/promises"; import * as path from "node:path"; +import { beforeEach, describe, expect, it, vi } from "vitest"; vi.mock("node:os", () => ({ - homedir: () => "/mock/home" + homedir: () => "/mock/home", })); import { loadConfig, saveConfig } from "../src/utils/config.js"; -import { saveSession, listSessions, loadSession } from "../src/utils/session.js"; +import { listSessions, loadSession, saveSession } from "../src/utils/session.js"; vi.mock("node:fs/promises"); @@ -26,7 +26,7 @@ describe("CLI Utilities", () => { it("should save and merge config", async () => { vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify({ plannerModel: "new-model" })); const updated = await saveConfig({ coderModel: "coder-9b" }); - + expect(updated.plannerModel).toBe("new-model"); expect(updated.coderModel).toBe("coder-9b"); expect(fs.writeFile).toHaveBeenCalled(); @@ -35,7 +35,11 @@ describe("CLI Utilities", () => { describe("Session Utility", () => { it("should list sessions", async () => { - vi.mocked(fs.readdir).mockResolvedValue(["session-1.json", "session-2.json", "other.txt"] as any); + vi.mocked(fs.readdir).mockResolvedValue([ + "session-1.json", + "session-2.json", + "other.txt", + ] as any); const sessions = await listSessions(); expect(sessions).toHaveLength(2); expect(sessions[0]).toBe("session-2.json"); // Sorted reverse diff --git a/apps/airiscode-cli/package.json b/apps/airiscode-cli/package.json index bd8f2ab02..5a5fb1303 100644 --- a/apps/airiscode-cli/package.json +++ b/apps/airiscode-cli/package.json @@ -15,7 +15,7 @@ "dev": "tsup --watch", "typecheck": "tsc --noEmit", "start": "node dist/index.js", - "lint": "eslint . --ext .ts,.tsx", + "lint": "biome check .", "format": "prettier --write .", "test": "vitest run" }, diff --git a/apps/airiscode-cli/src/commands/auth.ts b/apps/airiscode-cli/src/commands/auth.ts index 97444d3a9..0850f4cce 100644 --- a/apps/airiscode-cli/src/commands/auth.ts +++ b/apps/airiscode-cli/src/commands/auth.ts @@ -4,58 +4,52 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { CommandModule, Argv } from 'yargs'; -import { - handleQwenAuth, - runInteractiveAuth, - showAuthStatus, -} from './auth/handler.js'; -import { t } from '../i18n/index.js'; +import type { Argv, CommandModule } from "yargs"; +import { t } from "../i18n/index.js"; +import { handleQwenAuth, runInteractiveAuth, showAuthStatus } from "./auth/handler.js"; // Qwen OAuth subcommand removed - use coding-plan or interactive auth instead. const codePlanCommand = { - command: 'coding-plan', - describe: t('Authenticate using Alibaba Cloud Coding Plan'), + command: "coding-plan", + describe: t("Authenticate using Alibaba Cloud Coding Plan"), builder: (yargs: Argv) => yargs - .option('region', { - alias: 'r', - describe: t('Region for Coding Plan (china/global)'), - type: 'string', + .option("region", { + alias: "r", + describe: t("Region for Coding Plan (china/global)"), + type: "string", }) - .option('key', { - alias: 'k', - describe: t('API key for Coding Plan'), - type: 'string', + .option("key", { + alias: "k", + describe: t("API key for Coding Plan"), + type: "string", }), handler: async (argv: { region?: string; key?: string }) => { - const region = argv['region'] as string | undefined; - const key = argv['key'] as string | undefined; + const region = argv["region"] as string | undefined; + const key = argv["key"] as string | undefined; // If region and key are provided, use them directly if (region && key) { - await handleQwenAuth('coding-plan', { region, key }); + await handleQwenAuth("coding-plan", { region, key }); } else { // Otherwise, prompt interactively - await handleQwenAuth('coding-plan', {}); + await handleQwenAuth("coding-plan", {}); } }, }; const statusCommand = { - command: 'status', - describe: t('Show current authentication status'), + command: "status", + describe: t("Show current authentication status"), handler: async () => { await showAuthStatus(); }, }; export const authCommand: CommandModule = { - command: 'auth', - describe: t( - 'Configure authentication information with Alibaba Cloud Coding Plan', - ), + command: "auth", + describe: t("Configure authentication information with Alibaba Cloud Coding Plan"), builder: (yargs: Argv) => yargs .command(codePlanCommand) diff --git a/apps/airiscode-cli/src/commands/auth/handler.ts b/apps/airiscode-cli/src/commands/auth/handler.ts index e38674e41..227a7f9fb 100644 --- a/apps/airiscode-cli/src/commands/auth/handler.ts +++ b/apps/airiscode-cli/src/commands/auth/handler.ts @@ -6,24 +6,24 @@ import { AuthType, - getErrorMessage, type Config, + getErrorMessage, type ProviderModelConfig as ModelConfig, -} from '@airiscode/core'; -import { writeStdoutLine, writeStderrLine } from '../../utils/stdioHelpers.js'; -import { t } from '../../i18n/index.js'; +} from "@airiscode/core"; +import type { CliArgs } from "../../config/config.js"; +import { loadCliConfig } from "../../config/config.js"; +import { getPersistScopeForModelSelection } from "../../config/modelProvidersScope.js"; +import { type LoadedSettings, loadSettings } from "../../config/settings.js"; import { + CODING_PLAN_ENV_KEY, + CodingPlanRegion, getCodingPlanConfig, isCodingPlanConfig, - CodingPlanRegion, - CODING_PLAN_ENV_KEY, -} from '../../constants/codingPlan.js'; -import { getPersistScopeForModelSelection } from '../../config/modelProvidersScope.js'; -import { backupSettingsFile } from '../../utils/settingsUtils.js'; -import { loadSettings, type LoadedSettings } from '../../config/settings.js'; -import { loadCliConfig } from '../../config/config.js'; -import type { CliArgs } from '../../config/config.js'; -import { InteractiveSelector } from './interactiveSelector.js'; +} from "../../constants/codingPlan.js"; +import { t } from "../../i18n/index.js"; +import { backupSettingsFile } from "../../utils/settingsUtils.js"; +import { writeStderrLine, writeStdoutLine } from "../../utils/stdioHelpers.js"; +import { InteractiveSelector } from "./interactiveSelector.js"; interface QwenAuthOptions { region?: string; @@ -52,10 +52,7 @@ interface MergedSettingsWithCodingPlan { /** * Handles the authentication process based on the specified command and options */ -export async function handleQwenAuth( - command: 'coding-plan', - options: QwenAuthOptions, -) { +export async function handleQwenAuth(command: "coding-plan", options: QwenAuthOptions) { try { const settings = loadSettings(); @@ -119,12 +116,12 @@ export async function handleQwenAuth( [], // No extensions for auth command ); - if (command === 'coding-plan') { + if (command === "coding-plan") { await handleCodePlanAuth(config, settings, options); } // Exit after authentication is complete - writeStdoutLine(t('Authentication completed successfully.')); + writeStdoutLine(t("Authentication completed successfully.")); process.exit(0); } catch (error) { writeStderrLine(getErrorMessage(error)); @@ -148,9 +145,7 @@ async function handleCodePlanAuth( // If region and key are provided as options, use them if (region && key) { selectedRegion = - region.toLowerCase() === 'global' - ? CodingPlanRegion.GLOBAL - : CodingPlanRegion.CHINA; + region.toLowerCase() === "global" ? CodingPlanRegion.GLOBAL : CodingPlanRegion.CHINA; selectedKey = key; } else { // Otherwise, prompt interactively @@ -158,7 +153,7 @@ async function handleCodePlanAuth( selectedKey = await promptForKey(); } - writeStdoutLine(t('Processing Alibaba Cloud Coding Plan authentication...')); + writeStdoutLine(t("Processing Alibaba Cloud Coding Plan authentication...")); try { // Get configuration based on region @@ -185,9 +180,8 @@ async function handleCodePlanAuth( // Get existing configs const existingConfigs = - (settings.merged.modelProviders as Record)?.[ - AuthType.USE_OPENAI - ] || []; + (settings.merged.modelProviders as Record)?.[AuthType.USE_OPENAI] || + []; // Filter out all existing Coding Plan configs (mutually exclusive) const nonCodingPlanConfigs = existingConfigs.filter( @@ -198,43 +192,29 @@ async function handleCodePlanAuth( const updatedConfigs = [...newConfigs, ...nonCodingPlanConfigs]; // Persist to modelProviders - settings.setValue( - authTypeScope, - `modelProviders.${AuthType.USE_OPENAI}`, - updatedConfigs, - ); + settings.setValue(authTypeScope, `modelProviders.${AuthType.USE_OPENAI}`, updatedConfigs); // Also persist authType - settings.setValue( - authTypeScope, - 'security.auth.selectedType', - AuthType.USE_OPENAI, - ); + settings.setValue(authTypeScope, "security.auth.selectedType", AuthType.USE_OPENAI); // Persist coding plan region - settings.setValue(authTypeScope, 'codingPlan.region', selectedRegion); + settings.setValue(authTypeScope, "codingPlan.region", selectedRegion); // Persist coding plan version (single field for backward compatibility) - settings.setValue(authTypeScope, 'codingPlan.version', version); + settings.setValue(authTypeScope, "codingPlan.version", version); // If there are configs, use the first one as the model if (updatedConfigs.length > 0 && updatedConfigs[0]?.id) { - settings.setValue( - authTypeScope, - 'model.name', - (updatedConfigs[0] as ModelConfig).id, - ); + settings.setValue(authTypeScope, "model.name", (updatedConfigs[0] as ModelConfig).id); } // Refresh auth with the new configuration await config.refreshAuth(AuthType.USE_OPENAI); - writeStdoutLine( - t('Successfully authenticated with Alibaba Cloud Coding Plan.'), - ); + writeStdoutLine(t("Successfully authenticated with Alibaba Cloud Coding Plan.")); } catch (error) { writeStderrLine( - t('Failed to authenticate with Coding Plan: {{error}}', { + t("Failed to authenticate with Coding Plan: {{error}}", { error: getErrorMessage(error), }), ); @@ -250,16 +230,16 @@ async function promptForRegion(): Promise { [ { value: CodingPlanRegion.CHINA, - label: t('中国 (China)'), - description: t('阿里云百炼 (aliyun.com)'), + label: t("中国 (China)"), + description: t("阿里云百炼 (aliyun.com)"), }, { value: CodingPlanRegion.GLOBAL, - label: t('Global'), - description: t('Alibaba Cloud (alibabacloud.com)'), + label: t("Global"), + description: t("Alibaba Cloud (alibabacloud.com)"), }, ], - t('Select region for Coding Plan:'), + t("Select region for Coding Plan:"), ); return await selector.select(); @@ -273,7 +253,7 @@ async function promptForKey(): Promise { const stdin = process.stdin; const stdout = process.stdout; - stdout.write(t('Enter your Coding Plan API key: ')); + stdout.write(t("Enter your Coding Plan API key: ")); // Set raw mode to capture keystrokes const wasRaw = stdin.isRaw; @@ -283,47 +263,47 @@ async function promptForKey(): Promise { stdin.resume(); return new Promise((resolve, reject) => { - let input = ''; + let input = ""; const onData = (chunk: string) => { for (const char of chunk) { switch (char) { - case '\r': // Enter - case '\n': - stdin.removeListener('data', onData); + case "\r": // Enter + case "\n": + stdin.removeListener("data", onData); if (stdin.setRawMode) { stdin.setRawMode(wasRaw); } - stdout.write('\n'); // New line after input + stdout.write("\n"); // New line after input resolve(input); return; - case '\x03': // Ctrl+C - stdin.removeListener('data', onData); + case "\x03": // Ctrl+C + stdin.removeListener("data", onData); if (stdin.setRawMode) { stdin.setRawMode(wasRaw); } - stdout.write('^C\n'); - reject(new Error('Interrupted')); + stdout.write("^C\n"); + reject(new Error("Interrupted")); return; - case '\x08': // Backspace - case '\x7F': // Delete + case "\x08": // Backspace + case "\x7F": // Delete if (input.length > 0) { input = input.slice(0, -1); // Move cursor back, print space, move back again - stdout.write('\x1B[D \x1B[D'); + stdout.write("\x1B[D \x1B[D"); } break; default: // Add character to input input += char; // Print asterisk instead of the actual character for security - stdout.write('*'); + stdout.write("*"); break; } } }; - stdin.on('data', onData); + stdin.on("data", onData); }); } @@ -334,20 +314,18 @@ export async function runInteractiveAuth() { const selector = new InteractiveSelector( [ { - value: 'coding-plan' as const, - label: t('Alibaba Cloud Coding Plan'), - description: t( - 'Paid · Up to 6,000 requests/5 hrs · All Alibaba Cloud Coding Plan Models', - ), + value: "coding-plan" as const, + label: t("Alibaba Cloud Coding Plan"), + description: t("Paid · Up to 6,000 requests/5 hrs · All Alibaba Cloud Coding Plan Models"), }, ], - t('Select authentication method:'), + t("Select authentication method:"), ); const choice = await selector.select(); - if (choice === 'coding-plan') { - await handleQwenAuth('coding-plan', {}); + if (choice === "coding-plan") { + await handleQwenAuth("coding-plan", {}); } } @@ -359,23 +337,19 @@ export async function showAuthStatus(): Promise { const settings = loadSettings(); const mergedSettings = settings.merged as MergedSettingsWithCodingPlan; - writeStdoutLine(t('\n=== Authentication Status ===\n')); + writeStdoutLine(t("\n=== Authentication Status ===\n")); // Check for selected auth type const selectedType = mergedSettings.security?.auth?.selectedType; if (!selectedType) { - writeStdoutLine(t('⚠️ No authentication method configured.\n')); - writeStdoutLine(t('Run one of the following commands to get started:\n')); - writeStdoutLine( - t( - ' qwen auth coding-plan - Authenticate with Alibaba Cloud Coding Plan\n', - ), - ); - writeStdoutLine(t('Or simply run:')); + writeStdoutLine(t("⚠️ No authentication method configured.\n")); + writeStdoutLine(t("Run one of the following commands to get started:\n")); writeStdoutLine( - t(' qwen auth - Interactive authentication setup\n'), + t(" qwen auth coding-plan - Authenticate with Alibaba Cloud Coding Plan\n"), ); + writeStdoutLine(t("Or simply run:")); + writeStdoutLine(t(" qwen auth - Interactive authentication setup\n")); process.exit(0); } @@ -388,58 +362,45 @@ export async function showAuthStatus(): Promise { // Check if API key is set in environment const hasApiKey = - !!process.env[CODING_PLAN_ENV_KEY] || - !!mergedSettings.env?.[CODING_PLAN_ENV_KEY]; + !!process.env[CODING_PLAN_ENV_KEY] || !!mergedSettings.env?.[CODING_PLAN_ENV_KEY]; if (hasApiKey) { - writeStdoutLine( - t('✓ Authentication Method: Alibaba Cloud Coding Plan'), - ); + writeStdoutLine(t("✓ Authentication Method: Alibaba Cloud Coding Plan")); if (codingPlanRegion) { const regionDisplay = codingPlanRegion === CodingPlanRegion.CHINA - ? t('中国 (China) - 阿里云百炼') - : t('Global - Alibaba Cloud'); - writeStdoutLine(t(' Region: {{region}}', { region: regionDisplay })); + ? t("中国 (China) - 阿里云百炼") + : t("Global - Alibaba Cloud"); + writeStdoutLine(t(" Region: {{region}}", { region: regionDisplay })); } if (modelName) { - writeStdoutLine( - t(' Current Model: {{model}}', { model: modelName }), - ); + writeStdoutLine(t(" Current Model: {{model}}", { model: modelName })); } if (codingPlanVersion) { writeStdoutLine( - t(' Config Version: {{version}}', { - version: codingPlanVersion.substring(0, 8) + '...', + t(" Config Version: {{version}}", { + version: codingPlanVersion.substring(0, 8) + "...", }), ); } - writeStdoutLine(t(' Status: API key configured\n')); + writeStdoutLine(t(" Status: API key configured\n")); } else { - writeStdoutLine( - t( - '⚠️ Authentication Method: Alibaba Cloud Coding Plan (Incomplete)', - ), - ); - writeStdoutLine( - t(' Issue: API key not found in environment or settings\n'), - ); - writeStdoutLine(t(' Run `qwen auth coding-plan` to re-configure.\n')); + writeStdoutLine(t("⚠️ Authentication Method: Alibaba Cloud Coding Plan (Incomplete)")); + writeStdoutLine(t(" Issue: API key not found in environment or settings\n")); + writeStdoutLine(t(" Run `qwen auth coding-plan` to re-configure.\n")); } } else { - writeStdoutLine( - t('✓ Authentication Method: {{type}}', { type: selectedType }), - ); - writeStdoutLine(t(' Status: Configured\n')); + writeStdoutLine(t("✓ Authentication Method: {{type}}", { type: selectedType })); + writeStdoutLine(t(" Status: Configured\n")); } process.exit(0); } catch (error) { writeStderrLine( - t('Failed to check authentication status: {{error}}', { + t("Failed to check authentication status: {{error}}", { error: getErrorMessage(error), }), ); diff --git a/apps/airiscode-cli/src/commands/auth/interactiveSelector.ts b/apps/airiscode-cli/src/commands/auth/interactiveSelector.ts index 84b9c9f0d..7536b60ad 100644 --- a/apps/airiscode-cli/src/commands/auth/interactiveSelector.ts +++ b/apps/airiscode-cli/src/commands/auth/interactiveSelector.ts @@ -4,8 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { stdin, stdout } from 'node:process'; -import { t } from '../../i18n/index.js'; +import { stdin, stdout } from "node:process"; +import { t } from "../../i18n/index.js"; /** * Represents an option in the interactive selector @@ -25,7 +25,7 @@ export class InteractiveSelector { constructor( private options: Array>, - private prompt: string = t('Select an option:'), + private prompt: string = t("Select an option:"), ) {} /** @@ -41,36 +41,32 @@ export class InteractiveSelector { // Check if stdin supports raw mode if (!stdin.setRawMode) { // Fallback to readline if raw mode is not available (e.g., when piped) - reject( - new Error( - t('Raw mode not available. Please run in an interactive terminal.'), - ), - ); + reject(new Error(t("Raw mode not available. Please run in an interactive terminal."))); return; } const wasRaw = stdin.isRaw; stdin.setRawMode(true); stdin.resume(); - stdin.setEncoding('utf8'); + stdin.setEncoding("utf8"); const onData = (chunk: string) => { if (!this.isListening) return; for (const char of chunk) { switch (char) { - case '\x03': // Ctrl+C - stdin.removeListener('data', onData); + case "\x03": // Ctrl+C + stdin.removeListener("data", onData); stdin.setRawMode(wasRaw); - reject(new Error('Interrupted')); + reject(new Error("Interrupted")); return; - case '\r': // Enter - case '\n': // Newline - stdin.removeListener('data', onData); + case "\r": // Enter + case "\n": // Newline + stdin.removeListener("data", onData); stdin.setRawMode(wasRaw); resolve(this.options[this.selectedIndex].value); return; - case '\x1B': // ESC sequence + case "\x1B": // ESC sequence // Next character will be [, then A, B, C, or D break; default: @@ -80,24 +76,24 @@ export class InteractiveSelector { } // Handle escape sequences - if (chunk.startsWith('\x1B')) { - if (chunk === '\x1B[A') { + if (chunk.startsWith("\x1B")) { + if (chunk === "\x1B[A") { // Arrow up this.moveUp(); - } else if (chunk === '\x1B[B') { + } else if (chunk === "\x1B[B") { // Arrow down this.moveDown(); - } else if (chunk === '\x1B[C') { + } else if (chunk === "\x1B[C") { // Arrow right // Do nothing for now - } else if (chunk === '\x1B[D') { + } else if (chunk === "\x1B[D") { // Arrow left // Do nothing for now } } }; - stdin.on('data', onData); + stdin.on("data", onData); }); } @@ -119,9 +115,9 @@ export class InteractiveSelector { // Write each option - combine label and description on same line this.options.forEach((option, index) => { const isSelected = index === this.selectedIndex; - const indicator = isSelected ? '> ' : ' '; - const color = isSelected ? '\x1B[36m' : '\x1B[0m'; // Cyan for selected, default for others - const reset = '\x1B[0m'; + const indicator = isSelected ? "> " : " "; + const color = isSelected ? "\x1B[36m" : "\x1B[0m"; // Cyan for selected, default for others + const reset = "\x1B[0m"; // Combine label and description in one line let line = `${indicator}${color}${option.label}`; @@ -134,9 +130,7 @@ export class InteractiveSelector { }); // Add instructions - stdout.write( - `\n${t('(Use ↑ ↓ arrows to navigate, Enter to select, Ctrl+C to exit)\n')}`, - ); + stdout.write(`\n${t("(Use ↑ ↓ arrows to navigate, Enter to select, Ctrl+C to exit)\n")}`); } /** @@ -151,8 +145,7 @@ export class InteractiveSelector { * Moves selection up */ private moveUp(): void { - this.selectedIndex = - (this.selectedIndex - 1 + this.options.length) % this.options.length; + this.selectedIndex = (this.selectedIndex - 1 + this.options.length) % this.options.length; this.renderMenu(); } diff --git a/apps/airiscode-cli/src/commands/channel.ts b/apps/airiscode-cli/src/commands/channel.ts index f54249a36..8f0a208bb 100644 --- a/apps/airiscode-cli/src/commands/channel.ts +++ b/apps/airiscode-cli/src/commands/channel.ts @@ -1,6 +1,8 @@ // Channel commands removed - not needed for AIRIS Code export const channelCommand = { - command: 'channel', - describe: 'Channel commands (not available)', - handler: () => { console.log('Channel commands are not available in AIRIS Code'); }, + command: "channel", + describe: "Channel commands (not available)", + handler: () => { + console.log("Channel commands are not available in AIRIS Code"); + }, }; diff --git a/apps/airiscode-cli/src/commands/extensions.tsx b/apps/airiscode-cli/src/commands/extensions.tsx index d02aec31f..ea4a00077 100644 --- a/apps/airiscode-cli/src/commands/extensions.tsx +++ b/apps/airiscode-cli/src/commands/extensions.tsx @@ -4,20 +4,20 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { CommandModule } from 'yargs'; -import { installCommand } from './extensions/install.js'; -import { uninstallCommand } from './extensions/uninstall.js'; -import { listCommand } from './extensions/list.js'; -import { updateCommand } from './extensions/update.js'; -import { disableCommand } from './extensions/disable.js'; -import { enableCommand } from './extensions/enable.js'; -import { linkCommand } from './extensions/link.js'; -import { newCommand } from './extensions/new.js'; -import { settingsCommand } from './extensions/settings.js'; +import type { CommandModule } from "yargs"; +import { disableCommand } from "./extensions/disable.js"; +import { enableCommand } from "./extensions/enable.js"; +import { installCommand } from "./extensions/install.js"; +import { linkCommand } from "./extensions/link.js"; +import { listCommand } from "./extensions/list.js"; +import { newCommand } from "./extensions/new.js"; +import { settingsCommand } from "./extensions/settings.js"; +import { uninstallCommand } from "./extensions/uninstall.js"; +import { updateCommand } from "./extensions/update.js"; export const extensionsCommand: CommandModule = { - command: 'extensions ', - describe: 'Manage AIRIS Code extensions.', + command: "extensions ", + describe: "Manage AIRIS Code extensions.", builder: (yargs) => yargs .command(installCommand) @@ -29,7 +29,7 @@ export const extensionsCommand: CommandModule = { .command(linkCommand) .command(newCommand) .command(settingsCommand) - .demandCommand(1, 'You need at least one command before continuing.') + .demandCommand(1, "You need at least one command before continuing.") .version(false), handler: () => { // This handler is not called when a subcommand is provided. diff --git a/apps/airiscode-cli/src/commands/extensions/consent.ts b/apps/airiscode-cli/src/commands/extensions/consent.ts index 262422ea6..e38fc0578 100644 --- a/apps/airiscode-cli/src/commands/extensions/consent.ts +++ b/apps/airiscode-cli/src/commands/extensions/consent.ts @@ -4,12 +4,12 @@ import type { ExtensionRequestOptions, SkillConfig, SubagentConfig, -} from '@airiscode/core'; -import type { ConfirmationRequest } from '../../ui/types.js'; -import chalk from 'chalk'; -import prompts from 'prompts'; -import { t } from '../../i18n/index.js'; -import { writeStdoutLine } from '../../utils/stdioHelpers.js'; +} from "@airiscode/core"; +import chalk from "chalk"; +import prompts from "prompts"; +import { t } from "../../i18n/index.js"; +import type { ConfirmationRequest } from "../../ui/types.js"; +import { writeStdoutLine } from "../../utils/stdioHelpers.js"; /** * Requests consent from the user to perform an action, by reading a Y/n @@ -20,13 +20,9 @@ import { writeStdoutLine } from '../../utils/stdioHelpers.js'; * @param consentDescription The description of the thing they will be consenting to. * @returns boolean, whether they consented or not. */ -export async function requestConsentNonInteractive( - consentDescription: string, -): Promise { +export async function requestConsentNonInteractive(consentDescription: string): Promise { writeStdoutLine(consentDescription); - const result = await promptForConsentNonInteractive( - t('Do you want to continue? [Y/n]: '), - ); + const result = await promptForConsentNonInteractive(t("Do you want to continue? [Y/n]: ")); return result; } @@ -45,7 +41,7 @@ export async function requestChoicePluginNonInteractive( const plugins = marketplace.plugins; if (plugins.length === 0) { - throw new Error(t('No plugins available in this marketplace.')); + throw new Error(t("No plugins available in this marketplace.")); } // Build choices for prompts select @@ -56,8 +52,8 @@ export async function requestChoicePluginNonInteractive( })); const response = await prompts({ - type: 'select', - name: 'plugin', + type: "select", + name: "plugin", message: t('Select a plugin to install from marketplace "{{name}}":', { name: marketplace.name, }), @@ -67,7 +63,7 @@ export async function requestChoicePluginNonInteractive( // Handle cancellation (Ctrl+C) if (response.plugin === undefined) { - throw new Error(t('Plugin selection cancelled.')); + throw new Error(t("Plugin selection cancelled.")); } return response.plugin; @@ -87,7 +83,7 @@ export async function requestConsentInteractive( addExtensionUpdateConfirmationRequest: (value: ConfirmationRequest) => void, ): Promise { return promptForConsentInteractive( - consentDescription + '\n\n' + t('Do you want to continue?'), + consentDescription + "\n\n" + t("Do you want to continue?"), addExtensionUpdateConfirmationRequest, ); } @@ -100,10 +96,8 @@ export async function requestConsentInteractive( * @param prompt A yes/no prompt to ask the user * @returns Whether or not the user answers 'y' (yes). Defaults to 'yes' on enter. */ -async function promptForConsentNonInteractive( - prompt: string, -): Promise { - const readline = await import('node:readline'); +async function promptForConsentNonInteractive(prompt: string): Promise { + const readline = await import("node:readline"); const rl = readline.createInterface({ input: process.stdin, output: process.stdout, @@ -112,7 +106,7 @@ async function promptForConsentNonInteractive( return new Promise((resolve) => { rl.question(prompt, (answer) => { rl.close(); - resolve(['y', ''].includes(answer.trim().toLowerCase())); + resolve(["y", ""].includes(answer.trim().toLowerCase())); }); }); } @@ -149,70 +143,65 @@ export function extensionConsentString( commands: string[] = [], skills: SkillConfig[] = [], subagents: SubagentConfig[] = [], - originSource: string = 'AirisCode', + originSource: string = "AirisCode", ): string { const output: string[] = []; - if (originSource !== 'AirisCode') { + if (originSource !== "AirisCode") { output.push( t( - 'You are installing an extension from {{originSource}}. Some features may not work perfectly with AIRIS Code.', + "You are installing an extension from {{originSource}}. Some features may not work perfectly with AIRIS Code.", { originSource }, ), ); } const mcpServerEntries = Object.entries(extensionConfig.mcpServers || {}); - output.push( - t('Installing extension "{{name}}".', { name: extensionConfig.name }), - ); + output.push(t('Installing extension "{{name}}".', { name: extensionConfig.name })); output.push( t( - '**Extensions may introduce unexpected behavior. Ensure you have investigated the extension source and trust the author.**', + "**Extensions may introduce unexpected behavior. Ensure you have investigated the extension source and trust the author.**", ), ); if (mcpServerEntries.length) { - output.push(t('This extension will run the following MCP servers:')); + output.push(t("This extension will run the following MCP servers:")); for (const [key, mcpServer] of mcpServerEntries) { const isLocal = !!mcpServer.command; const source = mcpServer.httpUrl ?? - `${mcpServer.command || ''}${mcpServer.args ? ' ' + mcpServer.args.join(' ') : ''}`; - output.push( - ` * ${key} (${isLocal ? t('local') : t('remote')}): ${source}`, - ); + `${mcpServer.command || ""}${mcpServer.args ? " " + mcpServer.args.join(" ") : ""}`; + output.push(` * ${key} (${isLocal ? t("local") : t("remote")}): ${source}`); } } if (commands && commands.length > 0) { output.push( - t('This extension will add the following commands: {{commands}}.', { - commands: commands.join(', '), + t("This extension will add the following commands: {{commands}}.", { + commands: commands.join(", "), }), ); } if (extensionConfig.contextFileName) { const fileName = Array.isArray(extensionConfig.contextFileName) - ? extensionConfig.contextFileName.join(', ') + ? extensionConfig.contextFileName.join(", ") : extensionConfig.contextFileName; output.push( - t( - 'This extension will append info to your AIRISCODE.md context using {{fileName}}', - { fileName }, - ), + t("This extension will append info to your AIRISCODE.md context using {{fileName}}", { + fileName, + }), ); } if (skills.length > 0) { - output.push(t('This extension will install the following skills:')); + output.push(t("This extension will install the following skills:")); for (const skill of skills) { output.push(` * ${chalk.bold(skill.name)}: ${skill.description}`); } } if (subagents.length > 0) { - output.push(t('This extension will install the following subagents:')); + output.push(t("This extension will install the following subagents:")); for (const subagent of subagents) { output.push(` * ${chalk.bold(subagent.name)}: ${subagent.description}`); } } - return output.join('\n'); + return output.join("\n"); } /** @@ -231,7 +220,7 @@ export const requestConsentOrFail = async ( if (!options) return; const { extensionConfig, - originSource = 'AirisCode', + originSource = "AirisCode", commands = [], skills = [], subagents = [], diff --git a/apps/airiscode-cli/src/commands/extensions/disable.ts b/apps/airiscode-cli/src/commands/extensions/disable.ts index f13e3f550..16429f1d1 100644 --- a/apps/airiscode-cli/src/commands/extensions/disable.ts +++ b/apps/airiscode-cli/src/commands/extensions/disable.ts @@ -4,12 +4,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { type CommandModule } from 'yargs'; -import { SettingScope } from '../../config/settings.js'; -import { getErrorMessage } from '../../utils/errors.js'; -import { writeStdoutLine, writeStderrLine } from '../../utils/stdioHelpers.js'; -import { getExtensionManager } from './utils.js'; -import { t } from '../../i18n/index.js'; +import { type CommandModule } from "yargs"; +import { SettingScope } from "../../config/settings.js"; +import { t } from "../../i18n/index.js"; +import { getErrorMessage } from "../../utils/errors.js"; +import { writeStderrLine, writeStdoutLine } from "../../utils/stdioHelpers.js"; +import { getExtensionManager } from "./utils.js"; interface DisableArgs { name: string; @@ -19,7 +19,7 @@ interface DisableArgs { export async function handleDisable(args: DisableArgs) { const extensionManager = await getExtensionManager(); try { - if (args.scope?.toLowerCase() === 'workspace') { + if (args.scope?.toLowerCase() === "workspace") { extensionManager.disableExtension(args.name, SettingScope.Workspace); } else { extensionManager.disableExtension(args.name, SettingScope.User); @@ -37,17 +37,17 @@ export async function handleDisable(args: DisableArgs) { } export const disableCommand: CommandModule = { - command: 'disable [--scope] ', - describe: t('Disables an extension.'), + command: "disable [--scope] ", + describe: t("Disables an extension."), builder: (yargs) => yargs - .positional('name', { - describe: t('The name of the extension to disable.'), - type: 'string', + .positional("name", { + describe: t("The name of the extension to disable."), + type: "string", }) - .option('scope', { - describe: t('The scope to disable the extenison in.'), - type: 'string', + .option("scope", { + describe: t("The scope to disable the extenison in."), + type: "string", default: SettingScope.User, }) .check((argv) => { @@ -58,11 +58,11 @@ export const disableCommand: CommandModule = { .includes((argv.scope as string).toLowerCase()) ) { throw new Error( - t('Invalid scope: {{scope}}. Please use one of {{scopes}}.', { + t("Invalid scope: {{scope}}. Please use one of {{scopes}}.", { scope: argv.scope as string, scopes: Object.values(SettingScope) .map((s) => s.toLowerCase()) - .join(', '), + .join(", "), }), ); } @@ -70,8 +70,8 @@ export const disableCommand: CommandModule = { }), handler: async (argv) => { await handleDisable({ - name: argv['name'] as string, - scope: argv['scope'] as string, + name: argv["name"] as string, + scope: argv["scope"] as string, }); }, }; diff --git a/apps/airiscode-cli/src/commands/extensions/enable.ts b/apps/airiscode-cli/src/commands/extensions/enable.ts index 33e8bfba9..9e1747335 100644 --- a/apps/airiscode-cli/src/commands/extensions/enable.ts +++ b/apps/airiscode-cli/src/commands/extensions/enable.ts @@ -4,12 +4,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { type CommandModule } from 'yargs'; -import { FatalConfigError, getErrorMessage } from '@airiscode/core'; -import { SettingScope } from '../../config/settings.js'; -import { writeStdoutLine } from '../../utils/stdioHelpers.js'; -import { getExtensionManager } from './utils.js'; -import { t } from '../../i18n/index.js'; +import { FatalConfigError, getErrorMessage } from "@airiscode/core"; +import { type CommandModule } from "yargs"; +import { SettingScope } from "../../config/settings.js"; +import { t } from "../../i18n/index.js"; +import { writeStdoutLine } from "../../utils/stdioHelpers.js"; +import { getExtensionManager } from "./utils.js"; interface EnableArgs { name: string; @@ -20,7 +20,7 @@ export async function handleEnable(args: EnableArgs) { const extensionManager = await getExtensionManager(); try { - if (args.scope?.toLowerCase() === 'workspace') { + if (args.scope?.toLowerCase() === "workspace") { extensionManager.enableExtension(args.name, SettingScope.Workspace); } else { extensionManager.enableExtension(args.name, SettingScope.User); @@ -45,19 +45,19 @@ export async function handleEnable(args: EnableArgs) { } export const enableCommand: CommandModule = { - command: 'enable [--scope] ', - describe: t('Enables an extension.'), + command: "enable [--scope] ", + describe: t("Enables an extension."), builder: (yargs) => yargs - .positional('name', { - describe: t('The name of the extension to enable.'), - type: 'string', + .positional("name", { + describe: t("The name of the extension to enable."), + type: "string", }) - .option('scope', { + .option("scope", { describe: t( - 'The scope to enable the extenison in. If not set, will be enabled in all scopes.', + "The scope to enable the extenison in. If not set, will be enabled in all scopes.", ), - type: 'string', + type: "string", }) .check((argv) => { if ( @@ -67,11 +67,11 @@ export const enableCommand: CommandModule = { .includes((argv.scope as string).toLowerCase()) ) { throw new Error( - t('Invalid scope: {{scope}}. Please use one of {{scopes}}.', { + t("Invalid scope: {{scope}}. Please use one of {{scopes}}.", { scope: argv.scope as string, scopes: Object.values(SettingScope) .map((s) => s.toLowerCase()) - .join(', '), + .join(", "), }), ); } @@ -79,8 +79,8 @@ export const enableCommand: CommandModule = { }), handler: async (argv) => { await handleEnable({ - name: argv['name'] as string, - scope: argv['scope'] as string, + name: argv["name"] as string, + scope: argv["scope"] as string, }); }, }; diff --git a/apps/airiscode-cli/src/commands/extensions/examples/mcp-server/example.ts b/apps/airiscode-cli/src/commands/extensions/examples/mcp-server/example.ts index 21e01e17c..c946453e6 100644 --- a/apps/airiscode-cli/src/commands/extensions/examples/mcp-server/example.ts +++ b/apps/airiscode-cli/src/commands/extensions/examples/mcp-server/example.ts @@ -4,31 +4,29 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; -import { z } from 'zod'; +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { z } from "zod"; const server = new McpServer({ - name: 'prompt-server', - version: '1.0.0', + name: "prompt-server", + version: "1.0.0", }); server.registerTool( - 'fetch_posts', + "fetch_posts", { - description: 'Fetches a list of posts from a public API.', + description: "Fetches a list of posts from a public API.", inputSchema: z.object({}).shape, }, async () => { - const apiResponse = await fetch( - 'https://jsonplaceholder.typicode.com/posts', - ); + const apiResponse = await fetch("https://jsonplaceholder.typicode.com/posts"); const posts = await apiResponse.json(); const response = { posts: posts.slice(0, 5) }; return { content: [ { - type: 'text', + type: "text", text: JSON.stringify(response), }, ], @@ -37,19 +35,19 @@ server.registerTool( ); server.registerPrompt( - 'poem-writer', + "poem-writer", { - title: 'Poem Writer', - description: 'Write a nice haiku', + title: "Poem Writer", + description: "Write a nice haiku", argsSchema: { title: z.string(), mood: z.string().optional() }, }, ({ title, mood }) => ({ messages: [ { - role: 'user', + role: "user", content: { - type: 'text', - text: `Write a haiku${mood ? ` with the mood ${mood}` : ''} called ${title}. Note that a haiku is 5 syllables followed by 7 syllables followed by 5 syllables `, + type: "text", + text: `Write a haiku${mood ? ` with the mood ${mood}` : ""} called ${title}. Note that a haiku is 5 syllables followed by 7 syllables followed by 5 syllables `, }, }, ], diff --git a/apps/airiscode-cli/src/commands/extensions/install.ts b/apps/airiscode-cli/src/commands/extensions/install.ts index bfffce698..3087f70a7 100644 --- a/apps/airiscode-cli/src/commands/extensions/install.ts +++ b/apps/airiscode-cli/src/commands/extensions/install.ts @@ -4,22 +4,18 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { CommandModule } from 'yargs'; - -import { - ExtensionManager, - parseInstallSource, -} from '@airiscode/core'; -import { getErrorMessage } from '../../utils/errors.js'; -import { writeStdoutLine, writeStderrLine } from '../../utils/stdioHelpers.js'; -import { isWorkspaceTrusted } from '../../config/trustedFolders.js'; -import { loadSettings } from '../../config/settings.js'; +import { ExtensionManager, parseInstallSource } from "@airiscode/core"; +import type { CommandModule } from "yargs"; +import { loadSettings } from "../../config/settings.js"; +import { isWorkspaceTrusted } from "../../config/trustedFolders.js"; +import { t } from "../../i18n/index.js"; +import { getErrorMessage } from "../../utils/errors.js"; +import { writeStderrLine, writeStdoutLine } from "../../utils/stdioHelpers.js"; import { - requestConsentOrFail, - requestConsentNonInteractive, requestChoicePluginNonInteractive, -} from './consent.js'; -import { t } from '../../i18n/index.js'; + requestConsentNonInteractive, + requestConsentOrFail, +} from "./consent.js"; interface InstallArgs { source: string; @@ -35,32 +31,30 @@ export async function handleInstall(args: InstallArgs) { const installMetadata = await parseInstallSource(args.source); if ( - installMetadata.type !== 'git' && - installMetadata.type !== 'github-release' && - installMetadata.type !== 'npm' + installMetadata.type !== "git" && + installMetadata.type !== "github-release" && + installMetadata.type !== "npm" ) { if (args.ref || args.autoUpdate) { throw new Error( - t( - '--ref and --auto-update are not applicable for marketplace extensions.', - ), + t("--ref and --auto-update are not applicable for marketplace extensions."), ); } } - if (installMetadata.type === 'npm' && args.ref) { + if (installMetadata.type === "npm" && args.ref) { throw new Error( t( - '--ref is not applicable for npm extensions. Use @version suffix instead (e.g. @scope/package@1.2.0).', + "--ref is not applicable for npm extensions. Use @version suffix instead (e.g. @scope/package@1.2.0).", ), ); } - if (installMetadata.type !== 'npm' && args.registry) { - throw new Error(t('--registry is only applicable for npm extensions.')); + if (installMetadata.type !== "npm" && args.registry) { + throw new Error(t("--registry is only applicable for npm extensions.")); } - if (installMetadata.type === 'npm' && args.registry) { + if (installMetadata.type === "npm" && args.registry) { installMetadata.registryUrl = args.registry; } @@ -70,9 +64,7 @@ export async function handleInstall(args: InstallArgs) { const workspaceDir = process.cwd(); const extensionManager = new ExtensionManager({ workspaceDir, - isWorkspaceTrusted: !!isWorkspaceTrusted( - loadSettings(workspaceDir).merged, - ), + isWorkspaceTrusted: !!isWorkspaceTrusted(loadSettings(workspaceDir).merged), requestConsent, requestChoicePlugin: requestChoicePluginNonInteractive, }); @@ -99,56 +91,56 @@ export async function handleInstall(args: InstallArgs) { } export const installCommand: CommandModule = { - command: 'install ', + command: "install ", describe: t( - 'Installs an extension from a git repository URL, local path, scoped npm package (@scope/name), or claude marketplace (marketplace-url:plugin-name).', + "Installs an extension from a git repository URL, local path, scoped npm package (@scope/name), or claude marketplace (marketplace-url:plugin-name).", ), builder: (yargs) => yargs - .positional('source', { + .positional("source", { describe: t( - 'The github URL, local path, or marketplace source (marketplace-url:plugin-name) of the extension to install.', + "The github URL, local path, or marketplace source (marketplace-url:plugin-name) of the extension to install.", ), - type: 'string', + type: "string", demandOption: true, }) - .option('ref', { - describe: t('The git ref to install from.'), - type: 'string', + .option("ref", { + describe: t("The git ref to install from."), + type: "string", }) - .option('auto-update', { - describe: t('Enable auto-update for this extension.'), - type: 'boolean', + .option("auto-update", { + describe: t("Enable auto-update for this extension."), + type: "boolean", }) - .option('pre-release', { - describe: t('Enable pre-release versions for this extension.'), - type: 'boolean', + .option("pre-release", { + describe: t("Enable pre-release versions for this extension."), + type: "boolean", }) - .option('registry', { - describe: t('Custom npm registry URL (only for npm extensions).'), - type: 'string', + .option("registry", { + describe: t("Custom npm registry URL (only for npm extensions)."), + type: "string", }) - .option('consent', { + .option("consent", { describe: t( - 'Acknowledge the security risks of installing an extension and skip the confirmation prompt.', + "Acknowledge the security risks of installing an extension and skip the confirmation prompt.", ), - type: 'boolean', + type: "boolean", default: false, }) .check((argv) => { if (!argv.source) { - throw new Error(t('The source argument must be provided.')); + throw new Error(t("The source argument must be provided.")); } return true; }), handler: async (argv) => { await handleInstall({ - source: argv['source'] as string, - ref: argv['ref'] as string | undefined, - autoUpdate: argv['auto-update'] as boolean | undefined, - allowPreRelease: argv['pre-release'] as boolean | undefined, - consent: argv['consent'] as boolean | undefined, - registry: argv['registry'] as string | undefined, + source: argv["source"] as string, + ref: argv["ref"] as string | undefined, + autoUpdate: argv["auto-update"] as boolean | undefined, + allowPreRelease: argv["pre-release"] as boolean | undefined, + consent: argv["consent"] as boolean | undefined, + registry: argv["registry"] as string | undefined, }); }, }; diff --git a/apps/airiscode-cli/src/commands/extensions/link.ts b/apps/airiscode-cli/src/commands/extensions/link.ts index 484812b0a..cc65178c5 100644 --- a/apps/airiscode-cli/src/commands/extensions/link.ts +++ b/apps/airiscode-cli/src/commands/extensions/link.ts @@ -4,16 +4,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { CommandModule } from 'yargs'; -import { type ExtensionInstallMetadata } from '@airiscode/core'; -import { getErrorMessage } from '../../utils/errors.js'; -import { writeStdoutLine, writeStderrLine } from '../../utils/stdioHelpers.js'; -import { - requestConsentNonInteractive, - requestConsentOrFail, -} from './consent.js'; -import { getExtensionManager } from './utils.js'; -import { t } from '../../i18n/index.js'; +import { type ExtensionInstallMetadata } from "@airiscode/core"; +import type { CommandModule } from "yargs"; +import { t } from "../../i18n/index.js"; +import { getErrorMessage } from "../../utils/errors.js"; +import { writeStderrLine, writeStdoutLine } from "../../utils/stdioHelpers.js"; +import { requestConsentNonInteractive, requestConsentOrFail } from "./consent.js"; +import { getExtensionManager } from "./utils.js"; interface InstallArgs { path: string; @@ -23,7 +20,7 @@ export async function handleLink(args: InstallArgs) { try { const installMetadata: ExtensionInstallMetadata = { source: args.path, - type: 'link', + type: "link", }; const extensionManager = await getExtensionManager(); @@ -32,7 +29,7 @@ export async function handleLink(args: InstallArgs) { requestConsentOrFail.bind(null, requestConsentNonInteractive), ); if (!extension) { - writeStdoutLine(t('Link extension failed to install.')); + writeStdoutLine(t("Link extension failed to install.")); return; } writeStdoutLine( @@ -47,20 +44,20 @@ export async function handleLink(args: InstallArgs) { } export const linkCommand: CommandModule = { - command: 'link ', + command: "link ", describe: t( - 'Links an extension from a local path. Updates made to the local path will always be reflected.', + "Links an extension from a local path. Updates made to the local path will always be reflected.", ), builder: (yargs) => yargs - .positional('path', { - describe: t('The name of the extension to link.'), - type: 'string', + .positional("path", { + describe: t("The name of the extension to link."), + type: "string", }) .check((_) => true), handler: async (argv) => { await handleLink({ - path: argv['path'] as string, + path: argv["path"] as string, }); }, }; diff --git a/apps/airiscode-cli/src/commands/extensions/list.ts b/apps/airiscode-cli/src/commands/extensions/list.ts index 4444fba67..657b5fb54 100644 --- a/apps/airiscode-cli/src/commands/extensions/list.ts +++ b/apps/airiscode-cli/src/commands/extensions/list.ts @@ -4,11 +4,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { CommandModule } from 'yargs'; -import { getErrorMessage } from '../../utils/errors.js'; -import { writeStdoutLine, writeStderrLine } from '../../utils/stdioHelpers.js'; -import { extensionToOutputString, getExtensionManager } from './utils.js'; -import { t } from '../../i18n/index.js'; +import type { CommandModule } from "yargs"; +import { t } from "../../i18n/index.js"; +import { getErrorMessage } from "../../utils/errors.js"; +import { writeStderrLine, writeStdoutLine } from "../../utils/stdioHelpers.js"; +import { extensionToOutputString, getExtensionManager } from "./utils.js"; export async function handleList() { try { @@ -16,7 +16,7 @@ export async function handleList() { const extensions = extensionManager.getLoadedExtensions(); if (!extensions || extensions.length === 0) { - writeStdoutLine(t('No extensions installed.')); + writeStdoutLine(t("No extensions installed.")); return; } writeStdoutLine( @@ -24,7 +24,7 @@ export async function handleList() { .map((extension, _): string => extensionToOutputString(extension, extensionManager, process.cwd()), ) - .join('\n\n'), + .join("\n\n"), ); } catch (error) { writeStderrLine(getErrorMessage(error)); @@ -33,8 +33,8 @@ export async function handleList() { } export const listCommand: CommandModule = { - command: 'list', - describe: t('Lists installed extensions.'), + command: "list", + describe: t("Lists installed extensions."), builder: (yargs) => yargs, handler: async () => { await handleList(); diff --git a/apps/airiscode-cli/src/commands/extensions/new.ts b/apps/airiscode-cli/src/commands/extensions/new.ts index f47648ab9..b70157e73 100644 --- a/apps/airiscode-cli/src/commands/extensions/new.ts +++ b/apps/airiscode-cli/src/commands/extensions/new.ts @@ -4,12 +4,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { access, cp, mkdir, readdir, writeFile } from 'node:fs/promises'; -import { join, dirname, basename } from 'node:path'; -import type { CommandModule } from 'yargs'; -import { fileURLToPath } from 'node:url'; -import { getErrorMessage } from '../../utils/errors.js'; -import { writeStdoutLine, writeStderrLine } from '../../utils/stdioHelpers.js'; +import { access, cp, mkdir, readdir, writeFile } from "node:fs/promises"; +import { basename, dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import type { CommandModule } from "yargs"; +import { getErrorMessage } from "../../utils/errors.js"; +import { writeStderrLine, writeStdoutLine } from "../../utils/stdioHelpers.js"; interface NewArgs { path: string; @@ -19,7 +19,7 @@ interface NewArgs { const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); -const EXAMPLES_PATH = join(__dirname, 'examples'); +const EXAMPLES_PATH = join(__dirname, "examples"); async function pathExists(path: string) { try { @@ -61,12 +61,9 @@ async function handleNew(args: NewArgs) { const extensionName = basename(args.path); const manifest = { name: extensionName, - version: '1.0.0', + version: "1.0.0", }; - await writeFile( - join(args.path, 'qwen-extension.json'), - JSON.stringify(manifest, null, 2), - ); + await writeFile(join(args.path, "qwen-extension.json"), JSON.stringify(manifest, null, 2)); writeStdoutLine(`Successfully created new extension at ${args.path}.`); } writeStdoutLine( @@ -80,31 +77,29 @@ async function handleNew(args: NewArgs) { async function getBoilerplateChoices() { const entries = await readdir(EXAMPLES_PATH, { withFileTypes: true }); - return entries - .filter((entry) => entry.isDirectory()) - .map((entry) => entry.name); + return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name); } export const newCommand: CommandModule = { - command: 'new [template]', - describe: 'Create a new extension from a boilerplate example.', + command: "new [template]", + describe: "Create a new extension from a boilerplate example.", builder: async (yargs) => { const choices = await getBoilerplateChoices(); return yargs - .positional('path', { - describe: 'The path to create the extension in.', - type: 'string', + .positional("path", { + describe: "The path to create the extension in.", + type: "string", }) - .positional('template', { - describe: 'The boilerplate template to use.', - type: 'string', + .positional("template", { + describe: "The boilerplate template to use.", + type: "string", choices, }); }, handler: async (args) => { await handleNew({ - path: args['path'] as string, - template: args['template'] as string | undefined, + path: args["path"] as string, + template: args["template"] as string | undefined, }); }, }; diff --git a/apps/airiscode-cli/src/commands/extensions/settings.ts b/apps/airiscode-cli/src/commands/extensions/settings.ts index bbc22b2f2..abb3ada75 100644 --- a/apps/airiscode-cli/src/commands/extensions/settings.ts +++ b/apps/airiscode-cli/src/commands/extensions/settings.ts @@ -4,16 +4,16 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { CommandModule } from 'yargs'; -import { getExtensionManager } from './utils.js'; import { ExtensionSettingScope, getScopedEnvContents, promptForSetting, updateSetting, -} from '@airiscode/core'; -import { t } from '../../i18n/index.js'; -import { writeStdoutLine } from '../../utils/stdioHelpers.js'; +} from "@airiscode/core"; +import type { CommandModule } from "yargs"; +import { t } from "../../i18n/index.js"; +import { writeStdoutLine } from "../../utils/stdioHelpers.js"; +import { getExtensionManager } from "./utils.js"; // --- SET COMMAND --- interface SetArgs { @@ -23,25 +23,25 @@ interface SetArgs { } const setCommand: CommandModule = { - command: 'set [--scope] ', - describe: t('Set a specific setting for an extension.'), + command: "set [--scope] ", + describe: t("Set a specific setting for an extension."), builder: (yargs) => yargs - .positional('name', { - describe: t('Name of the extension to configure.'), - type: 'string', + .positional("name", { + describe: t("Name of the extension to configure."), + type: "string", demandOption: true, }) - .positional('setting', { - describe: t('The setting to configure (name or env var).'), - type: 'string', + .positional("setting", { + describe: t("The setting to configure (name or env var)."), + type: "string", demandOption: true, }) - .option('scope', { - describe: t('The scope to set the setting in.'), - type: 'string', - choices: ['user', 'workspace'], - default: 'user', + .option("scope", { + describe: t("The scope to set the setting in."), + type: "string", + choices: ["user", "workspace"], + default: "user", }), handler: async (args) => { const { name, setting, scope } = args; @@ -70,12 +70,12 @@ interface ListArgs { } const listCommand: CommandModule = { - command: 'list ', - describe: t('List all settings for an extension.'), + command: "list ", + describe: t("List all settings for an extension."), builder: (yargs) => - yargs.positional('name', { - describe: t('Name of the extension.'), - type: 'string', + yargs.positional("name", { + describe: t("Name of the extension."), + type: "string", demandOption: true, }), handler: async (args) => { @@ -90,9 +90,7 @@ const listCommand: CommandModule = { return; } if (!extension || !extension.settings || extension.settings.length === 0) { - writeStdoutLine( - t('Extension "{{name}}" has no settings to configure.', { name }), - ); + writeStdoutLine(t('Extension "{{name}}" has no settings to configure.', { name })); return; } @@ -112,38 +110,38 @@ const listCommand: CommandModule = { for (const setting of extension.settings) { const value = mergedSettings[setting.envVar]; let displayValue: string; - let scopeInfo = ''; + let scopeInfo = ""; if (workspaceSettings[setting.envVar] !== undefined) { - scopeInfo = ' ' + t('(workspace)'); + scopeInfo = " " + t("(workspace)"); } else if (userSettings[setting.envVar] !== undefined) { - scopeInfo = ' ' + t('(user)'); + scopeInfo = " " + t("(user)"); } if (value === undefined) { - displayValue = t('[not set]'); + displayValue = t("[not set]"); } else if (setting.sensitive) { - displayValue = t('[value stored in keychain]'); + displayValue = t("[value stored in keychain]"); } else { displayValue = value; } writeStdoutLine(` - ${setting.name} (${setting.envVar})`); - writeStdoutLine(` ${t('Description:')} ${setting.description}`); - writeStdoutLine(` ${t('Value:')} ${displayValue}${scopeInfo}`); + writeStdoutLine(` ${t("Description:")} ${setting.description}`); + writeStdoutLine(` ${t("Value:")} ${displayValue}${scopeInfo}`); } }, }; // --- SETTINGS COMMAND --- export const settingsCommand: CommandModule = { - command: 'settings ', - describe: t('Manage extension settings.'), + command: "settings ", + describe: t("Manage extension settings."), builder: (yargs) => yargs .command(setCommand) .command(listCommand) - .demandCommand(1, t('You need to specify a command (set or list).')) + .demandCommand(1, t("You need to specify a command (set or list).")) .version(false), handler: () => { // This handler is not called when a subcommand is provided. diff --git a/apps/airiscode-cli/src/commands/extensions/uninstall.ts b/apps/airiscode-cli/src/commands/extensions/uninstall.ts index 0450f4d69..fb385be66 100644 --- a/apps/airiscode-cli/src/commands/extensions/uninstall.ts +++ b/apps/airiscode-cli/src/commands/extensions/uninstall.ts @@ -4,17 +4,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { CommandModule } from 'yargs'; -import { getErrorMessage } from '../../utils/errors.js'; -import { writeStdoutLine, writeStderrLine } from '../../utils/stdioHelpers.js'; -import { ExtensionManager } from '@airiscode/core'; -import { - requestConsentNonInteractive, - requestConsentOrFail, -} from './consent.js'; -import { isWorkspaceTrusted } from '../../config/trustedFolders.js'; -import { loadSettings } from '../../config/settings.js'; -import { t } from '../../i18n/index.js'; +import { ExtensionManager } from "@airiscode/core"; +import type { CommandModule } from "yargs"; +import { loadSettings } from "../../config/settings.js"; +import { isWorkspaceTrusted } from "../../config/trustedFolders.js"; +import { t } from "../../i18n/index.js"; +import { getErrorMessage } from "../../utils/errors.js"; +import { writeStderrLine, writeStdoutLine } from "../../utils/stdioHelpers.js"; +import { requestConsentNonInteractive, requestConsentOrFail } from "./consent.js"; interface UninstallArgs { name: string; // can be extension name or source URL. @@ -25,19 +22,12 @@ export async function handleUninstall(args: UninstallArgs) { const workspaceDir = process.cwd(); const extensionManager = new ExtensionManager({ workspaceDir, - requestConsent: requestConsentOrFail.bind( - null, - requestConsentNonInteractive, - ), - isWorkspaceTrusted: !!isWorkspaceTrusted( - loadSettings(workspaceDir).merged, - ), + requestConsent: requestConsentOrFail.bind(null, requestConsentNonInteractive), + isWorkspaceTrusted: !!isWorkspaceTrusted(loadSettings(workspaceDir).merged), }); await extensionManager.refreshCache(); await extensionManager.uninstallExtension(args.name, false); - writeStdoutLine( - t('Extension "{{name}}" successfully uninstalled.', { name: args.name }), - ); + writeStdoutLine(t('Extension "{{name}}" successfully uninstalled.', { name: args.name })); } catch (error) { writeStderrLine(getErrorMessage(error)); process.exit(1); @@ -45,27 +35,25 @@ export async function handleUninstall(args: UninstallArgs) { } export const uninstallCommand: CommandModule = { - command: 'uninstall ', - describe: t('Uninstalls an extension.'), + command: "uninstall ", + describe: t("Uninstalls an extension."), builder: (yargs) => yargs - .positional('name', { - describe: t('The name or source path of the extension to uninstall.'), - type: 'string', + .positional("name", { + describe: t("The name or source path of the extension to uninstall."), + type: "string", }) .check((argv) => { if (!argv.name) { throw new Error( - t( - 'Please include the name of the extension to uninstall as a positional argument.', - ), + t("Please include the name of the extension to uninstall as a positional argument."), ); } return true; }), handler: async (argv) => { await handleUninstall({ - name: argv['name'] as string, + name: argv["name"] as string, }); }, }; diff --git a/apps/airiscode-cli/src/commands/extensions/update.ts b/apps/airiscode-cli/src/commands/extensions/update.ts index 17d5a1585..8f7ad88fb 100644 --- a/apps/airiscode-cli/src/commands/extensions/update.ts +++ b/apps/airiscode-cli/src/commands/extensions/update.ts @@ -4,16 +4,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { CommandModule } from 'yargs'; -import { getErrorMessage } from '../../utils/errors.js'; -import { writeStdoutLine, writeStderrLine } from '../../utils/stdioHelpers.js'; -import { ExtensionUpdateState } from '../../ui/state/extensions.js'; -import { - checkForExtensionUpdate, - type ExtensionUpdateInfo, -} from '@airiscode/core'; -import { getExtensionManager } from './utils.js'; -import { t } from '../../i18n/index.js'; +import { checkForExtensionUpdate, type ExtensionUpdateInfo } from "@airiscode/core"; +import type { CommandModule } from "yargs"; +import { t } from "../../i18n/index.js"; +import { ExtensionUpdateState } from "../../ui/state/extensions.js"; +import { getErrorMessage } from "../../utils/errors.js"; +import { writeStderrLine, writeStdoutLine } from "../../utils/stdioHelpers.js"; +import { getExtensionManager } from "./utils.js"; interface UpdateArgs { name?: string; @@ -21,14 +18,11 @@ interface UpdateArgs { } const updateOutput = (info: ExtensionUpdateInfo) => - t( - 'Extension "{{name}}" successfully updated: {{oldVersion}} → {{newVersion}}.', - { - name: info.name, - oldVersion: info.originalVersion, - newVersion: info.updatedVersion, - }, - ); + t('Extension "{{name}}" successfully updated: {{oldVersion}} → {{newVersion}}.', { + name: info.name, + oldVersion: info.originalVersion, + newVersion: info.updatedVersion, + }); export async function handleUpdate(args: UpdateArgs) { const extensionManager = await getExtensionManager(); @@ -36,32 +30,22 @@ export async function handleUpdate(args: UpdateArgs) { if (args.name) { try { - const extension = extensions.find( - (extension) => extension.name === args.name, - ); + const extension = extensions.find((extension) => extension.name === args.name); if (!extension) { - writeStdoutLine( - t('Extension "{{name}}" not found.', { name: args.name }), - ); + writeStdoutLine(t('Extension "{{name}}" not found.', { name: args.name })); return; } if (!extension.installMetadata) { writeStdoutLine( - t( - 'Unable to install extension "{{name}}" due to missing install metadata', - { name: args.name }, - ), + t('Unable to install extension "{{name}}" due to missing install metadata', { + name: args.name, + }), ); return; } - const updateState = await checkForExtensionUpdate( - extension, - extensionManager, - ); + const updateState = await checkForExtensionUpdate(extension, extensionManager); if (updateState !== ExtensionUpdateState.UPDATE_AVAILABLE) { - writeStdoutLine( - t('Extension "{{name}}" is already up to date.', { name: args.name }), - ); + writeStdoutLine(t('Extension "{{name}}" is already up to date.', { name: args.name })); return; } // TODO(chrstnb): we should list extensions if the requested extension is not installed. @@ -70,24 +54,16 @@ export async function handleUpdate(args: UpdateArgs) { updateState, () => {}, ))!; - if ( - updatedExtensionInfo.originalVersion !== - updatedExtensionInfo.updatedVersion - ) { + if (updatedExtensionInfo.originalVersion !== updatedExtensionInfo.updatedVersion) { writeStdoutLine( - t( - 'Extension "{{name}}" successfully updated: {{oldVersion}} → {{newVersion}}.', - { - name: args.name, - oldVersion: updatedExtensionInfo.originalVersion, - newVersion: updatedExtensionInfo.updatedVersion, - }, - ), + t('Extension "{{name}}" successfully updated: {{oldVersion}} → {{newVersion}}.', { + name: args.name, + oldVersion: updatedExtensionInfo.originalVersion, + newVersion: updatedExtensionInfo.updatedVersion, + }), ); } else { - writeStdoutLine( - t('Extension "{{name}}" is already up to date.', { name: args.name }), - ); + writeStdoutLine(t('Extension "{{name}}" is already up to date.', { name: args.name })); } } catch (error) { writeStderrLine(getErrorMessage(error)); @@ -96,26 +72,22 @@ export async function handleUpdate(args: UpdateArgs) { if (args.all) { try { const extensionState = new Map(); - await extensionManager.checkForAllExtensionUpdates( - (extensionName, state) => { - extensionState.set(extensionName, { - status: state, - processed: true, // No need to process as we will force the update. - }); - }, - ); + await extensionManager.checkForAllExtensionUpdates((extensionName, state) => { + extensionState.set(extensionName, { + status: state, + processed: true, // No need to process as we will force the update. + }); + }); let updateInfos = await extensionManager.updateAllUpdatableExtensions( extensionState, () => {}, ); - updateInfos = updateInfos.filter( - (info) => info.originalVersion !== info.updatedVersion, - ); + updateInfos = updateInfos.filter((info) => info.originalVersion !== info.updatedVersion); if (updateInfos.length === 0) { - writeStdoutLine(t('No extensions to update.')); + writeStdoutLine(t("No extensions to update.")); return; } - writeStdoutLine(updateInfos.map((info) => updateOutput(info)).join('\n')); + writeStdoutLine(updateInfos.map((info) => updateOutput(info)).join("\n")); } catch (error) { writeStderrLine(getErrorMessage(error)); } @@ -123,33 +95,29 @@ export async function handleUpdate(args: UpdateArgs) { } export const updateCommand: CommandModule = { - command: 'update [] [--all]', - describe: t( - 'Updates all extensions or a named extension to the latest version.', - ), + command: "update [] [--all]", + describe: t("Updates all extensions or a named extension to the latest version."), builder: (yargs) => yargs - .positional('name', { - describe: t('The name of the extension to update.'), - type: 'string', + .positional("name", { + describe: t("The name of the extension to update."), + type: "string", }) - .option('all', { - describe: t('Update all extensions.'), - type: 'boolean', + .option("all", { + describe: t("Update all extensions."), + type: "boolean", }) - .conflicts('name', 'all') + .conflicts("name", "all") .check((argv) => { if (!argv.all && !argv.name) { - throw new Error( - t('Either an extension name or --all must be provided'), - ); + throw new Error(t("Either an extension name or --all must be provided")); } return true; }), handler: async (argv) => { await handleUpdate({ - name: argv['name'] as string | undefined, - all: argv['all'] as boolean | undefined, + name: argv["name"] as string | undefined, + all: argv["all"] as boolean | undefined, }); }, }; diff --git a/apps/airiscode-cli/src/commands/extensions/utils.ts b/apps/airiscode-cli/src/commands/extensions/utils.ts index 7b80908d3..8b67cfe60 100644 --- a/apps/airiscode-cli/src/commands/extensions/utils.ts +++ b/apps/airiscode-cli/src/commands/extensions/utils.ts @@ -4,26 +4,23 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ExtensionManager, type Extension } from '@airiscode/core'; -import { loadSettings } from '../../config/settings.js'; +import * as os from "node:os"; +import { type Extension, ExtensionManager } from "@airiscode/core"; +import chalk from "chalk"; +import { loadSettings } from "../../config/settings.js"; +import { isWorkspaceTrusted } from "../../config/trustedFolders.js"; +import { t } from "../../i18n/index.js"; import { - requestConsentOrFail, - requestConsentNonInteractive, requestChoicePluginNonInteractive, -} from './consent.js'; -import { isWorkspaceTrusted } from '../../config/trustedFolders.js'; -import * as os from 'node:os'; -import chalk from 'chalk'; -import { t } from '../../i18n/index.js'; + requestConsentNonInteractive, + requestConsentOrFail, +} from "./consent.js"; export async function getExtensionManager(): Promise { const workspaceDir = process.cwd(); const extensionManager = new ExtensionManager({ workspaceDir, - requestConsent: requestConsentOrFail.bind( - null, - requestConsentNonInteractive, - ), + requestConsent: requestConsentOrFail.bind(null, requestConsentNonInteractive), requestChoicePlugin: requestChoicePluginNonInteractive, isWorkspaceTrusted: !!isWorkspaceTrusted(loadSettings(workspaceDir).merged), }); @@ -38,55 +35,49 @@ export function extensionToOutputString( inline = false, ): string { const cwd = workspaceDir; - const userEnabled = extensionManager.isEnabled( - extension.config.name, - os.homedir(), - ); - const workspaceEnabled = extensionManager.isEnabled( - extension.config.name, - cwd, - ); + const userEnabled = extensionManager.isEnabled(extension.config.name, os.homedir()); + const workspaceEnabled = extensionManager.isEnabled(extension.config.name, cwd); - const status = workspaceEnabled ? chalk.green('✓') : chalk.red('✗'); - let output = `${inline ? '' : status} ${extension.config.name} (${extension.config.version})`; - output += `\n ${t('Path:')} ${extension.path}`; + const status = workspaceEnabled ? chalk.green("✓") : chalk.red("✗"); + let output = `${inline ? "" : status} ${extension.config.name} (${extension.config.version})`; + output += `\n ${t("Path:")} ${extension.path}`; if (extension.installMetadata) { - output += `\n ${t('Source:')} ${extension.installMetadata.source} (${t('Type:')} ${extension.installMetadata.type})`; + output += `\n ${t("Source:")} ${extension.installMetadata.source} (${t("Type:")} ${extension.installMetadata.type})`; if (extension.installMetadata.ref) { - output += `\n ${t('Ref:')} ${extension.installMetadata.ref}`; + output += `\n ${t("Ref:")} ${extension.installMetadata.ref}`; } if (extension.installMetadata.releaseTag) { - output += `\n ${t('Release tag:')} ${extension.installMetadata.releaseTag}`; + output += `\n ${t("Release tag:")} ${extension.installMetadata.releaseTag}`; } } - output += `\n ${t('Enabled (User):')} ${userEnabled}`; - output += `\n ${t('Enabled (Workspace):')} ${workspaceEnabled}`; + output += `\n ${t("Enabled (User):")} ${userEnabled}`; + output += `\n ${t("Enabled (Workspace):")} ${workspaceEnabled}`; if (extension.contextFiles.length > 0) { - output += `\n ${t('Context files:')}`; + output += `\n ${t("Context files:")}`; extension.contextFiles.forEach((contextFile) => { output += `\n ${contextFile}`; }); } if (extension.commands && extension.commands.length > 0) { - output += `\n ${t('Commands:')}`; + output += `\n ${t("Commands:")}`; extension.commands.forEach((command) => { output += `\n /${command}`; }); } if (extension.skills && extension.skills.length > 0) { - output += `\n ${t('Skills:')}`; + output += `\n ${t("Skills:")}`; extension.skills.forEach((skill) => { output += `\n ${skill.name}`; }); } if (extension.agents && extension.agents.length > 0) { - output += `\n ${t('Agents:')}`; + output += `\n ${t("Agents:")}`; extension.agents.forEach((agent) => { output += `\n ${agent.name}`; }); } if (extension.config.mcpServers) { - output += `\n ${t('MCP servers:')}`; + output += `\n ${t("MCP servers:")}`; Object.keys(extension.config.mcpServers).forEach((key) => { output += `\n ${key}`; }); diff --git a/apps/airiscode-cli/src/commands/hooks.tsx b/apps/airiscode-cli/src/commands/hooks.tsx index 7dc64ee7a..1e525956e 100644 --- a/apps/airiscode-cli/src/commands/hooks.tsx +++ b/apps/airiscode-cli/src/commands/hooks.tsx @@ -4,22 +4,20 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { CommandModule } from 'yargs'; -import { createDebugLogger } from '@airiscode/core'; +import { createDebugLogger } from "@airiscode/core"; +import type { CommandModule } from "yargs"; -const debugLogger = createDebugLogger('HOOKS_UI'); +const debugLogger = createDebugLogger("HOOKS_UI"); export const hooksCommand: CommandModule = { - command: 'hooks', - aliases: ['hook'], - describe: 'Manage AIRIS Code hooks (use /hooks in interactive mode).', + command: "hooks", + aliases: ["hook"], + describe: "Manage AIRIS Code hooks (use /hooks in interactive mode).", builder: (yargs) => yargs.version(false).help(false), handler: () => { // In CLI mode, this command is not interactive. // Users should use /hooks in interactive mode for the full UI experience. - debugLogger.debug( - 'Use /hooks in interactive mode to manage hooks with the UI.', - ); + debugLogger.debug("Use /hooks in interactive mode to manage hooks with the UI."); process.exit(0); }, }; diff --git a/apps/airiscode-cli/src/commands/mcp.ts b/apps/airiscode-cli/src/commands/mcp.ts index 1bb9e0314..30cba5a49 100644 --- a/apps/airiscode-cli/src/commands/mcp.ts +++ b/apps/airiscode-cli/src/commands/mcp.ts @@ -5,22 +5,22 @@ */ // File for 'gemini mcp' command -import type { CommandModule, Argv } from 'yargs'; -import { addCommand } from './mcp/add.js'; -import { removeCommand } from './mcp/remove.js'; -import { listCommand } from './mcp/list.js'; -import { reconnectCommand } from './mcp/reconnect.js'; +import type { Argv, CommandModule } from "yargs"; +import { addCommand } from "./mcp/add.js"; +import { listCommand } from "./mcp/list.js"; +import { reconnectCommand } from "./mcp/reconnect.js"; +import { removeCommand } from "./mcp/remove.js"; export const mcpCommand: CommandModule = { - command: 'mcp', - describe: 'Manage MCP servers', + command: "mcp", + describe: "Manage MCP servers", builder: (yargs: Argv) => yargs .command(addCommand) .command(removeCommand) .command(listCommand) .command(reconnectCommand) - .demandCommand(1, 'You need at least one command before continuing.') + .demandCommand(1, "You need at least one command before continuing.") .version(false), handler: () => { // yargs will automatically show help if no subcommand is provided diff --git a/apps/airiscode-cli/src/commands/mcp/add.ts b/apps/airiscode-cli/src/commands/mcp/add.ts index 0f2c6d303..6f1b278e1 100644 --- a/apps/airiscode-cli/src/commands/mcp/add.ts +++ b/apps/airiscode-cli/src/commands/mcp/add.ts @@ -4,11 +4,11 @@ * SPDX-License-Identifier: Apache-2.0 */ +import type { MCPServerConfig } from "@airiscode/core"; // File for 'qwen mcp add' command -import type { CommandModule } from 'yargs'; -import { loadSettings, SettingScope } from '../../config/settings.js'; -import { writeStdoutLine, writeStderrLine } from '../../utils/stdioHelpers.js'; -import type { MCPServerConfig } from '@airiscode/core'; +import type { CommandModule } from "yargs"; +import { loadSettings, SettingScope } from "../../config/settings.js"; +import { writeStderrLine, writeStdoutLine } from "../../utils/stdioHelpers.js"; async function addMcpServer( name: string, @@ -26,37 +26,25 @@ async function addMcpServer( excludeTools?: string[]; }, ) { - const { - scope, - transport, - env, - header, - timeout, - trust, - description, - includeTools, - excludeTools, - } = options; + const { scope, transport, env, header, timeout, trust, description, includeTools, excludeTools } = + options; const settings = loadSettings(process.cwd()); const inHome = settings.workspace.path === settings.user.path; - if (scope === 'project' && inHome) { - writeStderrLine( - 'Error: Please use --scope user to edit settings in the home directory.', - ); + if (scope === "project" && inHome) { + writeStderrLine("Error: Please use --scope user to edit settings in the home directory."); process.exit(1); } - const settingsScope = - scope === 'user' ? SettingScope.User : SettingScope.Workspace; + const settingsScope = scope === "user" ? SettingScope.User : SettingScope.Workspace; let newServer: Partial = {}; const headers = header?.reduce( (acc, curr) => { - const [key, ...valueParts] = curr.split(':'); - const value = valueParts.join(':').trim(); + const [key, ...valueParts] = curr.split(":"); + const value = valueParts.join(":").trim(); if (key.trim() && value) { acc[key.trim()] = value; } @@ -66,7 +54,7 @@ async function addMcpServer( ); switch (transport) { - case 'sse': + case "sse": newServer = { url: commandOrUrl, headers, @@ -77,7 +65,7 @@ async function addMcpServer( excludeTools, }; break; - case 'http': + case "http": newServer = { httpUrl: commandOrUrl, headers, @@ -88,14 +76,14 @@ async function addMcpServer( excludeTools, }; break; - case 'stdio': + case "stdio": default: newServer = { command: commandOrUrl, args: args?.map(String), env: env?.reduce( (acc, curr) => { - const [key, value] = curr.split('='); + const [key, value] = curr.split("="); if (key && value) { acc[key] = value; } @@ -117,132 +105,125 @@ async function addMcpServer( const isExistingServer = !!mcpServers[name]; if (isExistingServer) { - writeStdoutLine( - `MCP server "${name}" is already configured within ${scope} settings.`, - ); + writeStdoutLine(`MCP server "${name}" is already configured within ${scope} settings.`); } mcpServers[name] = newServer as MCPServerConfig; - settings.setValue(settingsScope, 'mcpServers', mcpServers); + settings.setValue(settingsScope, "mcpServers", mcpServers); if (isExistingServer) { writeStdoutLine(`MCP server "${name}" updated in ${scope} settings.`); } else { - writeStdoutLine( - `MCP server "${name}" added to ${scope} settings. (${transport})`, - ); + writeStdoutLine(`MCP server "${name}" added to ${scope} settings. (${transport})`); } } export const addCommand: CommandModule = { - command: 'add [args...]', - describe: 'Add a server', + command: "add [args...]", + describe: "Add a server", builder: (yargs) => yargs - .usage('Usage: qwen mcp add [options] [args...]') + .usage("Usage: qwen mcp add [options] [args...]") .parserConfiguration({ - 'unknown-options-as-args': true, // Pass unknown options as server args - 'populate--': true, // Populate server args after -- separator + "unknown-options-as-args": true, // Pass unknown options as server args + "populate--": true, // Populate server args after -- separator }) - .positional('name', { - describe: 'Name of the server', - type: 'string', + .positional("name", { + describe: "Name of the server", + type: "string", demandOption: true, }) - .positional('commandOrUrl', { - describe: 'Command (stdio) or URL (sse, http)', - type: 'string', + .positional("commandOrUrl", { + describe: "Command (stdio) or URL (sse, http)", + type: "string", demandOption: true, }) - .option('scope', { - alias: 's', - describe: 'Configuration scope (user or project)', - type: 'string', - default: 'user', - choices: ['user', 'project'], + .option("scope", { + alias: "s", + describe: "Configuration scope (user or project)", + type: "string", + default: "user", + choices: ["user", "project"], }) - .option('transport', { - alias: 't', - describe: - 'Transport type (stdio, sse, http). Auto-detected from URL if not specified.', - type: 'string', - choices: ['stdio', 'sse', 'http'], + .option("transport", { + alias: "t", + describe: "Transport type (stdio, sse, http). Auto-detected from URL if not specified.", + type: "string", + choices: ["stdio", "sse", "http"], }) - .option('env', { - alias: 'e', - describe: 'Set environment variables (e.g. -e KEY=value)', - type: 'array', + .option("env", { + alias: "e", + describe: "Set environment variables (e.g. -e KEY=value)", + type: "array", string: true, nargs: 1, }) - .option('header', { - alias: 'H', + .option("header", { + alias: "H", describe: 'Set HTTP headers for SSE and HTTP transports (e.g. -H "X-Api-Key: abc123" -H "Authorization: Bearer abc123")', - type: 'array', + type: "array", string: true, nargs: 1, }) - .option('timeout', { - describe: 'Set connection timeout in milliseconds', - type: 'number', + .option("timeout", { + describe: "Set connection timeout in milliseconds", + type: "number", }) - .option('trust', { - describe: - 'Trust the server (bypass all tool call confirmation prompts)', - type: 'boolean', + .option("trust", { + describe: "Trust the server (bypass all tool call confirmation prompts)", + type: "boolean", }) - .option('description', { - describe: 'Set the description for the server', - type: 'string', + .option("description", { + describe: "Set the description for the server", + type: "string", }) - .option('include-tools', { - describe: 'A comma-separated list of tools to include', - type: 'array', + .option("include-tools", { + describe: "A comma-separated list of tools to include", + type: "array", string: true, }) - .option('exclude-tools', { - describe: 'A comma-separated list of tools to exclude', - type: 'array', + .option("exclude-tools", { + describe: "A comma-separated list of tools to exclude", + type: "array", string: true, }) .middleware((argv) => { // Handle -- separator args as server args if present - if (argv['--']) { - const existingArgs = (argv['args'] as Array) || []; - argv['args'] = [...existingArgs, ...(argv['--'] as string[])]; + if (argv["--"]) { + const existingArgs = (argv["args"] as Array) || []; + argv["args"] = [...existingArgs, ...(argv["--"] as string[])]; } // Auto-detect transport from URL if not explicitly specified - if (!argv['transport']) { - const commandOrUrl = argv['commandOrUrl'] as string; + if (!argv["transport"]) { + const commandOrUrl = argv["commandOrUrl"] as string; if ( commandOrUrl && - (commandOrUrl.startsWith('http://') || - commandOrUrl.startsWith('https://')) + (commandOrUrl.startsWith("http://") || commandOrUrl.startsWith("https://")) ) { - argv['transport'] = 'http'; + argv["transport"] = "http"; } else { - argv['transport'] = 'stdio'; + argv["transport"] = "stdio"; } } }), handler: async (argv) => { await addMcpServer( - argv['name'] as string, - argv['commandOrUrl'] as string, - argv['args'] as Array, + argv["name"] as string, + argv["commandOrUrl"] as string, + argv["args"] as Array, { - scope: argv['scope'] as string, - transport: argv['transport'] as string, - env: argv['env'] as string[], - header: argv['header'] as string[], - timeout: argv['timeout'] as number | undefined, - trust: argv['trust'] as boolean | undefined, - description: argv['description'] as string | undefined, - includeTools: argv['includeTools'] as string[] | undefined, - excludeTools: argv['excludeTools'] as string[] | undefined, + scope: argv["scope"] as string, + transport: argv["transport"] as string, + env: argv["env"] as string[], + header: argv["header"] as string[], + timeout: argv["timeout"] as number | undefined, + trust: argv["trust"] as boolean | undefined, + description: argv["description"] as string | undefined, + includeTools: argv["includeTools"] as string[] | undefined, + excludeTools: argv["excludeTools"] as string[] | undefined, }, ); }, diff --git a/apps/airiscode-cli/src/commands/mcp/list.ts b/apps/airiscode-cli/src/commands/mcp/list.ts index 26aef2545..b11ccb6f6 100644 --- a/apps/airiscode-cli/src/commands/mcp/list.ts +++ b/apps/airiscode-cli/src/commands/mcp/list.ts @@ -4,27 +4,21 @@ * SPDX-License-Identifier: Apache-2.0 */ +import type { MCPServerConfig } from "@airiscode/core"; +import { createTransport, ExtensionManager, MCPServerStatus } from "@airiscode/core"; +import { Client } from "@modelcontextprotocol/sdk/client/index.js"; // File for 'qwen mcp list' command -import type { CommandModule } from 'yargs'; -import { loadSettings } from '../../config/settings.js'; -import { writeStdoutLine } from '../../utils/stdioHelpers.js'; -import type { MCPServerConfig } from '@airiscode/core'; -import { - MCPServerStatus, - createTransport, - ExtensionManager, -} from '@airiscode/core'; -import { Client } from '@modelcontextprotocol/sdk/client/index.js'; -import { isWorkspaceTrusted } from '../../config/trustedFolders.js'; - -const COLOR_GREEN = '\u001b[32m'; -const COLOR_YELLOW = '\u001b[33m'; -const COLOR_RED = '\u001b[31m'; -const RESET_COLOR = '\u001b[0m'; - -async function getMcpServersFromConfig(): Promise< - Record -> { +import type { CommandModule } from "yargs"; +import { loadSettings } from "../../config/settings.js"; +import { isWorkspaceTrusted } from "../../config/trustedFolders.js"; +import { writeStdoutLine } from "../../utils/stdioHelpers.js"; + +const COLOR_GREEN = "\u001b[32m"; +const COLOR_YELLOW = "\u001b[33m"; +const COLOR_RED = "\u001b[31m"; +const RESET_COLOR = "\u001b[0m"; + +async function getMcpServersFromConfig(): Promise> { const settings = loadSettings(); const extensionManager = new ExtensionManager({ isWorkspaceTrusted: !!isWorkspaceTrusted(settings.merged), @@ -35,17 +29,15 @@ async function getMcpServersFromConfig(): Promise< const mcpServers = { ...(settings.merged.mcpServers || {}) }; for (const extension of extensions) { if (extension.isActive) { - Object.entries(extension.config.mcpServers || {}).forEach( - ([key, server]) => { - if (mcpServers[key]) { - return; - } - mcpServers[key] = { - ...server, - extensionName: extension.config.name, - }; - }, - ); + Object.entries(extension.config.mcpServers || {}).forEach(([key, server]) => { + if (mcpServers[key]) { + return; + } + mcpServers[key] = { + ...server, + extensionName: extension.config.name, + }; + }); } } return mcpServers; @@ -56,8 +48,8 @@ async function testMCPConnection( config: MCPServerConfig, ): Promise { const client = new Client({ - name: 'mcp-test-client', - version: '0.0.1', + name: "mcp-test-client", + version: "0.0.1", }); let transport; @@ -97,32 +89,32 @@ export async function listMcpServers(): Promise { const serverNames = Object.keys(mcpServers); if (serverNames.length === 0) { - writeStdoutLine('No MCP servers configured.'); + writeStdoutLine("No MCP servers configured."); return; } - writeStdoutLine('Configured MCP servers:\n'); + writeStdoutLine("Configured MCP servers:\n"); for (const serverName of serverNames) { const server = mcpServers[serverName]; const status = await getServerStatus(serverName, server); - let statusIndicator = ''; - let statusText = ''; + let statusIndicator = ""; + let statusText = ""; switch (status) { case MCPServerStatus.CONNECTED: - statusIndicator = COLOR_GREEN + '✓' + RESET_COLOR; - statusText = 'Connected'; + statusIndicator = COLOR_GREEN + "✓" + RESET_COLOR; + statusText = "Connected"; break; case MCPServerStatus.CONNECTING: - statusIndicator = COLOR_YELLOW + '…' + RESET_COLOR; - statusText = 'Connecting'; + statusIndicator = COLOR_YELLOW + "…" + RESET_COLOR; + statusText = "Connecting"; break; case MCPServerStatus.DISCONNECTED: default: - statusIndicator = COLOR_RED + '✗' + RESET_COLOR; - statusText = 'Disconnected'; + statusIndicator = COLOR_RED + "✗" + RESET_COLOR; + statusText = "Disconnected"; break; } @@ -132,7 +124,7 @@ export async function listMcpServers(): Promise { } else if (server.url) { serverInfo += `${server.url} (sse)`; } else if (server.command) { - serverInfo += `${server.command} ${server.args?.join(' ') || ''} (stdio)`; + serverInfo += `${server.command} ${server.args?.join(" ") || ""} (stdio)`; } writeStdoutLine(`${statusIndicator} ${serverInfo} - ${statusText}`); @@ -140,8 +132,8 @@ export async function listMcpServers(): Promise { } export const listCommand: CommandModule = { - command: 'list', - describe: 'List all configured MCP servers', + command: "list", + describe: "List all configured MCP servers", handler: async () => { await listMcpServers(); }, diff --git a/apps/airiscode-cli/src/commands/mcp/reconnect.ts b/apps/airiscode-cli/src/commands/mcp/reconnect.ts index 98ac6566c..9c6ebe727 100644 --- a/apps/airiscode-cli/src/commands/mcp/reconnect.ts +++ b/apps/airiscode-cli/src/commands/mcp/reconnect.ts @@ -4,16 +4,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { CommandModule } from 'yargs'; -import { loadSettings } from '../../config/settings.js'; -import { writeStdoutLine, writeStderrLine } from '../../utils/stdioHelpers.js'; -import { - Config, - FileDiscoveryService, - ExtensionManager, -} from '@airiscode/core'; -import { isWorkspaceTrusted } from '../../config/trustedFolders.js'; -import type { MCPServerConfig } from '@airiscode/core'; +import type { MCPServerConfig } from "@airiscode/core"; +import { Config, ExtensionManager, FileDiscoveryService } from "@airiscode/core"; +import type { CommandModule } from "yargs"; +import { loadSettings } from "../../config/settings.js"; +import { isWorkspaceTrusted } from "../../config/trustedFolders.js"; +import { writeStderrLine, writeStdoutLine } from "../../utils/stdioHelpers.js"; async function getMcpServersFromConfig( extensionManager?: ExtensionManager, @@ -33,17 +29,15 @@ async function getMcpServersFromConfig( const mcpServers = { ...(settings.merged.mcpServers || {}) }; for (const extension of extensions) { if (extension.isActive) { - Object.entries(extension.config.mcpServers || {}).forEach( - ([key, server]) => { - if (mcpServers[key]) { - return; - } - mcpServers[key] = { - ...server, - extensionName: extension.config.name, - }; - }, - ); + Object.entries(extension.config.mcpServers || {}).forEach(([key, server]) => { + if (mcpServers[key]) { + return; + } + mcpServers[key] = { + ...server, + extensionName: extension.config.name, + }; + }); } } return mcpServers; @@ -55,7 +49,7 @@ async function createMinimalConfig(): Promise { const fileService = new FileDiscoveryService(cwd); const config = new Config({ - sessionId: 'mcp-reconnect', + sessionId: "mcp-reconnect", targetDir: cwd, cwd, debugMode: false, @@ -73,10 +67,7 @@ interface ReconnectError extends Error { exitCode: number; } -function createReconnectError( - message: string, - exitCode: number = 1, -): ReconnectError { +function createReconnectError(message: string, exitCode: number = 1): ReconnectError { const error = new Error(message) as ReconnectError; error.exitCode = exitCode; return error; @@ -86,9 +77,7 @@ async function reconnectMcpServer(serverName: string): Promise { const mcpServers = await getMcpServersFromConfig(); if (!mcpServers[serverName]) { - throw createReconnectError( - `Error: Server "${serverName}" not found in configuration.`, - ); + throw createReconnectError(`Error: Server "${serverName}" not found in configuration.`); } writeStdoutLine(`Reconnecting to server "${serverName}"...`); @@ -101,9 +90,7 @@ async function reconnectMcpServer(serverName: string): Promise { await config.shutdown(); } catch (error) { const message = error instanceof Error ? error.message : String(error); - throw createReconnectError( - `Failed to reconnect to server "${serverName}": ${message}`, - ); + throw createReconnectError(`Failed to reconnect to server "${serverName}": ${message}`); } } @@ -119,11 +106,11 @@ async function reconnectAllMcpServers(): Promise { const serverNames = Object.keys(mcpServers); if (serverNames.length === 0) { - writeStdoutLine('No MCP servers configured.'); + writeStdoutLine("No MCP servers configured."); return; } - writeStdoutLine('Reconnecting to all MCP servers...\n'); + writeStdoutLine("Reconnecting to all MCP servers...\n"); let config: Config | undefined; try { @@ -147,35 +134,33 @@ async function reconnectAllMcpServers(): Promise { } export const reconnectCommand: CommandModule = { - command: 'reconnect [server-name]', - describe: 'Reconnect MCP server(s)', + command: "reconnect [server-name]", + describe: "Reconnect MCP server(s)", builder: (yargs) => yargs - .usage('Usage: qwen mcp reconnect [options] [server-name]') - .positional('server-name', { - describe: 'Name of the server to reconnect', - type: 'string', + .usage("Usage: qwen mcp reconnect [options] [server-name]") + .positional("server-name", { + describe: "Name of the server to reconnect", + type: "string", }) - .option('all', { - alias: 'a', - describe: 'Reconnect all configured servers', - type: 'boolean', + .option("all", { + alias: "a", + describe: "Reconnect all configured servers", + type: "boolean", default: false, }) - .conflicts('server-name', 'all') + .conflicts("server-name", "all") .check((argv) => { - const serverName = argv['server-name']; - const all = argv['all']; + const serverName = argv["server-name"]; + const all = argv["all"]; if (!serverName && !all) { - throw new Error( - 'Please specify a server name or use --all to reconnect all servers.', - ); + throw new Error("Please specify a server name or use --all to reconnect all servers."); } return true; }), handler: async (argv) => { - const serverName = argv['server-name'] as string | undefined; - const all = argv['all'] as boolean; + const serverName = argv["server-name"] as string | undefined; + const all = argv["all"] as boolean; try { if (all) { diff --git a/apps/airiscode-cli/src/commands/mcp/remove.ts b/apps/airiscode-cli/src/commands/mcp/remove.ts index c794ca9e6..30b285e29 100644 --- a/apps/airiscode-cli/src/commands/mcp/remove.ts +++ b/apps/airiscode-cli/src/commands/mcp/remove.ts @@ -4,11 +4,11 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { MCPOAuthTokenStorage } from "@airiscode/core"; // File for 'qwen mcp remove' command -import type { CommandModule } from 'yargs'; -import { loadSettings, SettingScope } from '../../config/settings.js'; -import { writeStdoutLine } from '../../utils/stdioHelpers.js'; -import { MCPOAuthTokenStorage } from '@airiscode/core'; +import type { CommandModule } from "yargs"; +import { loadSettings, SettingScope } from "../../config/settings.js"; +import { writeStdoutLine } from "../../utils/stdioHelpers.js"; async function removeMcpServer( name: string, @@ -17,8 +17,7 @@ async function removeMcpServer( }, ) { const { scope } = options; - const settingsScope = - scope === 'user' ? SettingScope.User : SettingScope.Workspace; + const settingsScope = scope === "user" ? SettingScope.User : SettingScope.Workspace; const settings = loadSettings(); const existingSettings = settings.forScope(settingsScope).settings; @@ -31,7 +30,7 @@ async function removeMcpServer( delete mcpServers[name]; - settings.setValue(settingsScope, 'mcpServers', mcpServers); + settings.setValue(settingsScope, "mcpServers", mcpServers); // Clean up any stored OAuth tokens for this server try { @@ -45,26 +44,26 @@ async function removeMcpServer( } export const removeCommand: CommandModule = { - command: 'remove ', - describe: 'Remove a server', + command: "remove ", + describe: "Remove a server", builder: (yargs) => yargs - .usage('Usage: qwen mcp remove [options] ') - .positional('name', { - describe: 'Name of the server', - type: 'string', + .usage("Usage: qwen mcp remove [options] ") + .positional("name", { + describe: "Name of the server", + type: "string", demandOption: true, }) - .option('scope', { - alias: 's', - describe: 'Configuration scope (user or project)', - type: 'string', - default: 'user', - choices: ['user', 'project'], + .option("scope", { + alias: "s", + describe: "Configuration scope (user or project)", + type: "string", + default: "user", + choices: ["user", "project"], }), handler: async (argv) => { - await removeMcpServer(argv['name'] as string, { - scope: argv['scope'] as string, + await removeMcpServer(argv["name"] as string, { + scope: argv["scope"] as string, }); }, }; diff --git a/apps/airiscode-cli/src/config/auth.ts b/apps/airiscode-cli/src/config/auth.ts index 5445bdb40..ea1f04afd 100644 --- a/apps/airiscode-cli/src/config/auth.ts +++ b/apps/airiscode-cli/src/config/auth.ts @@ -9,17 +9,17 @@ import { type Config, type ModelProvidersConfig, type ProviderModelConfig, -} from '@airiscode/core'; -import { loadEnvironment, loadSettings, type Settings } from './settings.js'; -import { t } from '../i18n/index.js'; +} from "@airiscode/core"; +import { t } from "../i18n/index.js"; +import { loadEnvironment, loadSettings, type Settings } from "./settings.js"; /** * Default environment variable names for each auth type */ const DEFAULT_ENV_KEYS: Record = { - [AuthType.USE_OPENAI]: 'OPENAI_API_KEY', - [AuthType.USE_OLLAMA]: '', - [AuthType.USE_ANTHROPIC]: 'ANTHROPIC_API_KEY', + [AuthType.USE_OPENAI]: "OPENAI_API_KEY", + [AuthType.USE_OLLAMA]: "", + [AuthType.USE_ANTHROPIC]: "ANTHROPIC_API_KEY", }; /** @@ -55,9 +55,7 @@ function hasApiKeyForAuth( checkedEnvKey: string | undefined; isExplicitEnvKey: boolean; } { - const modelProviders = settings.modelProviders as - | ModelProvidersConfig - | undefined; + const modelProviders = settings.modelProviders as ModelProvidersConfig | undefined; // Use config.getModelsConfig().getModel() if available for accurate model ID resolution // that accounts for CLI args, env vars, and settings. Fall back to settings.model.name. @@ -104,11 +102,7 @@ function hasApiKeyForAuth( * Generate API key error message based on auth check result. * Returns null if API key is present, otherwise returns the appropriate error message. */ -function getApiKeyError( - authMethod: string, - settings: Settings, - config?: Config, -): string | null { +function getApiKeyError(authMethod: string, settings: Settings, config?: Config): string | null { const { hasKey, checkedEnvKey, isExplicitEnvKey } = hasApiKeyForAuth( authMethod, settings, @@ -121,12 +115,12 @@ function getApiKeyError( const envKeyHint = checkedEnvKey || DEFAULT_ENV_KEYS[authMethod]; if (isExplicitEnvKey) { return t( - '{{envKeyHint}} environment variable not found. Please set it in your .env file or environment variables.', + "{{envKeyHint}} environment variable not found. Please set it in your .env file or environment variables.", { envKeyHint }, ); } return t( - '{{envKeyHint}} environment variable not found (or set settings.security.auth.apiKey). Please set it in your .env file or environment variables.', + "{{envKeyHint}} environment variable not found (or set settings.security.auth.apiKey). Please set it in your .env file or environment variables.", { envKeyHint }, ); } @@ -134,10 +128,7 @@ function getApiKeyError( /** * Validate that the required credentials and configuration exist for the given auth method. */ -export function validateAuthMethod( - authMethod: string, - config?: Config, -): string | null { +export function validateAuthMethod(authMethod: string, config?: Config): string | null { const settings = loadSettings(); loadEnvironment(settings.merged); @@ -148,19 +139,17 @@ export function validateAuthMethod( config, ); if (!hasKey) { - const envKeyHint = checkedEnvKey - ? `'${checkedEnvKey}'` - : "'OPENAI_API_KEY'"; + const envKeyHint = checkedEnvKey ? `'${checkedEnvKey}'` : "'OPENAI_API_KEY'"; if (isExplicitEnvKey) { // Explicit envKey configured - only suggest setting the env var return t( - 'Missing API key for OpenAI-compatible auth. Set the {{envKeyHint}} environment variable.', + "Missing API key for OpenAI-compatible auth. Set the {{envKeyHint}} environment variable.", { envKeyHint }, ); } // Default env key - can use either apiKey or env var return t( - 'Missing API key for OpenAI-compatible auth. Set settings.security.auth.apiKey, or set the {{envKeyHint}} environment variable.', + "Missing API key for OpenAI-compatible auth. Set settings.security.auth.apiKey, or set the {{envKeyHint}} environment variable.", { envKeyHint }, ); } @@ -174,21 +163,16 @@ export function validateAuthMethod( } // Check baseUrl - can come from modelProviders or environment - const modelProviders = settings.merged.modelProviders as - | ModelProvidersConfig - | undefined; + const modelProviders = settings.merged.modelProviders as ModelProvidersConfig | undefined; // Use config.getModelsConfig().getModel() if available for accurate model ID - const modelId = - config?.getModelsConfig().getModel() ?? settings.merged.model?.name; + const modelId = config?.getModelsConfig().getModel() ?? settings.merged.model?.name; const modelConfig = findModelConfig(modelProviders, authMethod, modelId); if (modelConfig && !modelConfig.baseUrl) { - return t( - 'Anthropic provider missing required baseUrl in modelProviders[].baseUrl.', - ); + return t("Anthropic provider missing required baseUrl in modelProviders[].baseUrl."); } - if (!modelConfig && !process.env['ANTHROPIC_BASE_URL']) { - return t('ANTHROPIC_BASE_URL environment variable not found.'); + if (!modelConfig && !process.env["ANTHROPIC_BASE_URL"]) { + return t("ANTHROPIC_BASE_URL environment variable not found."); } return null; @@ -202,5 +186,5 @@ export function validateAuthMethod( return null; } - return t('Invalid auth method selected.'); + return t("Invalid auth method selected."); } diff --git a/apps/airiscode-cli/src/config/config.ts b/apps/airiscode-cli/src/config/config.ts index 8e59563e0..6eeb0d5d3 100755 --- a/apps/airiscode-cli/src/config/config.ts +++ b/apps/airiscode-cli/src/config/config.ts @@ -4,54 +4,50 @@ * SPDX-License-Identifier: Apache-2.0 */ +import * as fs from "node:fs"; +import { homedir } from "node:os"; +import * as path from "node:path"; import { ApprovalMode, AuthType, Config, + createDebugLogger, DEFAULT_QWEN_EMBEDDING_MODEL, + EditTool, + FatalConfigError, FileDiscoveryService, getAllGeminiMdFilenames, - loadServerHierarchicalMemory, - setGeminiMdFilename as setServerGeminiMdFilename, - resolveTelemetrySettings, - FatalConfigError, - Storage, InputFormat, - OutputFormat, - SessionService, ideContextStore, - type ResumedSessionData, + isToolEnabled, type LspClient, - type ToolName, - EditTool, - ShellTool, - WriteFileTool, + loadServerHierarchicalMemory, NativeLspClient, - createDebugLogger, NativeLspService, - isToolEnabled, -} from '@airiscode/core'; -import { extensionsCommand } from '../commands/extensions.js'; -import { hooksCommand } from '../commands/hooks.js'; -import type { Settings } from './settings.js'; -import { loadSettings, SettingScope } from './settings.js'; -import { authCommand } from '../commands/auth.js'; -import { - resolveCliGenerationConfig, - getAuthTypeFromEnv, -} from '../utils/modelConfigUtils.js'; -import yargs, { type Argv } from 'yargs'; -import { hideBin } from 'yargs/helpers'; -import * as fs from 'node:fs'; -import * as path from 'node:path'; -import { homedir } from 'node:os'; - -import { resolvePath } from '../utils/resolvePath.js'; -import { getCliVersion } from '../utils/version.js'; -import { loadSandboxConfig } from './sandboxConfig.js'; -import { appEvents } from '../utils/events.js'; -import { mcpCommand } from '../commands/mcp.js'; -import { channelCommand } from '../commands/channel.js'; + OutputFormat, + type ResumedSessionData, + resolveTelemetrySettings, + SessionService, + ShellTool, + Storage, + setGeminiMdFilename as setServerGeminiMdFilename, + type ToolName, + WriteFileTool, +} from "@airiscode/core"; +import yargs, { type Argv } from "yargs"; +import { hideBin } from "yargs/helpers"; +import { authCommand } from "../commands/auth.js"; +import { channelCommand } from "../commands/channel.js"; +import { extensionsCommand } from "../commands/extensions.js"; +import { hooksCommand } from "../commands/hooks.js"; +import { mcpCommand } from "../commands/mcp.js"; +import { appEvents } from "../utils/events.js"; +import { getAuthTypeFromEnv, resolveCliGenerationConfig } from "../utils/modelConfigUtils.js"; +import { resolvePath } from "../utils/resolvePath.js"; +import { getCliVersion } from "../utils/version.js"; +import { loadSandboxConfig } from "./sandboxConfig.js"; +import type { Settings } from "./settings.js"; +import { loadSettings, SettingScope } from "./settings.js"; // UUID v4 regex pattern for validation const SESSION_ID_REGEX = @@ -66,39 +62,32 @@ function isValidSessionId(value: string): boolean { return SESSION_ID_REGEX.test(value); } -import { isWorkspaceTrusted } from './trustedFolders.js'; -import { buildWebSearchConfig } from './webSearch.js'; -import { writeStderrLine } from '../utils/stdioHelpers.js'; +import { writeStderrLine } from "../utils/stdioHelpers.js"; +import { isWorkspaceTrusted } from "./trustedFolders.js"; +import { buildWebSearchConfig } from "./webSearch.js"; -const debugLogger = createDebugLogger('CONFIG'); +const debugLogger = createDebugLogger("CONFIG"); -const VALID_APPROVAL_MODE_VALUES = [ - 'plan', - 'default', - 'auto-edit', - 'yolo', -] as const; +const VALID_APPROVAL_MODE_VALUES = ["plan", "default", "auto-edit", "yolo"] as const; function formatApprovalModeError(value: string): Error { return new Error( - `Invalid approval mode: ${value}. Valid values are: ${VALID_APPROVAL_MODE_VALUES.join( - ', ', - )}`, + `Invalid approval mode: ${value}. Valid values are: ${VALID_APPROVAL_MODE_VALUES.join(", ")}`, ); } function parseApprovalModeValue(value: string): ApprovalMode { const normalized = value.trim().toLowerCase(); switch (normalized) { - case 'plan': + case "plan": return ApprovalMode.PLAN; - case 'default': + case "default": return ApprovalMode.DEFAULT; - case 'yolo': + case "yolo": return ApprovalMode.YOLO; - case 'auto_edit': - case 'autoedit': - case 'auto-edit': + case "auto_edit": + case "autoedit": + case "auto-edit": return ApprovalMode.AUTO_EDIT; default: throw formatApprovalModeError(value); @@ -172,7 +161,7 @@ function normalizeOutputFormat( if (format === OutputFormat.STREAM_JSON) { return OutputFormat.STREAM_JSON; } - if (format === 'json' || format === OutputFormat.JSON) { + if (format === "json" || format === OutputFormat.JSON) { return OutputFormat.JSON; } return OutputFormat.TEXT; @@ -184,393 +173,368 @@ export async function parseArguments(): Promise { // hack: if the first argument is the CLI entry point, remove it if ( rawArgv.length > 0 && - (rawArgv[0].endsWith('/dist/qwen-cli/cli.js') || - rawArgv[0].endsWith('/dist/cli.js') || - rawArgv[0].endsWith('/dist/cli/cli.js')) + (rawArgv[0].endsWith("/dist/qwen-cli/cli.js") || + rawArgv[0].endsWith("/dist/cli.js") || + rawArgv[0].endsWith("/dist/cli/cli.js")) ) { rawArgv = rawArgv.slice(1); } const yargsInstance = yargs(rawArgv) - .locale('en') - .scriptName('airiscode') + .locale("en") + .scriptName("airiscode") .usage( - 'Usage: qwen [options] [command]\n\nAIRIS Code - Launch an interactive CLI, use -p/--prompt for non-interactive mode', + "Usage: qwen [options] [command]\n\nAIRIS Code - Launch an interactive CLI, use -p/--prompt for non-interactive mode", ) - .option('telemetry', { - type: 'boolean', + .option("telemetry", { + type: "boolean", description: - 'Enable telemetry? This flag specifically controls if telemetry is sent. Other --telemetry-* flags set specific values but do not enable telemetry on their own.', + "Enable telemetry? This flag specifically controls if telemetry is sent. Other --telemetry-* flags set specific values but do not enable telemetry on their own.", }) - .option('telemetry-target', { - type: 'string', - choices: ['local', 'gcp'], - description: - 'Set the telemetry target (local or gcp). Overrides settings files.', + .option("telemetry-target", { + type: "string", + choices: ["local", "gcp"], + description: "Set the telemetry target (local or gcp). Overrides settings files.", }) - .option('telemetry-otlp-endpoint', { - type: 'string', + .option("telemetry-otlp-endpoint", { + type: "string", description: - 'Set the OTLP endpoint for telemetry. Overrides environment variables and settings files.', + "Set the OTLP endpoint for telemetry. Overrides environment variables and settings files.", }) - .option('telemetry-otlp-protocol', { - type: 'string', - choices: ['grpc', 'http'], - description: - 'Set the OTLP protocol for telemetry (grpc or http). Overrides settings files.', + .option("telemetry-otlp-protocol", { + type: "string", + choices: ["grpc", "http"], + description: "Set the OTLP protocol for telemetry (grpc or http). Overrides settings files.", }) - .option('telemetry-log-prompts', { - type: 'boolean', + .option("telemetry-log-prompts", { + type: "boolean", description: - 'Enable or disable logging of user prompts for telemetry. Overrides settings files.', + "Enable or disable logging of user prompts for telemetry. Overrides settings files.", }) - .option('telemetry-outfile', { - type: 'string', - description: 'Redirect all telemetry output to the specified file.', + .option("telemetry-outfile", { + type: "string", + description: "Redirect all telemetry output to the specified file.", }) .deprecateOption( - 'telemetry', + "telemetry", 'Use the "telemetry.enabled" setting in settings.json instead. This flag will be removed in a future version.', ) .deprecateOption( - 'telemetry-target', + "telemetry-target", 'Use the "telemetry.target" setting in settings.json instead. This flag will be removed in a future version.', ) .deprecateOption( - 'telemetry-otlp-endpoint', + "telemetry-otlp-endpoint", 'Use the "telemetry.otlpEndpoint" setting in settings.json instead. This flag will be removed in a future version.', ) .deprecateOption( - 'telemetry-otlp-protocol', + "telemetry-otlp-protocol", 'Use the "telemetry.otlpProtocol" setting in settings.json instead. This flag will be removed in a future version.', ) .deprecateOption( - 'telemetry-log-prompts', + "telemetry-log-prompts", 'Use the "telemetry.logPrompts" setting in settings.json instead. This flag will be removed in a future version.', ) .deprecateOption( - 'telemetry-outfile', + "telemetry-outfile", 'Use the "telemetry.outfile" setting in settings.json instead. This flag will be removed in a future version.', ) - .option('debug', { - alias: 'd', - type: 'boolean', - description: 'Run in debug mode?', + .option("debug", { + alias: "d", + type: "boolean", + description: "Run in debug mode?", default: false, }) - .option('proxy', { - type: 'string', - description: 'Proxy for AIRIS Code, like schema://user:password@host:port', + .option("proxy", { + type: "string", + description: "Proxy for AIRIS Code, like schema://user:password@host:port", }) .deprecateOption( - 'proxy', + "proxy", 'Use the "proxy" setting in settings.json instead. This flag will be removed in a future version.', ) - .option('chat-recording', { - type: 'boolean', + .option("chat-recording", { + type: "boolean", description: - 'Enable chat recording to disk. If false, chat history is not saved and --continue/--resume will not work.', + "Enable chat recording to disk. If false, chat history is not saved and --continue/--resume will not work.", }) - .command('$0 [query..]', 'Launch AIRIS Code CLI', (yargsInstance: Argv) => + .command("$0 [query..]", "Launch AIRIS Code CLI", (yargsInstance: Argv) => yargsInstance - .positional('query', { + .positional("query", { description: - 'Positional prompt. Defaults to one-shot; use -i/--prompt-interactive for interactive.', + "Positional prompt. Defaults to one-shot; use -i/--prompt-interactive for interactive.", }) - .option('model', { - alias: 'm', - type: 'string', + .option("model", { + alias: "m", + type: "string", description: `Model`, }) - .option('prompt', { - alias: 'p', - type: 'string', - description: 'Prompt. Appended to input on stdin (if any).', + .option("prompt", { + alias: "p", + type: "string", + description: "Prompt. Appended to input on stdin (if any).", }) - .option('prompt-interactive', { - alias: 'i', - type: 'string', - description: - 'Execute the provided prompt and continue in interactive mode', + .option("prompt-interactive", { + alias: "i", + type: "string", + description: "Execute the provided prompt and continue in interactive mode", }) - .option('system-prompt', { - type: 'string', + .option("system-prompt", { + type: "string", description: - 'Override the main session system prompt for this run. Can be combined with --append-system-prompt.', + "Override the main session system prompt for this run. Can be combined with --append-system-prompt.", }) - .option('append-system-prompt', { - type: 'string', + .option("append-system-prompt", { + type: "string", description: - 'Append instructions to the main session system prompt for this run. Can be combined with --system-prompt.', + "Append instructions to the main session system prompt for this run. Can be combined with --system-prompt.", }) - .option('sandbox', { - alias: 's', - type: 'boolean', - description: 'Run in sandbox?', + .option("sandbox", { + alias: "s", + type: "boolean", + description: "Run in sandbox?", }) - .option('sandbox-image', { - type: 'string', - description: 'Sandbox image URI.', + .option("sandbox-image", { + type: "string", + description: "Sandbox image URI.", }) - .option('yolo', { - alias: 'y', - type: 'boolean', + .option("yolo", { + alias: "y", + type: "boolean", description: - 'Automatically accept all actions (aka YOLO mode, see https://www.youtube.com/watch?v=xvFZjo5PgG0 for more details)?', + "Automatically accept all actions (aka YOLO mode, see https://www.youtube.com/watch?v=xvFZjo5PgG0 for more details)?", default: false, }) - .option('approval-mode', { - type: 'string', - choices: ['plan', 'default', 'auto-edit', 'yolo'], + .option("approval-mode", { + type: "string", + choices: ["plan", "default", "auto-edit", "yolo"], description: - 'Set the approval mode: plan (plan only), default (prompt for approval), auto-edit (auto-approve edit tools), yolo (auto-approve all tools)', + "Set the approval mode: plan (plan only), default (prompt for approval), auto-edit (auto-approve edit tools), yolo (auto-approve all tools)", }) - .option('checkpointing', { - type: 'boolean', - description: 'Enables checkpointing of file edits', + .option("checkpointing", { + type: "boolean", + description: "Enables checkpointing of file edits", default: false, }) - .option('acp', { - type: 'boolean', - description: 'Starts the agent in ACP mode', + .option("acp", { + type: "boolean", + description: "Starts the agent in ACP mode", }) - .option('experimental-acp', { - type: 'boolean', - description: - 'Starts the agent in ACP mode (deprecated, use --acp instead)', + .option("experimental-acp", { + type: "boolean", + description: "Starts the agent in ACP mode (deprecated, use --acp instead)", hidden: true, }) - .option('experimental-skills', { - type: 'boolean', - description: - 'Deprecated: Skills are now enabled by default. This flag is ignored.', + .option("experimental-skills", { + type: "boolean", + description: "Deprecated: Skills are now enabled by default. This flag is ignored.", hidden: true, }) - .option('experimental-lsp', { - type: 'boolean', + .option("experimental-lsp", { + type: "boolean", description: - 'Enable experimental LSP (Language Server Protocol) feature for code intelligence', + "Enable experimental LSP (Language Server Protocol) feature for code intelligence", default: false, }) - .option('channel', { - type: 'string', - choices: ['VSCode', 'ACP', 'SDK', 'CI'], - description: 'Channel identifier (VSCode, ACP, SDK, CI)', + .option("channel", { + type: "string", + choices: ["VSCode", "ACP", "SDK", "CI"], + description: "Channel identifier (VSCode, ACP, SDK, CI)", }) - .option('allowed-mcp-server-names', { - type: 'array', + .option("allowed-mcp-server-names", { + type: "array", string: true, - description: 'Allowed MCP server names', + description: "Allowed MCP server names", coerce: (mcpServerNames: string[]) => // Handle comma-separated values mcpServerNames.flatMap((mcpServerName) => - mcpServerName.split(',').map((m) => m.trim()), + mcpServerName.split(",").map((m) => m.trim()), ), }) - .option('allowed-tools', { - type: 'array', + .option("allowed-tools", { + type: "array", string: true, - description: 'Tools that are allowed to run without confirmation', + description: "Tools that are allowed to run without confirmation", coerce: (tools: string[]) => // Handle comma-separated values - tools.flatMap((tool) => tool.split(',').map((t) => t.trim())), + tools.flatMap((tool) => tool.split(",").map((t) => t.trim())), }) - .option('extensions', { - alias: 'e', - type: 'array', + .option("extensions", { + alias: "e", + type: "array", string: true, - description: - 'A list of extensions to use. If not provided, all extensions are used.', + description: "A list of extensions to use. If not provided, all extensions are used.", coerce: (extensions: string[]) => // Handle comma-separated values - extensions.flatMap((extension) => - extension.split(',').map((e) => e.trim()), - ), + extensions.flatMap((extension) => extension.split(",").map((e) => e.trim())), }) - .option('list-extensions', { - alias: 'l', - type: 'boolean', - description: 'List all available extensions and exit.', + .option("list-extensions", { + alias: "l", + type: "boolean", + description: "List all available extensions and exit.", }) - .option('include-directories', { - alias: 'add-dir', - type: 'array', + .option("include-directories", { + alias: "add-dir", + type: "array", string: true, description: - 'Additional directories to include in the workspace (comma-separated or multiple --include-directories)', + "Additional directories to include in the workspace (comma-separated or multiple --include-directories)", coerce: (dirs: string[]) => // Handle comma-separated values - dirs.flatMap((dir) => dir.split(',').map((d) => d.trim())), + dirs.flatMap((dir) => dir.split(",").map((d) => d.trim())), }) - .option('openai-logging', { - type: 'boolean', - description: - 'Enable logging of OpenAI API calls for debugging and analysis', + .option("openai-logging", { + type: "boolean", + description: "Enable logging of OpenAI API calls for debugging and analysis", }) - .option('openai-logging-dir', { - type: 'string', - description: - 'Custom directory path for OpenAI API logs. Overrides settings files.', + .option("openai-logging-dir", { + type: "string", + description: "Custom directory path for OpenAI API logs. Overrides settings files.", }) - .option('openai-api-key', { - type: 'string', - description: 'OpenAI API key to use for authentication', + .option("openai-api-key", { + type: "string", + description: "OpenAI API key to use for authentication", }) - .option('openai-base-url', { - type: 'string', - description: 'OpenAI base URL (for custom endpoints)', + .option("openai-base-url", { + type: "string", + description: "OpenAI base URL (for custom endpoints)", }) - .option('tavily-api-key', { - type: 'string', - description: 'Tavily API key for web search', + .option("tavily-api-key", { + type: "string", + description: "Tavily API key for web search", }) - .option('google-api-key', { - type: 'string', - description: 'Google Custom Search API key', + .option("google-api-key", { + type: "string", + description: "Google Custom Search API key", }) - .option('google-search-engine-id', { - type: 'string', - description: 'Google Custom Search Engine ID', + .option("google-search-engine-id", { + type: "string", + description: "Google Custom Search Engine ID", }) - .option('web-search-default', { - type: 'string', - description: - 'Default web search provider (dashscope, tavily, google)', + .option("web-search-default", { + type: "string", + description: "Default web search provider (dashscope, tavily, google)", }) - .option('screen-reader', { - type: 'boolean', - description: 'Enable screen reader mode for accessibility.', + .option("screen-reader", { + type: "boolean", + description: "Enable screen reader mode for accessibility.", }) - .option('input-format', { - type: 'string', - choices: ['text', 'stream-json'], - description: 'The format consumed from standard input.', - default: 'text', + .option("input-format", { + type: "string", + choices: ["text", "stream-json"], + description: "The format consumed from standard input.", + default: "text", }) - .option('output-format', { - alias: 'o', - type: 'string', - description: 'The format of the CLI output.', - choices: ['text', 'json', 'stream-json'], + .option("output-format", { + alias: "o", + type: "string", + description: "The format of the CLI output.", + choices: ["text", "json", "stream-json"], }) - .option('include-partial-messages', { - type: 'boolean', - description: - 'Include partial assistant messages when using stream-json output.', + .option("include-partial-messages", { + type: "boolean", + description: "Include partial assistant messages when using stream-json output.", default: false, }) - .option('continue', { - alias: 'c', - type: 'boolean', - description: - 'Resume the most recent session for the current project.', + .option("continue", { + alias: "c", + type: "boolean", + description: "Resume the most recent session for the current project.", default: false, }) - .option('resume', { - alias: 'r', - type: 'string', + .option("resume", { + alias: "r", + type: "string", description: - 'Resume a specific session by its ID. Use without an ID to show session picker.', + "Resume a specific session by its ID. Use without an ID to show session picker.", }) - .option('session-id', { - type: 'string', - description: 'Specify a session ID for this run.', + .option("session-id", { + type: "string", + description: "Specify a session ID for this run.", }) - .option('max-session-turns', { - type: 'number', - description: 'Maximum number of session turns', + .option("max-session-turns", { + type: "number", + description: "Maximum number of session turns", }) - .option('core-tools', { - type: 'array', + .option("core-tools", { + type: "array", string: true, - description: 'Core tool paths', + description: "Core tool paths", coerce: (tools: string[]) => - tools.flatMap((tool) => tool.split(',').map((t) => t.trim())), + tools.flatMap((tool) => tool.split(",").map((t) => t.trim())), }) - .option('exclude-tools', { - type: 'array', + .option("exclude-tools", { + type: "array", string: true, - description: 'Tools to exclude', + description: "Tools to exclude", coerce: (tools: string[]) => - tools.flatMap((tool) => tool.split(',').map((t) => t.trim())), + tools.flatMap((tool) => tool.split(",").map((t) => t.trim())), }) - .option('allowed-tools', { - type: 'array', + .option("allowed-tools", { + type: "array", string: true, - description: 'Tools to allow, will bypass confirmation', + description: "Tools to allow, will bypass confirmation", coerce: (tools: string[]) => - tools.flatMap((tool) => tool.split(',').map((t) => t.trim())), + tools.flatMap((tool) => tool.split(",").map((t) => t.trim())), }) - .option('auth-type', { - type: 'string', - choices: [ - AuthType.USE_OPENAI, - AuthType.USE_ANTHROPIC, - AuthType.USE_OLLAMA, - ], - description: 'Authentication type', + .option("auth-type", { + type: "string", + choices: [AuthType.USE_OPENAI, AuthType.USE_ANTHROPIC, AuthType.USE_OLLAMA], + description: "Authentication type", }) .deprecateOption( - 'sandbox-image', + "sandbox-image", 'Use the "tools.sandbox" setting in settings.json instead. This flag will be removed in a future version.', ) .deprecateOption( - 'checkpointing', + "checkpointing", 'Use the "general.checkpointing.enabled" setting in settings.json instead. This flag will be removed in a future version.', ) .deprecateOption( - 'prompt', - 'Use the positional prompt instead. This flag will be removed in a future version.', + "prompt", + "Use the positional prompt instead. This flag will be removed in a future version.", ) // Ensure validation flows through .fail() for clean UX .fail((msg: string, err: Error | undefined, yargs: Argv) => { - writeStderrLine(msg || err?.message || 'Unknown error'); + writeStderrLine(msg || err?.message || "Unknown error"); yargs.showHelp(); process.exit(1); }) .check((argv: { [x: string]: unknown }) => { // The 'query' positional can be a string (for one arg) or string[] (for multiple). // This guard safely checks if any positional argument was provided. - const query = argv['query'] as string | string[] | undefined; - const hasPositionalQuery = Array.isArray(query) - ? query.length > 0 - : !!query; + const query = argv["query"] as string | string[] | undefined; + const hasPositionalQuery = Array.isArray(query) ? query.length > 0 : !!query; - if (argv['prompt'] && hasPositionalQuery) { - return 'Cannot use both a positional prompt and the --prompt (-p) flag together'; + if (argv["prompt"] && hasPositionalQuery) { + return "Cannot use both a positional prompt and the --prompt (-p) flag together"; } - if (argv['prompt'] && argv['promptInteractive']) { - return 'Cannot use both --prompt (-p) and --prompt-interactive (-i) together'; + if (argv["prompt"] && argv["promptInteractive"]) { + return "Cannot use both --prompt (-p) and --prompt-interactive (-i) together"; } - if (argv['yolo'] && argv['approvalMode']) { - return 'Cannot use both --yolo (-y) and --approval-mode together. Use --approval-mode=yolo instead.'; + if (argv["yolo"] && argv["approvalMode"]) { + return "Cannot use both --yolo (-y) and --approval-mode together. Use --approval-mode=yolo instead."; } - if ( - argv['includePartialMessages'] && - argv['outputFormat'] !== OutputFormat.STREAM_JSON - ) { - return '--include-partial-messages requires --output-format stream-json'; + if (argv["includePartialMessages"] && argv["outputFormat"] !== OutputFormat.STREAM_JSON) { + return "--include-partial-messages requires --output-format stream-json"; } if ( - argv['inputFormat'] === 'stream-json' && - argv['outputFormat'] !== OutputFormat.STREAM_JSON + argv["inputFormat"] === "stream-json" && + argv["outputFormat"] !== OutputFormat.STREAM_JSON ) { - return '--input-format stream-json requires --output-format stream-json'; + return "--input-format stream-json requires --output-format stream-json"; } - if (argv['continue'] && argv['resume']) { - return 'Cannot use both --continue and --resume together. Use --continue to resume the latest session, or --resume to resume a specific session.'; + if (argv["continue"] && argv["resume"]) { + return "Cannot use both --continue and --resume together. Use --continue to resume the latest session, or --resume to resume a specific session."; } - if (argv['sessionId'] && (argv['continue'] || argv['resume'])) { - return 'Cannot use --session-id with --continue or --resume. Use --session-id to start a new session with a specific ID, or use --continue/--resume to resume an existing session.'; + if (argv["sessionId"] && (argv["continue"] || argv["resume"])) { + return "Cannot use --session-id with --continue or --resume. Use --session-id to start a new session with a specific ID, or use --continue/--resume to resume an existing session."; } - if ( - argv['sessionId'] && - !isValidSessionId(argv['sessionId'] as string) - ) { - return `Invalid --session-id: "${argv['sessionId']}". Must be a valid UUID (e.g., "123e4567-e89b-12d3-a456-426614174000").`; + if (argv["sessionId"] && !isValidSessionId(argv["sessionId"] as string)) { + return `Invalid --session-id: "${argv["sessionId"]}". Must be a valid UUID (e.g., "123e4567-e89b-12d3-a456-426614174000").`; } - if (argv['resume'] && !isValidSessionId(argv['resume'] as string)) { - return `Invalid --resume: "${argv['resume']}". Must be a valid UUID (e.g., "123e4567-e89b-12d3-a456-426614174000").`; + if (argv["resume"] && !isValidSessionId(argv["resume"] as string)) { + return `Invalid --resume: "${argv["resume"]}". Must be a valid UUID (e.g., "123e4567-e89b-12d3-a456-426614174000").`; } return true; }), @@ -588,9 +552,9 @@ export async function parseArguments(): Promise { yargsInstance .version(await getCliVersion()) // This will enable the --version flag based on package.json - .alias('v', 'version') + .alias("v", "version") .help() - .alias('h', 'help') + .alias("h", "help") .strict() .demandCommand(0, 0); // Allow base command to run with no subcommands @@ -603,10 +567,10 @@ export async function parseArguments(): Promise { // and not return to main CLI logic if ( result._.length > 0 && - (result._[0] === 'mcp' || - result._[0] === 'extensions' || - result._[0] === 'hooks' || - result._[0] === 'channel') + (result._[0] === "mcp" || + result._[0] === "extensions" || + result._[0] === "hooks" || + result._[0] === "channel") ) { // MCP/Extensions/Hooks commands handle their own execution and process exit process.exit(0); @@ -614,41 +578,39 @@ export async function parseArguments(): Promise { // Normalize query args: handle both quoted "@path file" and unquoted @path file const queryArg = (result as { query?: string | string[] | undefined }).query; - const q: string | undefined = Array.isArray(queryArg) - ? queryArg.join(' ') - : queryArg; + const q: string | undefined = Array.isArray(queryArg) ? queryArg.join(" ") : queryArg; // Route positional args: explicit -i flag -> interactive; else -> one-shot (even for @commands) - if (q && !result['prompt']) { + if (q && !result["prompt"]) { const hasExplicitInteractive = - result['promptInteractive'] === '' || !!result['promptInteractive']; + result["promptInteractive"] === "" || !!result["promptInteractive"]; if (hasExplicitInteractive) { - result['promptInteractive'] = q; + result["promptInteractive"] = q; } else { - result['prompt'] = q; + result["prompt"] = q; } } // Keep CliArgs.query as a string for downstream typing - (result as Record)['query'] = q || undefined; + (result as Record)["query"] = q || undefined; // The import format is now only controlled by settings.memoryImportFormat // We no longer accept it as a CLI argument // Handle deprecated --experimental-acp flag - if (result['experimentalAcp']) { + if (result["experimentalAcp"]) { writeStderrLine( - '\x1b[33m⚠ Warning: --experimental-acp is deprecated and will be removed in a future release. Please use --acp instead.\x1b[0m', + "\x1b[33m⚠ Warning: --experimental-acp is deprecated and will be removed in a future release. Please use --acp instead.\x1b[0m", ); // Map experimental-acp to acp if acp is not explicitly set - if (!result['acp']) { - (result as Record)['acp'] = true; + if (!result["acp"]) { + (result as Record)["acp"] = true; } } // Apply ACP fallback: if acp or experimental-acp is present but no explicit --channel, treat as ACP - if ((result['acp'] || result['experimentalAcp']) && !result['channel']) { - (result as Record)['channel'] = 'ACP'; + if ((result["acp"] || result["experimentalAcp"]) && !result["channel"]) { + (result as Record)["channel"] = "ACP"; } return result as unknown as CliArgs; @@ -663,7 +625,7 @@ export async function loadHierarchicalGeminiMemory( fileService: FileDiscoveryService, extensionContextFilePaths: string[] = [], folderTrust: boolean, - memoryImportFormat: 'flat' | 'tree' = 'tree', + memoryImportFormat: "flat" | "tree" = "tree", ): Promise<{ memoryContent: string; fileCount: number }> { // FIX: Use real, canonical paths for a reliable comparison to handle symlinks. const realCwd = fs.realpathSync(path.resolve(currentWorkingDirectory)); @@ -672,7 +634,7 @@ export async function loadHierarchicalGeminiMemory( // If it is the home directory, pass an empty string to the core memory // function to signal that it should skip the workspace search. - const effectiveCwd = isHomeDirectory ? '' : currentWorkingDirectory; + const effectiveCwd = isHomeDirectory ? "" : currentWorkingDirectory; // Directly call the server function with the corrected path. return loadServerHierarchicalMemory( @@ -688,9 +650,7 @@ export async function loadHierarchicalGeminiMemory( export function isDebugMode(argv: CliArgs): boolean { return ( argv.debug || - [process.env['DEBUG'], process.env['DEBUG_MODE']].some( - (v) => v === 'true' || v === '1', - ) + [process.env["DEBUG"], process.env["DEBUG_MODE"]].some((v) => v === "true" || v === "1") ); } @@ -725,14 +685,8 @@ export async function loadCliConfig( // Automatically load output-language.md if it exists const projectStorage = new Storage(cwd); - const projectOutputLanguagePath = path.join( - projectStorage.getQwenDir(), - 'output-language.md', - ); - const globalOutputLanguagePath = path.join( - Storage.getGlobalQwenDir(), - 'output-language.md', - ); + const projectOutputLanguagePath = path.join(projectStorage.getQwenDir(), "output-language.md"); + const globalOutputLanguagePath = path.join(Storage.getGlobalQwenDir(), "output-language.md"); let outputLanguageFilePath: string | undefined; if (fs.existsSync(projectOutputLanguagePath)) { @@ -750,19 +704,17 @@ export async function loadCliConfig( // LSP configuration: enabled only via --experimental-lsp flag const lspEnabled = argv.experimentalLsp === true; let lspClient: LspClient | undefined; - const question = argv.promptInteractive || argv.prompt || ''; + const question = argv.promptInteractive || argv.prompt || ""; const inputFormat: InputFormat = (argv.inputFormat as InputFormat | undefined) ?? InputFormat.TEXT; const argvOutputFormat = normalizeOutputFormat( argv.outputFormat as string | OutputFormat | undefined, ); const settingsOutputFormat = normalizeOutputFormat(settings.output?.format); - const outputFormat = - argvOutputFormat ?? settingsOutputFormat ?? OutputFormat.TEXT; + const outputFormat = argvOutputFormat ?? settingsOutputFormat ?? OutputFormat.TEXT; const outputSettingsFormat: OutputFormat = outputFormat === OutputFormat.STREAM_JSON - ? settingsOutputFormat && - settingsOutputFormat !== OutputFormat.STREAM_JSON + ? settingsOutputFormat && settingsOutputFormat !== OutputFormat.STREAM_JSON ? settingsOutputFormat : OutputFormat.TEXT : (outputFormat as OutputFormat); @@ -801,9 +753,7 @@ export async function loadCliConfig( }); } catch (err) { if (err instanceof FatalConfigError) { - throw new FatalConfigError( - `Invalid telemetry configuration: ${err.message}.`, - ); + throw new FatalConfigError(`Invalid telemetry configuration: ${err.message}.`); } throw err; } @@ -819,8 +769,7 @@ export async function loadCliConfig( // Priority 1: Explicit -i flag means interactive interactive = true; } else if ( - (outputFormat === OutputFormat.STREAM_JSON || - outputFormat === OutputFormat.JSON) && + (outputFormat === OutputFormat.STREAM_JSON || outputFormat === OutputFormat.JSON) && (hasQuery || hasPrompt) ) { // Priority 2: JSON/stream-json output with query/prompt means non-interactive @@ -851,10 +800,7 @@ export async function loadCliConfig( // mergedAllow — they have whitelist semantics (only listed tools are registered), // not auto-approve semantics. They are passed via the `coreTools` Config param // and handled by PermissionManager.coreToolsAllowList. - const resolvedCoreTools: string[] = [ - ...(argv.coreTools ?? []), - ...(settings.tools?.core ?? []), - ]; + const resolvedCoreTools: string[] = [...(argv.coreTools ?? []), ...(settings.tools?.core ?? [])]; const mergedAllow: string[] = [ ...(settings.permissions?.allow ?? []), ...(settings.tools?.allowed ?? []), @@ -884,9 +830,8 @@ export async function loadCliConfig( // 1. Check permissions.allow / allowedTools rules. if ( mergedAllow.some((rule) => { - const openParen = rule.indexOf('('); - const ruleName = - openParen === -1 ? rule.trim() : rule.substring(0, openParen).trim(); + const openParen = rule.indexOf("("); + const ruleName = openParen === -1 ? rule.trim() : rule.substring(0, openParen).trim(); return ruleName === name; }) ) { @@ -996,17 +941,17 @@ export async function loadCliConfig( process.exit(1); } } - } else if (argv['sessionId']) { + } else if (argv["sessionId"]) { // Use provided session ID without session resumption // Check if session ID is already in use const sessionService = new SessionService(cwd); - const exists = await sessionService.sessionExists(argv['sessionId']); + const exists = await sessionService.sessionExists(argv["sessionId"]); if (exists) { - const message = `Error: Session Id ${argv['sessionId']} is already in use.`; + const message = `Error: Session Id ${argv["sessionId"]} is already in use.`; writeStderrLine(message); process.exit(1); } - sessionId = argv['sessionId']; + sessionId = argv["sessionId"]; } const modelProvidersConfig = settings.modelProviders; @@ -1018,9 +963,8 @@ export async function loadCliConfig( sandbox: sandboxConfig, targetDir: cwd, includeDirectories, - loadMemoryFromIncludeDirectories: - settings.context?.loadFromIncludeDirectories || false, - importFormat: settings.context?.importFormat || 'tree', + loadMemoryFromIncludeDirectories: settings.context?.loadFromIncludeDirectories || false, + importFormat: settings.context?.importFormat || "tree", debugMode, question, systemPrompt: argv.systemPrompt, @@ -1038,13 +982,10 @@ export async function loadCliConfig( // Permission rule persistence callback (writes to settings files). onPersistPermissionRule: async (scope, ruleType, rule) => { const currentSettings = loadSettings(cwd); - const settingScope = - scope === 'project' ? SettingScope.Workspace : SettingScope.User; + const settingScope = scope === "project" ? SettingScope.Workspace : SettingScope.User; const key = `permissions.${ruleType}`; const currentRules: string[] = - currentSettings.forScope(settingScope).settings.permissions?.[ - ruleType - ] ?? []; + currentSettings.forScope(settingScope).settings.permissions?.[ruleType] ?? []; if (!currentRules.includes(rule)) { currentSettings.setValue(settingScope, key, [...currentRules, rule]); } @@ -1053,12 +994,8 @@ export async function loadCliConfig( toolCallCommand: settings.tools?.callCommand, mcpServerCommand: settings.mcp?.serverCommand, mcpServers: settings.mcpServers || {}, - allowedMcpServers: allowedMcpServers - ? Array.from(allowedMcpServers) - : undefined, - excludedMcpServers: excludedMcpServers - ? Array.from(excludedMcpServers) - : undefined, + allowedMcpServers: allowedMcpServers ? Array.from(allowedMcpServers) : undefined, + excludedMcpServers: excludedMcpServers ? Array.from(excludedMcpServers) : undefined, approvalMode, accessibility: { ...settings.ui?.accessibility, @@ -1068,27 +1005,25 @@ export async function loadCliConfig( usageStatisticsEnabled: settings.privacy?.usageStatisticsEnabled ?? true, fileFiltering: settings.context?.fileFiltering, thinkingIdleThresholdMinutes: settings.context?.gapThresholdMinutes, - checkpointing: - argv.checkpointing || settings.general?.checkpointing?.enabled, + checkpointing: argv.checkpointing || settings.general?.checkpointing?.enabled, proxy: argv.proxy || - process.env['HTTPS_PROXY'] || - process.env['https_proxy'] || - process.env['HTTP_PROXY'] || - process.env['http_proxy'], + process.env["HTTPS_PROXY"] || + process.env["https_proxy"] || + process.env["HTTP_PROXY"] || + process.env["http_proxy"], cwd, fileDiscoveryService: fileService, bugCommand: settings.advanced?.bugCommand, model: resolvedModel, outputLanguageFilePath, sessionTokenLimit: settings.model?.sessionTokenLimit ?? -1, - maxSessionTurns: - argv.maxSessionTurns ?? settings.model?.maxSessionTurns ?? -1, + maxSessionTurns: argv.maxSessionTurns ?? settings.model?.maxSessionTurns ?? -1, experimentalZedIntegration: argv.acp || argv.experimentalAcp || false, cronEnabled: settings.experimental?.cron ?? false, listExtensions: argv.listExtensions || false, overrideExtensions: overrideExtensions || argv.extensions, - noBrowser: !!process.env['NO_BROWSER'], + noBrowser: !!process.env["NO_BROWSER"], authType: selectedAuthType, inputFormat, outputFormat, @@ -1123,8 +1058,7 @@ export async function loadCliConfig( // Precedence: explicit CLI flag > settings file > default(true). // NOTE: do NOT set a yargs default for `chat-recording`, otherwise argv will // always be true and the settings file can never disable recording. - chatRecording: - argv.chatRecording ?? settings.general?.chatRecording ?? true, + chatRecording: argv.chatRecording ?? settings.general?.chatRecording ?? true, defaultFileEncoding: settings.general?.defaultFileEncoding, lsp: { enabled: lspEnabled, @@ -1135,8 +1069,7 @@ export async function loadCliConfig( arena: settings.agents.arena ? { worktreeBaseDir: settings.agents.arena.worktreeBaseDir, - preserveArtifacts: - settings.agents.arena.preserveArtifacts ?? false, + preserveArtifacts: settings.agents.arena.preserveArtifacts ?? false, } : undefined, } @@ -1161,7 +1094,7 @@ export async function loadCliConfig( lspClient = new NativeLspClient(lspService); config.setLspClient(lspClient); } catch (err) { - debugLogger.warn('Failed to initialize native LSP service:', err); + debugLogger.warn("Failed to initialize native LSP service:", err); } } diff --git a/apps/airiscode-cli/src/config/keyBindings.ts b/apps/airiscode-cli/src/config/keyBindings.ts index b13da27fa..e10383d68 100644 --- a/apps/airiscode-cli/src/config/keyBindings.ts +++ b/apps/airiscode-cli/src/config/keyBindings.ts @@ -9,59 +9,59 @@ */ export enum Command { // Basic bindings - RETURN = 'return', - ESCAPE = 'escape', + RETURN = "return", + ESCAPE = "escape", // Cursor movement - HOME = 'home', - END = 'end', + HOME = "home", + END = "end", // Text deletion - KILL_LINE_RIGHT = 'killLineRight', - KILL_LINE_LEFT = 'killLineLeft', - CLEAR_INPUT = 'clearInput', - DELETE_WORD_BACKWARD = 'deleteWordBackward', + KILL_LINE_RIGHT = "killLineRight", + KILL_LINE_LEFT = "killLineLeft", + CLEAR_INPUT = "clearInput", + DELETE_WORD_BACKWARD = "deleteWordBackward", // Screen control - CLEAR_SCREEN = 'clearScreen', + CLEAR_SCREEN = "clearScreen", // History navigation - HISTORY_UP = 'historyUp', - HISTORY_DOWN = 'historyDown', - NAVIGATION_UP = 'navigationUp', - NAVIGATION_DOWN = 'navigationDown', + HISTORY_UP = "historyUp", + HISTORY_DOWN = "historyDown", + NAVIGATION_UP = "navigationUp", + NAVIGATION_DOWN = "navigationDown", // Auto-completion - ACCEPT_SUGGESTION = 'acceptSuggestion', - COMPLETION_UP = 'completionUp', - COMPLETION_DOWN = 'completionDown', + ACCEPT_SUGGESTION = "acceptSuggestion", + COMPLETION_UP = "completionUp", + COMPLETION_DOWN = "completionDown", // Text input - SUBMIT = 'submit', - NEWLINE = 'newline', + SUBMIT = "submit", + NEWLINE = "newline", // External tools - OPEN_EXTERNAL_EDITOR = 'openExternalEditor', - PASTE_CLIPBOARD_IMAGE = 'pasteClipboardImage', + OPEN_EXTERNAL_EDITOR = "openExternalEditor", + PASTE_CLIPBOARD_IMAGE = "pasteClipboardImage", // App level bindings - TOGGLE_TOOL_DESCRIPTIONS = 'toggleToolDescriptions', - TOGGLE_IDE_CONTEXT_DETAIL = 'toggleIDEContextDetail', - QUIT = 'quit', - EXIT = 'exit', - SHOW_MORE_LINES = 'showMoreLines', - RETRY_LAST = 'retryLast', - TOGGLE_VERBOSE_MODE = 'toggleVerboseMode', + TOGGLE_TOOL_DESCRIPTIONS = "toggleToolDescriptions", + TOGGLE_IDE_CONTEXT_DETAIL = "toggleIDEContextDetail", + QUIT = "quit", + EXIT = "exit", + SHOW_MORE_LINES = "showMoreLines", + RETRY_LAST = "retryLast", + TOGGLE_VERBOSE_MODE = "toggleVerboseMode", // Shell commands - REVERSE_SEARCH = 'reverseSearch', - SUBMIT_REVERSE_SEARCH = 'submitReverseSearch', - ACCEPT_SUGGESTION_REVERSE_SEARCH = 'acceptSuggestionReverseSearch', - TOGGLE_SHELL_INPUT_FOCUS = 'toggleShellInputFocus', + REVERSE_SEARCH = "reverseSearch", + SUBMIT_REVERSE_SEARCH = "submitReverseSearch", + ACCEPT_SUGGESTION_REVERSE_SEARCH = "acceptSuggestionReverseSearch", + TOGGLE_SHELL_INPUT_FOCUS = "toggleShellInputFocus", // Suggestion expansion - EXPAND_SUGGESTION = 'expandSuggestion', - COLLAPSE_SUGGESTION = 'collapseSuggestion', + EXPAND_SUGGESTION = "expandSuggestion", + COLLAPSE_SUGGESTION = "collapseSuggestion", } /** @@ -96,44 +96,44 @@ export type KeyBindingConfig = { */ export const defaultKeyBindings: KeyBindingConfig = { // Basic bindings - [Command.RETURN]: [{ key: 'return' }], - [Command.ESCAPE]: [{ key: 'escape' }], + [Command.RETURN]: [{ key: "return" }], + [Command.ESCAPE]: [{ key: "escape" }], // Cursor movement - [Command.HOME]: [{ key: 'a', ctrl: true }], - [Command.END]: [{ key: 'e', ctrl: true }], + [Command.HOME]: [{ key: "a", ctrl: true }], + [Command.END]: [{ key: "e", ctrl: true }], // Text deletion - [Command.KILL_LINE_RIGHT]: [{ key: 'k', ctrl: true }], - [Command.KILL_LINE_LEFT]: [{ key: 'u', ctrl: true }], - [Command.CLEAR_INPUT]: [{ key: 'c', ctrl: true }], + [Command.KILL_LINE_RIGHT]: [{ key: "k", ctrl: true }], + [Command.KILL_LINE_LEFT]: [{ key: "u", ctrl: true }], + [Command.CLEAR_INPUT]: [{ key: "c", ctrl: true }], // Added command (meta/alt/option) for mac compatibility [Command.DELETE_WORD_BACKWARD]: [ - { key: 'backspace', ctrl: true }, - { key: 'backspace', command: true }, + { key: "backspace", ctrl: true }, + { key: "backspace", command: true }, ], // Screen control - [Command.CLEAR_SCREEN]: [{ key: 'l', ctrl: true }], + [Command.CLEAR_SCREEN]: [{ key: "l", ctrl: true }], // History navigation - [Command.HISTORY_UP]: [{ key: 'p', ctrl: true }], - [Command.HISTORY_DOWN]: [{ key: 'n', ctrl: true }], - [Command.NAVIGATION_UP]: [{ key: 'up' }], - [Command.NAVIGATION_DOWN]: [{ key: 'down' }], + [Command.HISTORY_UP]: [{ key: "p", ctrl: true }], + [Command.HISTORY_DOWN]: [{ key: "n", ctrl: true }], + [Command.NAVIGATION_UP]: [{ key: "up" }], + [Command.NAVIGATION_DOWN]: [{ key: "down" }], // Auto-completion - [Command.ACCEPT_SUGGESTION]: [{ key: 'tab' }, { key: 'return', ctrl: false }], + [Command.ACCEPT_SUGGESTION]: [{ key: "tab" }, { key: "return", ctrl: false }], // Completion navigation uses only arrow keys // Ctrl+P/N are reserved for history navigation (HISTORY_UP/DOWN) - [Command.COMPLETION_UP]: [{ key: 'up' }], - [Command.COMPLETION_DOWN]: [{ key: 'down' }], + [Command.COMPLETION_UP]: [{ key: "up" }], + [Command.COMPLETION_DOWN]: [{ key: "down" }], // Text input // Must also exclude shift to allow shift+enter for newline [Command.SUBMIT]: [ { - key: 'return', + key: "return", ctrl: false, command: false, paste: false, @@ -143,46 +143,46 @@ export const defaultKeyBindings: KeyBindingConfig = { // Split into multiple data-driven bindings // Now also includes shift+enter for multi-line input [Command.NEWLINE]: [ - { key: 'return', ctrl: true }, - { key: 'return', command: true }, - { key: 'return', paste: true }, - { key: 'return', shift: true }, - { key: 'j', ctrl: true }, + { key: "return", ctrl: true }, + { key: "return", command: true }, + { key: "return", paste: true }, + { key: "return", shift: true }, + { key: "j", ctrl: true }, ], // External tools [Command.OPEN_EXTERNAL_EDITOR]: [ - { key: 'x', ctrl: true }, - { sequence: '\x18', ctrl: true }, + { key: "x", ctrl: true }, + { sequence: "\x18", ctrl: true }, ], [Command.PASTE_CLIPBOARD_IMAGE]: - process.platform === 'win32' + process.platform === "win32" ? [ - { key: 'v', command: true }, - { key: 'v', meta: true }, + { key: "v", command: true }, + { key: "v", meta: true }, ] : [ - { key: 'v', ctrl: true }, - { key: 'v', command: true }, + { key: "v", ctrl: true }, + { key: "v", command: true }, ], // App level bindings - [Command.TOGGLE_TOOL_DESCRIPTIONS]: [{ key: 't', ctrl: true }], - [Command.TOGGLE_IDE_CONTEXT_DETAIL]: [{ key: 'g', ctrl: true }], - [Command.QUIT]: [{ key: 'c', ctrl: true }], - [Command.EXIT]: [{ key: 'd', ctrl: true }], - [Command.SHOW_MORE_LINES]: [{ key: 's', ctrl: true }], - [Command.RETRY_LAST]: [{ key: 'y', ctrl: true }], - [Command.TOGGLE_VERBOSE_MODE]: [{ key: 'o', ctrl: true }], + [Command.TOGGLE_TOOL_DESCRIPTIONS]: [{ key: "t", ctrl: true }], + [Command.TOGGLE_IDE_CONTEXT_DETAIL]: [{ key: "g", ctrl: true }], + [Command.QUIT]: [{ key: "c", ctrl: true }], + [Command.EXIT]: [{ key: "d", ctrl: true }], + [Command.SHOW_MORE_LINES]: [{ key: "s", ctrl: true }], + [Command.RETRY_LAST]: [{ key: "y", ctrl: true }], + [Command.TOGGLE_VERBOSE_MODE]: [{ key: "o", ctrl: true }], // Shell commands - [Command.REVERSE_SEARCH]: [{ key: 'r', ctrl: true }], + [Command.REVERSE_SEARCH]: [{ key: "r", ctrl: true }], // Note: original logic ONLY checked ctrl=false, ignored meta/shift/paste - [Command.SUBMIT_REVERSE_SEARCH]: [{ key: 'return', ctrl: false }], - [Command.ACCEPT_SUGGESTION_REVERSE_SEARCH]: [{ key: 'tab' }], - [Command.TOGGLE_SHELL_INPUT_FOCUS]: [{ key: 'f', ctrl: true }], + [Command.SUBMIT_REVERSE_SEARCH]: [{ key: "return", ctrl: false }], + [Command.ACCEPT_SUGGESTION_REVERSE_SEARCH]: [{ key: "tab" }], + [Command.TOGGLE_SHELL_INPUT_FOCUS]: [{ key: "f", ctrl: true }], // Suggestion expansion - [Command.EXPAND_SUGGESTION]: [{ key: 'right' }], - [Command.COLLAPSE_SUGGESTION]: [{ key: 'left' }], + [Command.EXPAND_SUGGESTION]: [{ key: "right" }], + [Command.COLLAPSE_SUGGESTION]: [{ key: "left" }], }; diff --git a/apps/airiscode-cli/src/config/migration/index.ts b/apps/airiscode-cli/src/config/migration/index.ts index 40d176cbe..580d05bd9 100644 --- a/apps/airiscode-cli/src/config/migration/index.ts +++ b/apps/airiscode-cli/src/config/migration/index.ts @@ -4,26 +4,24 @@ * SPDX-License-Identifier: Apache-2.0 */ -// Export types -export type { SettingsMigration, MigrationResult } from './types.js'; - // Export scheduler -export { MigrationScheduler } from './scheduler.js'; +export { MigrationScheduler } from "./scheduler.js"; +// Export types +export type { MigrationResult, SettingsMigration } from "./types.js"; // Export migrations -export { v1ToV2Migration, V1ToV2Migration } from './versions/v1-to-v2.js'; -export { v2ToV3Migration, V2ToV3Migration } from './versions/v2-to-v3.js'; +export { V1ToV2Migration, v1ToV2Migration } from "./versions/v1-to-v2.js"; +export { V2ToV3Migration, v2ToV3Migration } from "./versions/v2-to-v3.js"; // Import settings version from single source of truth -import { SETTINGS_VERSION } from '../settings.js'; - +import { SETTINGS_VERSION } from "../settings.js"; +import { MigrationScheduler } from "./scheduler.js"; +import type { MigrationResult } from "./types.js"; // Ordered array of all migrations for use with MigrationScheduler // Each migration handles one version transition (N → N+1) // Order matters: migrations must be sorted by ascending version -import { v1ToV2Migration } from './versions/v1-to-v2.js'; -import { v2ToV3Migration } from './versions/v2-to-v3.js'; -import { MigrationScheduler } from './scheduler.js'; -import type { MigrationResult } from './types.js'; +import { v1ToV2Migration } from "./versions/v1-to-v2.js"; +import { v2ToV3Migration } from "./versions/v2-to-v3.js"; /** * Ordered array of all settings migrations. @@ -53,10 +51,7 @@ export const ALL_MIGRATIONS = [v1ToV2Migration, v2ToV3Migration] as const; * } * ``` */ -export function runMigrations( - settings: unknown, - scope: string, -): MigrationResult { +export function runMigrations(settings: unknown, scope: string): MigrationResult { const scheduler = new MigrationScheduler([...ALL_MIGRATIONS], scope); return scheduler.migrate(settings); } @@ -81,18 +76,18 @@ export function runMigrations( * @returns true if migration is needed, false otherwise */ export function needsMigration(settings: unknown): boolean { - if (typeof settings !== 'object' || settings === null) { + if (typeof settings !== "object" || settings === null) { return false; } const s = settings as Record; - const version = s['$version']; + const version = s["$version"]; const hasApplicableMigration = ALL_MIGRATIONS.some((migration) => migration.shouldMigrate(settings), ); // If $version is a valid number, use version comparison - if (typeof version === 'number') { + if (typeof version === "number") { if (version >= SETTINGS_VERSION) { return false; } diff --git a/apps/airiscode-cli/src/config/migration/scheduler.ts b/apps/airiscode-cli/src/config/migration/scheduler.ts index 8cdbe8214..9b586e01f 100644 --- a/apps/airiscode-cli/src/config/migration/scheduler.ts +++ b/apps/airiscode-cli/src/config/migration/scheduler.ts @@ -4,10 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { createDebugLogger } from '@airiscode/core'; -import type { SettingsMigration, MigrationResult } from './types.js'; +import { createDebugLogger } from "@airiscode/core"; +import type { MigrationResult, SettingsMigration } from "./types.js"; -const debugLogger = createDebugLogger('SETTINGS_MIGRATION'); +const debugLogger = createDebugLogger("SETTINGS_MIGRATION"); /** * Formats a SettingScope enum value to a human-readable string. @@ -15,8 +15,8 @@ const debugLogger = createDebugLogger('SETTINGS_MIGRATION'); * - Special case: 'SystemDefaults' -> 'system default' */ export function formatScope(scope: string): string { - if (scope === 'SystemDefaults') { - return 'system default'; + if (scope === "SystemDefaults") { + return "system default"; } return scope.toLowerCase(); } @@ -61,7 +61,7 @@ export class MigrationScheduler { * @returns MigrationResult containing the final settings, version, and execution log */ migrate(settings: unknown): MigrationResult { - debugLogger.debug('MigrationScheduler: Starting migration chain'); + debugLogger.debug("MigrationScheduler: Starting migration chain"); let current = settings; const executed: Array<{ fromVersion: number; toVersion: number }> = []; @@ -98,8 +98,7 @@ export class MigrationScheduler { } // Determine final version from the settings object - const finalVersion = - ((current as Record)['$version'] as number) ?? 1; + const finalVersion = ((current as Record)["$version"] as number) ?? 1; debugLogger.debug( `MigrationScheduler: Migration chain complete. Final version: ${finalVersion}, Executed: ${executed.length} migrations`, diff --git a/apps/airiscode-cli/src/config/migration/types.ts b/apps/airiscode-cli/src/config/migration/types.ts index ca1e23aaf..f87bdc27e 100644 --- a/apps/airiscode-cli/src/config/migration/types.ts +++ b/apps/airiscode-cli/src/config/migration/types.ts @@ -34,10 +34,7 @@ export interface SettingsMigration { * @returns The migrated settings object of version N+1 with optional warnings * @throws Error if the migration fails */ - migrate( - settings: unknown, - scope: string, - ): { settings: unknown; warnings: string[] }; + migrate(settings: unknown, scope: string): { settings: unknown; warnings: string[] }; } /** diff --git a/apps/airiscode-cli/src/config/migration/versions/v1-to-v2-shared.ts b/apps/airiscode-cli/src/config/migration/versions/v1-to-v2-shared.ts index c63979f35..550bf5401 100644 --- a/apps/airiscode-cli/src/config/migration/versions/v1-to-v2-shared.ts +++ b/apps/airiscode-cli/src/config/migration/versions/v1-to-v2-shared.ts @@ -12,65 +12,65 @@ * - warnings for residual legacy keys in latest-version settings files */ export const V1_TO_V2_MIGRATION_MAP: Record = { - accessibility: 'ui.accessibility', - allowedTools: 'tools.allowed', - allowMCPServers: 'mcp.allowed', - autoAccept: 'tools.autoAccept', - autoConfigureMaxOldSpaceSize: 'advanced.autoConfigureMemory', - bugCommand: 'advanced.bugCommand', - chatCompression: 'model.chatCompression', - checkpointing: 'general.checkpointing', - coreTools: 'tools.core', - contextFileName: 'context.fileName', - customThemes: 'ui.customThemes', - customWittyPhrases: 'ui.customWittyPhrases', - debugKeystrokeLogging: 'general.debugKeystrokeLogging', - dnsResolutionOrder: 'advanced.dnsResolutionOrder', - enforcedAuthType: 'security.auth.enforcedType', - excludeTools: 'tools.exclude', - excludeMCPServers: 'mcp.excluded', - excludedProjectEnvVars: 'advanced.excludedEnvVars', - extensions: 'extensions', - fileFiltering: 'context.fileFiltering', - folderTrustFeature: 'security.folderTrust.featureEnabled', - folderTrust: 'security.folderTrust.enabled', - hasSeenIdeIntegrationNudge: 'ide.hasSeenNudge', - hideWindowTitle: 'ui.hideWindowTitle', - showStatusInTitle: 'ui.showStatusInTitle', - hideTips: 'ui.hideTips', - showLineNumbers: 'ui.showLineNumbers', - showCitations: 'ui.showCitations', - ideMode: 'ide.enabled', - includeDirectories: 'context.includeDirectories', - loadMemoryFromIncludeDirectories: 'context.loadFromIncludeDirectories', - maxSessionTurns: 'model.maxSessionTurns', - mcpServers: 'mcpServers', - mcpServerCommand: 'mcp.serverCommand', - memoryImportFormat: 'context.importFormat', - model: 'model.name', - preferredEditor: 'general.preferredEditor', - sandbox: 'tools.sandbox', - selectedAuthType: 'security.auth.selectedType', - shouldUseNodePtyShell: 'tools.shell.enableInteractiveShell', - shellPager: 'tools.shell.pager', - shellShowColor: 'tools.shell.showColor', - skipNextSpeakerCheck: 'model.skipNextSpeakerCheck', - telemetry: 'telemetry', - theme: 'ui.theme', - toolDiscoveryCommand: 'tools.discoveryCommand', - toolCallCommand: 'tools.callCommand', - usageStatisticsEnabled: 'privacy.usageStatisticsEnabled', - useExternalAuth: 'security.auth.useExternal', - useRipgrep: 'tools.useRipgrep', - vimMode: 'general.vimMode', - enableWelcomeBack: 'ui.enableWelcomeBack', - approvalMode: 'tools.approvalMode', - sessionTokenLimit: 'model.sessionTokenLimit', - contentGenerator: 'model.generationConfig', - skipLoopDetection: 'model.skipLoopDetection', - skipStartupContext: 'model.skipStartupContext', - enableOpenAILogging: 'model.enableOpenAILogging', - tavilyApiKey: 'advanced.tavilyApiKey', + accessibility: "ui.accessibility", + allowedTools: "tools.allowed", + allowMCPServers: "mcp.allowed", + autoAccept: "tools.autoAccept", + autoConfigureMaxOldSpaceSize: "advanced.autoConfigureMemory", + bugCommand: "advanced.bugCommand", + chatCompression: "model.chatCompression", + checkpointing: "general.checkpointing", + coreTools: "tools.core", + contextFileName: "context.fileName", + customThemes: "ui.customThemes", + customWittyPhrases: "ui.customWittyPhrases", + debugKeystrokeLogging: "general.debugKeystrokeLogging", + dnsResolutionOrder: "advanced.dnsResolutionOrder", + enforcedAuthType: "security.auth.enforcedType", + excludeTools: "tools.exclude", + excludeMCPServers: "mcp.excluded", + excludedProjectEnvVars: "advanced.excludedEnvVars", + extensions: "extensions", + fileFiltering: "context.fileFiltering", + folderTrustFeature: "security.folderTrust.featureEnabled", + folderTrust: "security.folderTrust.enabled", + hasSeenIdeIntegrationNudge: "ide.hasSeenNudge", + hideWindowTitle: "ui.hideWindowTitle", + showStatusInTitle: "ui.showStatusInTitle", + hideTips: "ui.hideTips", + showLineNumbers: "ui.showLineNumbers", + showCitations: "ui.showCitations", + ideMode: "ide.enabled", + includeDirectories: "context.includeDirectories", + loadMemoryFromIncludeDirectories: "context.loadFromIncludeDirectories", + maxSessionTurns: "model.maxSessionTurns", + mcpServers: "mcpServers", + mcpServerCommand: "mcp.serverCommand", + memoryImportFormat: "context.importFormat", + model: "model.name", + preferredEditor: "general.preferredEditor", + sandbox: "tools.sandbox", + selectedAuthType: "security.auth.selectedType", + shouldUseNodePtyShell: "tools.shell.enableInteractiveShell", + shellPager: "tools.shell.pager", + shellShowColor: "tools.shell.showColor", + skipNextSpeakerCheck: "model.skipNextSpeakerCheck", + telemetry: "telemetry", + theme: "ui.theme", + toolDiscoveryCommand: "tools.discoveryCommand", + toolCallCommand: "tools.callCommand", + usageStatisticsEnabled: "privacy.usageStatisticsEnabled", + useExternalAuth: "security.auth.useExternal", + useRipgrep: "tools.useRipgrep", + vimMode: "general.vimMode", + enableWelcomeBack: "ui.enableWelcomeBack", + approvalMode: "tools.approvalMode", + sessionTokenLimit: "model.sessionTokenLimit", + contentGenerator: "model.generationConfig", + skipLoopDetection: "model.skipLoopDetection", + skipStartupContext: "model.skipStartupContext", + enableOpenAILogging: "model.enableOpenAILogging", + tavilyApiKey: "advanced.tavilyApiKey", }; /** @@ -78,101 +78,98 @@ export const V1_TO_V2_MIGRATION_MAP: Record = { * If one of these keys already has object value, treat it as latest-format data. */ export const V2_CONTAINER_KEYS = new Set([ - 'ui', - 'tools', - 'mcp', - 'advanced', - 'model', - 'general', - 'context', - 'security', - 'ide', - 'privacy', - 'telemetry', - 'extensions', + "ui", + "tools", + "mcp", + "advanced", + "model", + "general", + "context", + "security", + "ide", + "privacy", + "telemetry", + "extensions", ]); /** * Legacy disable* keys that remain in disable* form for V2. */ export const V1_TO_V2_PRESERVE_DISABLE_MAP: Record = { - disableAutoUpdate: 'general.disableAutoUpdate', - disableUpdateNag: 'general.disableUpdateNag', - disableLoadingPhrases: 'ui.accessibility.disableLoadingPhrases', - disableFuzzySearch: 'context.fileFiltering.disableFuzzySearch', - disableCacheControl: 'model.generationConfig.disableCacheControl', + disableAutoUpdate: "general.disableAutoUpdate", + disableUpdateNag: "general.disableUpdateNag", + disableLoadingPhrases: "ui.accessibility.disableLoadingPhrases", + disableFuzzySearch: "context.fileFiltering.disableFuzzySearch", + disableCacheControl: "model.generationConfig.disableCacheControl", }; -export const CONSOLIDATED_DISABLE_KEYS = new Set([ - 'disableAutoUpdate', - 'disableUpdateNag', -]); +export const CONSOLIDATED_DISABLE_KEYS = new Set(["disableAutoUpdate", "disableUpdateNag"]); /** * Keys that indicate V1-like top-level structure when holding primitive values. */ export const V1_INDICATOR_KEYS = [ // From V1_TO_V2_MIGRATION_MAP - keys that map to different paths in V2 - 'theme', - 'model', - 'autoAccept', - 'hideTips', - 'vimMode', - 'checkpointing', - 'accessibility', - 'allowedTools', - 'allowMCPServers', - 'autoConfigureMaxOldSpaceSize', - 'bugCommand', - 'chatCompression', - 'coreTools', - 'contextFileName', - 'customThemes', - 'customWittyPhrases', - 'debugKeystrokeLogging', - 'dnsResolutionOrder', - 'enforcedAuthType', - 'excludeTools', - 'excludeMCPServers', - 'excludedProjectEnvVars', - 'fileFiltering', - 'folderTrustFeature', - 'folderTrust', - 'hasSeenIdeIntegrationNudge', - 'hideWindowTitle', - 'showStatusInTitle', - 'showLineNumbers', - 'showCitations', - 'ideMode', - 'includeDirectories', - 'loadMemoryFromIncludeDirectories', - 'maxSessionTurns', - 'mcpServerCommand', - 'memoryImportFormat', - 'preferredEditor', - 'sandbox', - 'selectedAuthType', - 'shouldUseNodePtyShell', - 'shellPager', - 'shellShowColor', - 'skipNextSpeakerCheck', - 'toolDiscoveryCommand', - 'toolCallCommand', - 'usageStatisticsEnabled', - 'useExternalAuth', - 'useRipgrep', - 'enableWelcomeBack', - 'approvalMode', - 'sessionTokenLimit', - 'contentGenerator', - 'skipLoopDetection', - 'skipStartupContext', - 'enableOpenAILogging', - 'tavilyApiKey', + "theme", + "model", + "autoAccept", + "hideTips", + "vimMode", + "checkpointing", + "accessibility", + "allowedTools", + "allowMCPServers", + "autoConfigureMaxOldSpaceSize", + "bugCommand", + "chatCompression", + "coreTools", + "contextFileName", + "customThemes", + "customWittyPhrases", + "debugKeystrokeLogging", + "dnsResolutionOrder", + "enforcedAuthType", + "excludeTools", + "excludeMCPServers", + "excludedProjectEnvVars", + "fileFiltering", + "folderTrustFeature", + "folderTrust", + "hasSeenIdeIntegrationNudge", + "hideWindowTitle", + "showStatusInTitle", + "showLineNumbers", + "showCitations", + "ideMode", + "includeDirectories", + "loadMemoryFromIncludeDirectories", + "maxSessionTurns", + "mcpServerCommand", + "memoryImportFormat", + "preferredEditor", + "sandbox", + "selectedAuthType", + "shouldUseNodePtyShell", + "shellPager", + "shellShowColor", + "skipNextSpeakerCheck", + "toolDiscoveryCommand", + "toolCallCommand", + "usageStatisticsEnabled", + "useExternalAuth", + "useRipgrep", + "enableWelcomeBack", + "approvalMode", + "sessionTokenLimit", + "contentGenerator", + "skipLoopDetection", + "skipStartupContext", + "enableOpenAILogging", + "tavilyApiKey", // From V1_TO_V2_PRESERVE_DISABLE_MAP - disable* keys that get nested in V2 - 'disableAutoUpdate', - 'disableUpdateNag', - 'disableLoadingPhrases', - 'disableFuzzySearch', - 'disableCacheControl', + "disableAutoUpdate", + "disableUpdateNag", + "disableLoadingPhrases", + "disableFuzzySearch", + "disableCacheControl", ]; diff --git a/apps/airiscode-cli/src/config/migration/versions/v1-to-v2.ts b/apps/airiscode-cli/src/config/migration/versions/v1-to-v2.ts index 4dceffe44..fc5f5c518 100644 --- a/apps/airiscode-cli/src/config/migration/versions/v1-to-v2.ts +++ b/apps/airiscode-cli/src/config/migration/versions/v1-to-v2.ts @@ -4,15 +4,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { SettingsMigration } from '../types.js'; +import { setNestedPropertySafe } from "../../../utils/settingsUtils.js"; +import type { SettingsMigration } from "../types.js"; import { CONSOLIDATED_DISABLE_KEYS, V1_INDICATOR_KEYS, V1_TO_V2_MIGRATION_MAP, V1_TO_V2_PRESERVE_DISABLE_MAP, V2_CONTAINER_KEYS, -} from './v1-to-v2-shared.js'; -import { setNestedPropertySafe } from '../../../utils/settingsUtils.js'; +} from "./v1-to-v2-shared.js"; /** * Heuristic indicators for deciding whether an object is "V1-like". @@ -64,15 +64,15 @@ export class V1ToV2Migration implements SettingsMigration { * are ignored while legacy-shaped indicators can still trigger migration. */ shouldMigrate(settings: unknown): boolean { - if (typeof settings !== 'object' || settings === null) { + if (typeof settings !== "object" || settings === null) { return false; } const s = settings as Record; // If $version exists and is a number >= 2, it's not V1 - const version = s['$version']; - if (typeof version === 'number' && version >= 2) { + const version = s["$version"]; + if (typeof version === "number" && version >= 2) { return false; } @@ -87,11 +87,7 @@ export class V1ToV2Migration implements SettingsMigration { const value = s[key]; // Skip keys with object values - they may already be in V2 nested format // But don't let them block migration of other keys - if ( - typeof value === 'object' && - value !== null && - !Array.isArray(value) - ) { + if (typeof value === "object" && value !== null && !Array.isArray(value)) { // This key appears to be in V2 format, skip it but continue // checking other keys return false; @@ -121,12 +117,9 @@ export class V1ToV2Migration implements SettingsMigration { * * The method is pure with respect to input mutation. */ - migrate( - settings: unknown, - _scope: string, - ): { settings: unknown; warnings: string[] } { - if (typeof settings !== 'object' || settings === null) { - throw new Error('Settings must be an object'); + migrate(settings: unknown, _scope: string): { settings: unknown; warnings: string[] } { + if (typeof settings !== "object" || settings === null) { + throw new Error("Settings must be an object"); } const source = settings as Record; @@ -144,7 +137,7 @@ export class V1ToV2Migration implements SettingsMigration { // to prevent double-nesting (e.g., model.name.name). if ( V2_CONTAINER_KEYS.has(v1Key) && - typeof value === 'object' && + typeof value === "object" && value !== null && !Array.isArray(value) ) { @@ -157,18 +150,14 @@ export class V1ToV2Migration implements SettingsMigration { // If value is already an object and the path matches the key, // it might be a partial V2 structure. Merge its contents. if ( - typeof value === 'object' && + typeof value === "object" && value !== null && !Array.isArray(value) && - v2Path.startsWith(v1Key + '.') + v2Path.startsWith(v1Key + ".") ) { // Merge nested properties from this partial V2 structure for (const [nestedKey, nestedValue] of Object.entries(value)) { - setNestedPropertySafe( - result, - `${v2Path}.${nestedKey}`, - nestedValue, - ); + setNestedPropertySafe(result, `${v2Path}.${nestedKey}`, nestedValue); } } else { setNestedPropertySafe(result, v2Path, value); @@ -178,16 +167,14 @@ export class V1ToV2Migration implements SettingsMigration { } // Step 2: Map V1 disable* keys to V2 nested disable* paths - for (const [v1Key, v2Path] of Object.entries( - V1_TO_V2_PRESERVE_DISABLE_MAP, - )) { + for (const [v1Key, v2Path] of Object.entries(V1_TO_V2_PRESERVE_DISABLE_MAP)) { if (v1Key in source) { const value = source[v1Key]; if (CONSOLIDATED_DISABLE_KEYS.has(v1Key)) { // Preserve stable behavior: consolidated keys use presence semantics. // Only literal true remains true; all other present values become false. setNestedPropertySafe(result, v2Path, value === true); - } else if (typeof value === 'boolean') { + } else if (typeof value === "boolean") { // Non-consolidated disable* keys only migrate when explicitly boolean. setNestedPropertySafe(result, v2Path, value); } @@ -196,9 +183,9 @@ export class V1ToV2Migration implements SettingsMigration { } // Step 3: Preserve mcpServers at the top level - if ('mcpServers' in source) { - result['mcpServers'] = source['mcpServers']; - processedKeys.add('mcpServers'); + if ("mcpServers" in source) { + result["mcpServers"] = source["mcpServers"]; + processedKeys.add("mcpServers"); } // Step 4: Carry over any unrecognized keys (including unknown nested objects) @@ -207,40 +194,33 @@ export class V1ToV2Migration implements SettingsMigration { for (const key of Object.keys(source)) { if (!processedKeys.has(key)) { // Check if this key is a parent of any already-migrated path - const isParentOfMigratedPath = Array.from(processedKeys).some( - (processedKey) => { - // Get the v2 path for this processed key - const v2Path = - V1_TO_V2_MIGRATION_MAP[processedKey] || - V1_TO_V2_PRESERVE_DISABLE_MAP[processedKey]; - if (!v2Path) return false; - // Check if the v2 path starts with this key + '.' - return v2Path.startsWith(key + '.'); - }, - ); + const isParentOfMigratedPath = Array.from(processedKeys).some((processedKey) => { + // Get the v2 path for this processed key + const v2Path = + V1_TO_V2_MIGRATION_MAP[processedKey] || V1_TO_V2_PRESERVE_DISABLE_MAP[processedKey]; + if (!v2Path) return false; + // Check if the v2 path starts with this key + '.' + return v2Path.startsWith(key + "."); + }); if (isParentOfMigratedPath) { // This key is a parent of an already-migrated path // Merge its unprocessed children instead of overwriting const existingValue = source[key]; if ( - typeof existingValue === 'object' && + typeof existingValue === "object" && existingValue !== null && !Array.isArray(existingValue) ) { - for (const [nestedKey, nestedValue] of Object.entries( - existingValue, - )) { + for (const [nestedKey, nestedValue] of Object.entries(existingValue)) { // Only merge if this nested key wasn't already processed const fullNestedPath = `${key}.${nestedKey}`; - const wasProcessed = Array.from(processedKeys).some( - (processedKey) => { - const v2Path = - V1_TO_V2_MIGRATION_MAP[processedKey] || - V1_TO_V2_PRESERVE_DISABLE_MAP[processedKey]; - return v2Path === fullNestedPath; - }, - ); + const wasProcessed = Array.from(processedKeys).some((processedKey) => { + const v2Path = + V1_TO_V2_MIGRATION_MAP[processedKey] || + V1_TO_V2_PRESERVE_DISABLE_MAP[processedKey]; + return v2Path === fullNestedPath; + }); if (!wasProcessed) { setNestedPropertySafe(result, fullNestedPath, nestedValue); } @@ -257,7 +237,7 @@ export class V1ToV2Migration implements SettingsMigration { } // Step 5: Set version to 2 - result['$version'] = 2; + result["$version"] = 2; return { settings: result, warnings }; } diff --git a/apps/airiscode-cli/src/config/migration/versions/v2-to-v3.ts b/apps/airiscode-cli/src/config/migration/versions/v2-to-v3.ts index 6c0133443..f75810306 100644 --- a/apps/airiscode-cli/src/config/migration/versions/v2-to-v3.ts +++ b/apps/airiscode-cli/src/config/migration/versions/v2-to-v3.ts @@ -4,12 +4,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { SettingsMigration } from '../types.js'; import { deleteNestedPropertySafe, getNestedProperty, setNestedPropertySafe, -} from '../../../utils/settingsUtils.js'; +} from "../../../utils/settingsUtils.js"; +import type { SettingsMigration } from "../types.js"; /** * Path mapping for boolean polarity migration (V2 disable* -> V3 enable*). @@ -24,14 +24,11 @@ import { * - Invalid values do not create enable* keys and produce warnings. */ const V2_TO_V3_BOOLEAN_MAP: Record = { - 'general.disableAutoUpdate': 'general.enableAutoUpdate', - 'general.disableUpdateNag': 'general.enableAutoUpdate', - 'ui.accessibility.disableLoadingPhrases': - 'ui.accessibility.enableLoadingPhrases', - 'context.fileFiltering.disableFuzzySearch': - 'context.fileFiltering.enableFuzzySearch', - 'model.generationConfig.disableCacheControl': - 'model.generationConfig.enableCacheControl', + "general.disableAutoUpdate": "general.enableAutoUpdate", + "general.disableUpdateNag": "general.enableAutoUpdate", + "ui.accessibility.disableLoadingPhrases": "ui.accessibility.enableLoadingPhrases", + "context.fileFiltering.disableFuzzySearch": "context.fileFiltering.enableFuzzySearch", + "model.generationConfig.disableCacheControl": "model.generationConfig.enableCacheControl", }; /** @@ -45,10 +42,7 @@ const V2_TO_V3_BOOLEAN_MAP: Record = { * - Invalid present values are removed and warned, and do not contribute to target calculation. */ const CONSOLIDATED_V2_PATHS: Record = { - 'general.enableAutoUpdate': [ - 'general.disableAutoUpdate', - 'general.disableUpdateNag', - ], + "general.enableAutoUpdate": ["general.disableAutoUpdate", "general.disableUpdateNag"], }; /** @@ -67,15 +61,15 @@ function normalizeDisableValue(value: unknown): { if (value === undefined) { return { isPresent: false, isValid: false }; } - if (typeof value === 'boolean') { + if (typeof value === "boolean") { return { isPresent: true, isValid: true, booleanValue: value }; } - if (typeof value === 'string') { + if (typeof value === "string") { const normalized = value.trim().toLowerCase(); - if (normalized === 'true') { + if (normalized === "true") { return { isPresent: true, isValid: true, booleanValue: true }; } - if (normalized === 'false') { + if (normalized === "false") { return { isPresent: true, isValid: true, booleanValue: false }; } } @@ -108,14 +102,14 @@ export class V2ToV3Migration implements SettingsMigration { * metadata still advances to 3. */ shouldMigrate(settings: unknown): boolean { - if (typeof settings !== 'object' || settings === null) { + if (typeof settings !== "object" || settings === null) { return false; } const s = settings as Record; // Migrate if $version is 2 - return s['$version'] === 2; + return s["$version"] === 2; } /** @@ -141,12 +135,9 @@ export class V2ToV3Migration implements SettingsMigration { * - Valid migration and invalid cleanup are deterministic. * - Deprecated disable* keys are not retained after migration. */ - migrate( - settings: unknown, - scope: string, - ): { settings: unknown; warnings: string[] } { - if (typeof settings !== 'object' || settings === null) { - throw new Error('Settings must be an object'); + migrate(settings: unknown, scope: string): { settings: unknown; warnings: string[] } { + if (typeof settings !== "object" || settings === null) { + throw new Error("Settings must be an object"); } // Deep clone to avoid mutating input @@ -212,7 +203,7 @@ export class V2ToV3Migration implements SettingsMigration { } // Step 3: Always update version to 3 - result['$version'] = 3; + result["$version"] = 3; return { settings: result, warnings }; } diff --git a/apps/airiscode-cli/src/config/modelProvidersScope.ts b/apps/airiscode-cli/src/config/modelProvidersScope.ts index 136141103..2d3ee6af0 100644 --- a/apps/airiscode-cli/src/config/modelProvidersScope.ts +++ b/apps/airiscode-cli/src/config/modelProvidersScope.ts @@ -4,16 +4,16 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { SettingScope, type LoadedSettings } from './settings.js'; +import { type LoadedSettings, SettingScope } from "./settings.js"; function hasOwnModelProviders(settingsObj: unknown): boolean { - if (!settingsObj || typeof settingsObj !== 'object') { + if (!settingsObj || typeof settingsObj !== "object") { return false; } const obj = settingsObj as Record; // Treat an explicitly configured empty object (modelProviders: {}) as "owned" // by this scope, which is important when mergeStrategy is REPLACE. - return Object.prototype.hasOwnProperty.call(obj, 'modelProviders'); + return Object.hasOwn(obj, "modelProviders"); } /** @@ -22,9 +22,7 @@ function hasOwnModelProviders(settingsObj: unknown): boolean { * * Note: Workspace scope is only considered when the workspace is trusted. */ -export function getModelProvidersOwnerScope( - settings: LoadedSettings, -): SettingScope | undefined { +export function getModelProvidersOwnerScope(settings: LoadedSettings): SettingScope | undefined { if (settings.isTrusted && hasOwnModelProviders(settings.workspace.settings)) { return SettingScope.Workspace; } @@ -41,8 +39,6 @@ export function getModelProvidersOwnerScope( * Prefer persisting back to the scope that contains the effective modelProviders * config, otherwise fall back to the legacy trust-based heuristic. */ -export function getPersistScopeForModelSelection( - settings: LoadedSettings, -): SettingScope { +export function getPersistScopeForModelSelection(settings: LoadedSettings): SettingScope { return getModelProvidersOwnerScope(settings) ?? SettingScope.User; } diff --git a/apps/airiscode-cli/src/config/sandboxConfig.ts b/apps/airiscode-cli/src/config/sandboxConfig.ts index bc5248a41..eadef8b61 100644 --- a/apps/airiscode-cli/src/config/sandboxConfig.ts +++ b/apps/airiscode-cli/src/config/sandboxConfig.ts @@ -4,12 +4,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { SandboxConfig } from '@airiscode/core'; -import { FatalSandboxError } from '@airiscode/core'; -import commandExists from 'command-exists'; -import * as os from 'node:os'; -import { getPackageJson } from '../utils/package.js'; -import type { Settings } from './settings.js'; +import * as os from "node:os"; +import type { SandboxConfig } from "@airiscode/core"; +import { FatalSandboxError } from "@airiscode/core"; +import commandExists from "command-exists"; +import { getPackageJson } from "../utils/package.js"; +import type { Settings } from "./settings.js"; // This is a stripped-down version of the CliArgs interface from config.ts // to avoid circular dependencies. @@ -18,74 +18,64 @@ interface SandboxCliArgs { sandboxImage?: string; } -const VALID_SANDBOX_COMMANDS: ReadonlyArray = [ - 'docker', - 'podman', - 'sandbox-exec', +const VALID_SANDBOX_COMMANDS: ReadonlyArray = [ + "docker", + "podman", + "sandbox-exec", ]; -function isSandboxCommand(value: string): value is SandboxConfig['command'] { +function isSandboxCommand(value: string): value is SandboxConfig["command"] { return (VALID_SANDBOX_COMMANDS as readonly string[]).includes(value); } -function getSandboxCommand( - sandbox?: boolean | string, -): SandboxConfig['command'] | '' { +function getSandboxCommand(sandbox?: boolean | string): SandboxConfig["command"] | "" { // If the SANDBOX env var is set, we're already inside the sandbox. - if (process.env['SANDBOX']) { - return ''; + if (process.env["SANDBOX"]) { + return ""; } // note environment variable takes precedence over argument (from command line or settings) - const environmentConfiguredSandbox = - process.env['AIRISCODE_SANDBOX']?.toLowerCase().trim() ?? ''; - sandbox = - environmentConfiguredSandbox?.length > 0 - ? environmentConfiguredSandbox - : sandbox; - if (sandbox === '1' || sandbox === 'true') sandbox = true; - else if (sandbox === '0' || sandbox === 'false' || !sandbox) sandbox = false; + const environmentConfiguredSandbox = process.env["AIRISCODE_SANDBOX"]?.toLowerCase().trim() ?? ""; + sandbox = environmentConfiguredSandbox?.length > 0 ? environmentConfiguredSandbox : sandbox; + if (sandbox === "1" || sandbox === "true") sandbox = true; + else if (sandbox === "0" || sandbox === "false" || !sandbox) sandbox = false; if (sandbox === false) { - return ''; + return ""; } - if (typeof sandbox === 'string' && sandbox) { + if (typeof sandbox === "string" && sandbox) { if (!isSandboxCommand(sandbox)) { throw new FatalSandboxError( - `Invalid sandbox command '${sandbox}'. Must be one of ${VALID_SANDBOX_COMMANDS.join( - ', ', - )}`, + `Invalid sandbox command '${sandbox}'. Must be one of ${VALID_SANDBOX_COMMANDS.join(", ")}`, ); } // confirm that specified command exists if (commandExists.sync(sandbox)) { return sandbox; } - throw new FatalSandboxError( - `Missing sandbox command '${sandbox}' (from AIRISCODE_SANDBOX)`, - ); + throw new FatalSandboxError(`Missing sandbox command '${sandbox}' (from AIRISCODE_SANDBOX)`); } // look for seatbelt, docker, or podman, in that order // for container-based sandboxing, require sandbox to be enabled explicitly - if (os.platform() === 'darwin' && commandExists.sync('sandbox-exec')) { - return 'sandbox-exec'; - } else if (commandExists.sync('docker') && sandbox === true) { - return 'docker'; - } else if (commandExists.sync('podman') && sandbox === true) { - return 'podman'; + if (os.platform() === "darwin" && commandExists.sync("sandbox-exec")) { + return "sandbox-exec"; + } else if (commandExists.sync("docker") && sandbox === true) { + return "docker"; + } else if (commandExists.sync("podman") && sandbox === true) { + return "podman"; } // throw an error if user requested sandbox but no command was found if (sandbox === true) { throw new FatalSandboxError( - 'AIRISCODE_SANDBOX is true but failed to determine command for sandbox; ' + - 'install docker or podman or specify command in AIRISCODE_SANDBOX', + "AIRISCODE_SANDBOX is true but failed to determine command for sandbox; " + + "install docker or podman or specify command in AIRISCODE_SANDBOX", ); } - return ''; + return ""; } export async function loadSandboxConfig( @@ -98,7 +88,7 @@ export async function loadSandboxConfig( const packageJson = await getPackageJson(); const image = argv.sandboxImage ?? - process.env['AIRISCODE_SANDBOX_IMAGE'] ?? + process.env["AIRISCODE_SANDBOX_IMAGE"] ?? packageJson?.config?.sandboxImageUri; return command && image ? { command, image } : undefined; diff --git a/apps/airiscode-cli/src/config/settings.ts b/apps/airiscode-cli/src/config/settings.ts index 06f33f480..364e69722 100644 --- a/apps/airiscode-cli/src/config/settings.ts +++ b/apps/airiscode-cli/src/config/settings.ts @@ -4,42 +4,39 @@ * SPDX-License-Identifier: Apache-2.0 */ -import * as fs from 'node:fs'; -import * as path from 'node:path'; -import { homedir, platform } from 'node:os'; -import * as dotenv from 'dotenv'; -import process from 'node:process'; +import * as fs from "node:fs"; +import { homedir, platform } from "node:os"; +import * as path from "node:path"; +import process from "node:process"; import { - FatalConfigError, AIRISCODE_DIR, + createDebugLogger, + FatalConfigError, getErrorMessage, Storage, - createDebugLogger, -} from '@airiscode/core'; -import stripJsonComments from 'strip-json-comments'; -import { DefaultLight } from '../ui/themes/default-light.js'; -import { DefaultDark } from '../ui/themes/default.js'; -import { isWorkspaceTrusted } from './trustedFolders.js'; +} from "@airiscode/core"; +import * as dotenv from "dotenv"; +import stripJsonComments from "strip-json-comments"; +import { DefaultDark } from "../ui/themes/default.js"; +import { DefaultLight } from "../ui/themes/default-light.js"; +import { updateSettingsFilePreservingFormat } from "../utils/commentJson.js"; +import { customDeepMerge } from "../utils/deepMerge.js"; +import { resolveEnvVarsInObject } from "../utils/envVarResolver.js"; +import { setNestedPropertySafe } from "../utils/settingsUtils.js"; +import { writeWithBackupSync } from "../utils/writeWithBackup.js"; +import { needsMigration, runMigrations } from "./migration/index.js"; +import { V1_TO_V2_MIGRATION_MAP, V2_CONTAINER_KEYS } from "./migration/versions/v1-to-v2-shared.js"; import { - type Settings, + getSettingsSchema, type MemoryImportFormat, type MergeStrategy, - type SettingsSchema, type SettingDefinition, - getSettingsSchema, -} from './settingsSchema.js'; -import { resolveEnvVarsInObject } from '../utils/envVarResolver.js'; -import { setNestedPropertySafe } from '../utils/settingsUtils.js'; -import { customDeepMerge } from '../utils/deepMerge.js'; -import { updateSettingsFilePreservingFormat } from '../utils/commentJson.js'; -import { runMigrations, needsMigration } from './migration/index.js'; -import { - V1_TO_V2_MIGRATION_MAP, - V2_CONTAINER_KEYS, -} from './migration/versions/v1-to-v2-shared.js'; -import { writeWithBackupSync } from '../utils/writeWithBackup.js'; + type Settings, + type SettingsSchema, +} from "./settingsSchema.js"; +import { isWorkspaceTrusted } from "./trustedFolders.js"; -const debugLogger = createDebugLogger('SETTINGS'); +const debugLogger = createDebugLogger("SETTINGS"); function getMergeStrategyForPath(path: string[]): MergeStrategy | undefined { let current: SettingDefinition | undefined = undefined; @@ -56,16 +53,16 @@ function getMergeStrategyForPath(path: string[]): MergeStrategy | undefined { return current?.mergeStrategy; } -export type { Settings, MemoryImportFormat }; +export type { MemoryImportFormat, Settings }; -export const SETTINGS_DIRECTORY_NAME = '.airiscode'; +export const SETTINGS_DIRECTORY_NAME = ".airiscode"; export const USER_SETTINGS_PATH = Storage.getGlobalSettingsPath(); export const USER_SETTINGS_DIR = path.dirname(USER_SETTINGS_PATH); -export const DEFAULT_EXCLUDED_ENV_VARS = ['DEBUG', 'DEBUG_MODE']; +export const DEFAULT_EXCLUDED_ENV_VARS = ["DEBUG", "DEBUG_MODE"]; // Settings version to track migration state export const SETTINGS_VERSION = 3; -export const SETTINGS_VERSION_KEY = '$version'; +export const SETTINGS_VERSION_KEY = "$version"; /** * Migrate legacy tool permission settings (tools.core / tools.allowed / tools.exclude) @@ -82,39 +79,37 @@ export const SETTINGS_VERSION_KEY = '$version'; export function migrateLegacyPermissions( settings: Record, ): Record | null { - const tools = settings['tools'] as Record | undefined; + const tools = settings["tools"] as Record | undefined; if (!tools) return null; const hasLegacy = - Array.isArray(tools['core']) || - Array.isArray(tools['allowed']) || - Array.isArray(tools['exclude']); + Array.isArray(tools["core"]) || + Array.isArray(tools["allowed"]) || + Array.isArray(tools["exclude"]); if (!hasLegacy) return null; const result = structuredClone(settings) as Record; - const resultTools = result['tools'] as Record; - const permissions = (result['permissions'] as Record) ?? {}; - result['permissions'] = permissions; + const resultTools = result["tools"] as Record; + const permissions = (result["permissions"] as Record) ?? {}; + result["permissions"] = permissions; const mergeInto = (key: string, items: string[]) => { - const existing = Array.isArray(permissions[key]) - ? (permissions[key] as string[]) - : []; + const existing = Array.isArray(permissions[key]) ? (permissions[key] as string[]) : []; const merged = Array.from(new Set([...existing, ...items])); permissions[key] = merged; }; // tools.allowed → permissions.allow - if (Array.isArray(resultTools['allowed'])) { - mergeInto('allow', resultTools['allowed'] as string[]); - delete resultTools['allowed']; + if (Array.isArray(resultTools["allowed"])) { + mergeInto("allow", resultTools["allowed"] as string[]); + delete resultTools["allowed"]; } // tools.exclude → permissions.deny - if (Array.isArray(resultTools['exclude'])) { - mergeInto('deny', resultTools['exclude'] as string[]); - delete resultTools['exclude']; + if (Array.isArray(resultTools["exclude"])) { + mergeInto("deny", resultTools["exclude"] as string[]); + delete resultTools["exclude"]; } // tools.core → permissions.allow (explicit enables) @@ -127,44 +122,41 @@ export function migrateLegacyPermissions( // Instead we just migrate to allow (auto-approve) and let the coreTools // semantics continue to work through the Config.getCoreTools() path until // the old API is fully removed. - if (Array.isArray(resultTools['core'])) { - mergeInto('allow', resultTools['core'] as string[]); - delete resultTools['core']; + if (Array.isArray(resultTools["core"])) { + mergeInto("allow", resultTools["core"] as string[]); + delete resultTools["core"]; } return result; } export function getSystemSettingsPath(): string { - if (process.env['AIRISCODE_SYSTEM_SETTINGS_PATH']) { - return process.env['AIRISCODE_SYSTEM_SETTINGS_PATH']; + if (process.env["AIRISCODE_SYSTEM_SETTINGS_PATH"]) { + return process.env["AIRISCODE_SYSTEM_SETTINGS_PATH"]; } - if (platform() === 'darwin') { - return '/Library/Application Support/AirisCode/settings.json'; - } else if (platform() === 'win32') { - return 'C:\\ProgramData\\airiscode\\settings.json'; + if (platform() === "darwin") { + return "/Library/Application Support/AirisCode/settings.json"; + } else if (platform() === "win32") { + return "C:\\ProgramData\\airiscode\\settings.json"; } else { - return '/etc/airiscode/settings.json'; + return "/etc/airiscode/settings.json"; } } export function getSystemDefaultsPath(): string { - if (process.env['AIRISCODE_SYSTEM_DEFAULTS_PATH']) { - return process.env['AIRISCODE_SYSTEM_DEFAULTS_PATH']; + if (process.env["AIRISCODE_SYSTEM_DEFAULTS_PATH"]) { + return process.env["AIRISCODE_SYSTEM_DEFAULTS_PATH"]; } - return path.join( - path.dirname(getSystemSettingsPath()), - 'system-defaults.json', - ); + return path.join(path.dirname(getSystemSettingsPath()), "system-defaults.json"); } -export type { DnsResolutionOrder } from './settingsSchema.js'; +export type { DnsResolutionOrder } from "./settingsSchema.js"; export enum SettingScope { - User = 'User', - Workspace = 'Workspace', - System = 'System', - SystemDefaults = 'SystemDefaults', + User = "User", + Workspace = "Workspace", + System = "System", + SystemDefaults = "SystemDefaults", } export interface CheckpointingSettings { @@ -193,7 +185,7 @@ function getSettingsFileKeyWarnings( settingsFilePath: string, ): string[] { const version = settings[SETTINGS_VERSION_KEY]; - if (typeof version !== 'number' || version < SETTINGS_VERSION) { + if (typeof version !== "number" || version < SETTINGS_VERSION) { return []; } @@ -215,7 +207,7 @@ function getSettingsFileKeyWarnings( // it's likely already in V2 format. Don't warn. if ( V2_CONTAINER_KEYS.has(oldKey) && - typeof oldValue === 'object' && + typeof oldValue === "object" && oldValue !== null && !Array.isArray(oldValue) ) { @@ -241,9 +233,7 @@ function getSettingsFileKeyWarnings( continue; } - debugLogger.warn( - `Unknown setting '${key}' will be ignored in ${settingsFilePath}.`, - ); + debugLogger.warn(`Unknown setting '${key}' will be ignored in ${settingsFilePath}.`); } return warnings; @@ -270,15 +260,9 @@ export function getSettingsWarnings(loadedSettings: LoadedSettings): string[] { continue; // File not present / not loaded. } - const settingsObject = settingsFile.originalSettings as unknown as Record< - string, - unknown - >; - - for (const warning of getSettingsFileKeyWarnings( - settingsObject, - settingsFile.path, - )) { + const settingsObject = settingsFile.originalSettings as unknown as Record; + + for (const warning of getSettingsFileKeyWarnings(settingsObject, settingsFile.path)) { warningSet.add(warning); } } @@ -385,10 +369,10 @@ export class LoadedSettings { */ export function createMinimalSettings(): LoadedSettings { const emptySettingsFile: SettingsFile = { - path: '', + path: "", settings: {}, originalSettings: {}, - rawJson: '{}', + rawJson: "{}", }; return new LoadedSettings( emptySettingsFile, @@ -414,8 +398,8 @@ function findEnvFile(settings: Settings, startDir: string): string | null { // Pre-compute user-level .env paths for fast comparison const userLevelPaths = new Set([ - path.normalize(path.join(homeDir, '.env')), - path.normalize(path.join(homeDir, AIRISCODE_DIR, '.env')), + path.normalize(path.join(homeDir, ".env")), + path.normalize(path.join(homeDir, AIRISCODE_DIR, ".env")), ]); // Determine if we can use this .env file based on trust settings @@ -425,12 +409,12 @@ function findEnvFile(settings: Settings, startDir: string): string | null { let currentDir = path.resolve(startDir); while (true) { // Prefer gemini-specific .env under AIRISCODE_DIR - const geminiEnvPath = path.join(currentDir, AIRISCODE_DIR, '.env'); + const geminiEnvPath = path.join(currentDir, AIRISCODE_DIR, ".env"); if (fs.existsSync(geminiEnvPath) && canUseEnvFile(geminiEnvPath)) { return geminiEnvPath; } - const envPath = path.join(currentDir, '.env'); + const envPath = path.join(currentDir, ".env"); if (fs.existsSync(envPath) && canUseEnvFile(envPath)) { return envPath; } @@ -438,11 +422,11 @@ function findEnvFile(settings: Settings, startDir: string): string | null { const parentDir = path.dirname(currentDir); if (parentDir === currentDir || !parentDir) { // At home directory - check fallback .env files - const homeGeminiEnvPath = path.join(homeDir, AIRISCODE_DIR, '.env'); + const homeGeminiEnvPath = path.join(homeDir, AIRISCODE_DIR, ".env"); if (fs.existsSync(homeGeminiEnvPath)) { return homeGeminiEnvPath; } - const homeEnvPath = path.join(homeDir, '.env'); + const homeEnvPath = path.join(homeDir, ".env"); if (fs.existsSync(homeEnvPath)) { return homeEnvPath; } @@ -461,16 +445,16 @@ export function setUpCloudShellEnvironment(envFilePath: string | null): void { if (envFilePath && fs.existsSync(envFilePath)) { const envFileContent = fs.readFileSync(envFilePath); const parsedEnv = dotenv.parse(envFileContent); - if (parsedEnv['GOOGLE_CLOUD_PROJECT']) { + if (parsedEnv["GOOGLE_CLOUD_PROJECT"]) { // .env file takes precedence in Cloud Shell - process.env['GOOGLE_CLOUD_PROJECT'] = parsedEnv['GOOGLE_CLOUD_PROJECT']; + process.env["GOOGLE_CLOUD_PROJECT"] = parsedEnv["GOOGLE_CLOUD_PROJECT"]; } else { // If not in .env, set to default and override global - process.env['GOOGLE_CLOUD_PROJECT'] = 'cloudshell-gca'; + process.env["GOOGLE_CLOUD_PROJECT"] = "cloudshell-gca"; } } else { // If no .env file, set to default and override global - process.env['GOOGLE_CLOUD_PROJECT'] = 'cloudshell-gca'; + process.env["GOOGLE_CLOUD_PROJECT"] = "cloudshell-gca"; } } /** @@ -487,7 +471,7 @@ export function loadEnvironment(settings: Settings): void { const envFilePath = findEnvFile(settings, process.cwd()); // Cloud Shell environment variable handling - if (process.env['CLOUD_SHELL'] === 'true') { + if (process.env["CLOUD_SHELL"] === "true") { setUpCloudShellEnvironment(envFilePath); } @@ -495,11 +479,10 @@ export function loadEnvironment(settings: Settings): void { // Only set if not already present in process.env (no-override mode) if (envFilePath) { try { - const envFileContent = fs.readFileSync(envFilePath, 'utf-8'); + const envFileContent = fs.readFileSync(envFilePath, "utf-8"); const parsedEnv = dotenv.parse(envFileContent); - const excludedVars = - settings?.advanced?.excludedEnvVars || DEFAULT_EXCLUDED_ENV_VARS; + const excludedVars = settings?.advanced?.excludedEnvVars || DEFAULT_EXCLUDED_ENV_VARS; const isProjectEnvFile = !envFilePath.includes(AIRISCODE_DIR); for (const key in parsedEnv) { @@ -524,7 +507,7 @@ export function loadEnvironment(settings: Settings): void { // Only set if not already present (no-override, after .env is loaded) if (settings.env) { for (const [key, value] of Object.entries(settings.env)) { - if (!Object.hasOwn(process.env, key) && typeof value === 'string') { + if (!Object.hasOwn(process.env, key) && typeof value === "string") { process.env[key] = value; } } @@ -535,9 +518,7 @@ export function loadEnvironment(settings: Settings): void { * Loads settings from user and workspace directories. * Project settings override user settings. */ -export function loadSettings( - workspaceDir: string = process.cwd(), -): LoadedSettings { +export function loadSettings(workspaceDir: string = process.cwd()): LoadedSettings { let systemSettings: Settings = {}; let systemDefaultSettings: Settings = {}; let userSettings: Settings = {}; @@ -562,9 +543,7 @@ export function loadSettings( // We expect homedir to always exist and be resolvable. const realHomeDir = fs.realpathSync(resolvedHomeDir); - const workspaceSettingsPath = new Storage( - workspaceDir, - ).getWorkspaceSettingsPath(); + const workspaceSettingsPath = new Storage(workspaceDir).getWorkspaceSettingsPath(); const loadAndMigrate = ( filePath: string, @@ -572,16 +551,12 @@ export function loadSettings( ): { settings: Settings; rawJson?: string; migrationWarnings?: string[] } => { try { if (fs.existsSync(filePath)) { - const content = fs.readFileSync(filePath, 'utf-8'); + const content = fs.readFileSync(filePath, "utf-8"); const rawSettings: unknown = JSON.parse(stripJsonComments(content)); - if ( - typeof rawSettings !== 'object' || - rawSettings === null || - Array.isArray(rawSettings) - ) { + if (typeof rawSettings !== "object" || rawSettings === null || Array.isArray(rawSettings)) { settingsErrors.push({ - message: 'Settings file is not a valid JSON object.', + message: "Settings file is not a valid JSON object.", path: filePath, }); return { settings: {} }; @@ -590,18 +565,14 @@ export function loadSettings( let settingsObject = rawSettings as Record; const hasVersionKey = SETTINGS_VERSION_KEY in settingsObject; const versionValue = settingsObject[SETTINGS_VERSION_KEY]; - const hasInvalidVersion = - hasVersionKey && typeof versionValue !== 'number'; + const hasInvalidVersion = hasVersionKey && typeof versionValue !== "number"; const hasLegacyNumericVersion = - typeof versionValue === 'number' && versionValue < SETTINGS_VERSION; + typeof versionValue === "number" && versionValue < SETTINGS_VERSION; let migrationWarnings: string[] | undefined; const persistSettingsObject = (warningPrefix: string) => { try { - writeWithBackupSync( - filePath, - JSON.stringify(settingsObject, null, 2), - ); + writeWithBackupSync(filePath, JSON.stringify(settingsObject, null, 2)); } catch (e) { debugLogger.error(`${warningPrefix}: ${getErrorMessage(e)}`); } @@ -610,12 +581,9 @@ export function loadSettings( if (needsMigration(settingsObject)) { const migrationResult = runMigrations(settingsObject, scope); if (migrationResult.executedMigrations.length > 0) { - settingsObject = migrationResult.settings as Record< - string, - unknown - >; + settingsObject = migrationResult.settings as Record; migrationWarnings = migrationResult.warnings; - persistSettingsObject('Error migrating settings file on disk'); + persistSettingsObject("Error migrating settings file on disk"); } else if (hasLegacyNumericVersion || hasInvalidVersion) { // Migration was deemed needed but nothing executed. Normalize version metadata // to avoid repeated no-op checks on startup. @@ -623,17 +591,13 @@ export function loadSettings( debugLogger.warn( `Settings version metadata in ${filePath} could not be migrated by any registered migration. Normalizing ${SETTINGS_VERSION_KEY} to ${SETTINGS_VERSION}.`, ); - persistSettingsObject('Error normalizing settings version on disk'); + persistSettingsObject("Error normalizing settings version on disk"); } - } else if ( - !hasVersionKey || - hasInvalidVersion || - hasLegacyNumericVersion - ) { + } else if (!hasVersionKey || hasInvalidVersion || hasLegacyNumericVersion) { // No migration needed/executable, but version metadata is missing or invalid. // Normalize it to current version to avoid repeated startup work. settingsObject[SETTINGS_VERSION_KEY] = SETTINGS_VERSION; - persistSettingsObject('Error normalizing settings version on disk'); + persistSettingsObject("Error normalizing settings version on disk"); } return { @@ -652,10 +616,7 @@ export function loadSettings( }; const systemResult = loadAndMigrate(systemSettingsPath, SettingScope.System); - const systemDefaultsResult = loadAndMigrate( - systemDefaultsPath, - SettingScope.SystemDefaults, - ); + const systemDefaultsResult = loadAndMigrate(systemDefaultsPath, SettingScope.SystemDefaults); const userResult = loadAndMigrate(USER_SETTINGS_PATH, SettingScope.User); let workspaceResult: { @@ -667,25 +628,19 @@ export function loadSettings( rawJson: undefined, }; if (realWorkspaceDir !== realHomeDir) { - workspaceResult = loadAndMigrate( - workspaceSettingsPath, - SettingScope.Workspace, - ); + workspaceResult = loadAndMigrate(workspaceSettingsPath, SettingScope.Workspace); } // Load workspace-local settings (.airiscode/settings.local.json) // This file is gitignored and has higher priority than workspace settings let workspaceLocalSettings: Settings = {}; if (realWorkspaceDir !== realHomeDir) { - const localSettingsPath = path.join( - path.dirname(workspaceSettingsPath), - 'settings.local.json', - ); + const localSettingsPath = path.join(path.dirname(workspaceSettingsPath), "settings.local.json"); try { if (fs.existsSync(localSettingsPath)) { - const content = fs.readFileSync(localSettingsPath, 'utf-8'); + const content = fs.readFileSync(localSettingsPath, "utf-8"); const parsed = JSON.parse(stripJsonComments(content)); - if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) { + if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) { workspaceLocalSettings = parsed as Settings; debugLogger.debug(`Loaded local settings from ${localSettingsPath}`); } @@ -696,9 +651,7 @@ export function loadSettings( } const systemOriginalSettings = structuredClone(systemResult.settings); - const systemDefaultsOriginalSettings = structuredClone( - systemDefaultsResult.settings, - ); + const systemDefaultsOriginalSettings = structuredClone(systemDefaultsResult.settings); const userOriginalSettings = structuredClone(userResult.settings); const workspaceOriginalSettings = structuredClone(workspaceResult.settings); @@ -718,14 +671,14 @@ export function loadSettings( } // Support legacy theme names - if (userSettings.ui?.theme === 'VS') { + if (userSettings.ui?.theme === "VS") { userSettings.ui.theme = DefaultLight.name; - } else if (userSettings.ui?.theme === 'VS2015') { + } else if (userSettings.ui?.theme === "VS2015") { userSettings.ui.theme = DefaultDark.name; } - if (workspaceSettings.ui?.theme === 'VS') { + if (workspaceSettings.ui?.theme === "VS") { workspaceSettings.ui.theme = DefaultLight.name; - } else if (workspaceSettings.ui?.theme === 'VS2015') { + } else if (workspaceSettings.ui?.theme === "VS2015") { workspaceSettings.ui.theme = DefaultDark.name; } @@ -736,8 +689,7 @@ export function loadSettings( systemSettings, userSettings, ); - const isTrusted = - isWorkspaceTrusted(initialTrustCheckSettings as Settings).isTrusted ?? true; + const isTrusted = isWorkspaceTrusted(initialTrustCheckSettings as Settings).isTrusted ?? true; // Create a temporary merged settings object to pass to loadEnvironment. const tempMergedSettings = mergeSettings( @@ -755,11 +707,9 @@ export function loadSettings( // Create LoadedSettings first if (settingsErrors.length > 0) { - const errorMessages = settingsErrors.map( - (error) => `Error in ${error.path}: ${error.message}`, - ); + const errorMessages = settingsErrors.map((error) => `Error in ${error.path}: ${error.message}`); throw new FatalConfigError( - `${errorMessages.join('\n')}\nPlease fix the configuration file(s) and try again.`, + `${errorMessages.join("\n")}\nPlease fix the configuration file(s) and try again.`, ); } @@ -802,10 +752,7 @@ export function loadSettings( ); } -function createSettingsUpdate( - key: string, - value: unknown, -): Record { +function createSettingsUpdate(key: string, value: unknown): Record { const root: Record = {}; setNestedPropertySafe(root, key, value); return root; @@ -813,10 +760,7 @@ function createSettingsUpdate( export function saveSettings( settingsFile: SettingsFile, - updates: Record = settingsFile.originalSettings as Record< - string, - unknown - >, + updates: Record = settingsFile.originalSettings as Record, ): void { try { // Ensure the directory exists @@ -828,7 +772,7 @@ export function saveSettings( // Use the format-preserving update function updateSettingsFilePreservingFormat(settingsFile.path, updates); } catch (error) { - debugLogger.error('Error saving user settings file.'); + debugLogger.error("Error saving user settings file."); debugLogger.error(error instanceof Error ? error.message : String(error)); throw error; } diff --git a/apps/airiscode-cli/src/config/settingsSchema.ts b/apps/airiscode-cli/src/config/settingsSchema.ts index c67ad25ea..685401e89 100644 --- a/apps/airiscode-cli/src/config/settingsSchema.ts +++ b/apps/airiscode-cli/src/config/settingsSchema.ts @@ -5,46 +5,31 @@ */ import type { - MCPServerConfig, - BugCommandSettings, - TelemetrySettings, AuthType, + BugCommandSettings, ChatCompressionSettings, + MCPServerConfig, ModelProvidersConfig, -} from '@airiscode/core'; + TelemetrySettings, +} from "@airiscode/core"; import { ApprovalMode, DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES, DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD, -} from '@airiscode/core'; -import type { CustomTheme } from '../ui/themes/theme.js'; -import { getLanguageSettingsOptions } from '../i18n/languages.js'; +} from "@airiscode/core"; +import { getLanguageSettingsOptions } from "../i18n/languages.js"; +import type { CustomTheme } from "../ui/themes/theme.js"; -export type SettingsType = - | 'boolean' - | 'string' - | 'number' - | 'array' - | 'object' - | 'enum'; +export type SettingsType = "boolean" | "string" | "number" | "array" | "object" | "enum"; -export type SettingsValue = - | boolean - | string - | number - | string[] - | object - | undefined; +export type SettingsValue = boolean | string | number | string[] | object | undefined; /** * Setting datatypes that "toggle" through a fixed list of options * (e.g. an enum or true/false) rather than allowing for free form input * (like a number or string). */ -export const TOGGLE_TYPES: ReadonlySet = new Set([ - 'boolean', - 'enum', -]); +export const TOGGLE_TYPES: ReadonlySet = new Set(["boolean", "enum"]); export interface SettingEnumOption { value: string | number; @@ -53,13 +38,13 @@ export interface SettingEnumOption { export enum MergeStrategy { // Replace the old value with the new value. This is the default. - REPLACE = 'replace', + REPLACE = "replace", // Concatenate arrays. - CONCAT = 'concat', + CONCAT = "concat", // Merge arrays, ensuring unique values. - UNION = 'union', + UNION = "union", // Shallow merge objects. - SHALLOW_MERGE = 'shallow_merge', + SHALLOW_MERGE = "shallow_merge", } export interface SettingDefinition { @@ -85,7 +70,7 @@ export interface SettingDefinition { * Supports simple types (string, number, boolean) and complex object types. */ export interface SettingItemDefinition { - type: 'string' | 'number' | 'boolean' | 'object' | 'array'; + type: "string" | "number" | "boolean" | "object" | "array"; properties?: Record< string, SettingItemDefinition & { @@ -110,57 +95,52 @@ export interface SettingsSchema { * Used by all hook event types in the hooks configuration. */ const HOOK_DEFINITION_ITEMS: SettingItemDefinition = { - type: 'object', - description: - 'A hook definition with an optional matcher and a list of hook configurations.', + type: "object", + description: "A hook definition with an optional matcher and a list of hook configurations.", properties: { matcher: { - type: 'string', - description: - 'An optional matcher pattern to filter when this hook definition applies.', + type: "string", + description: "An optional matcher pattern to filter when this hook definition applies.", }, sequential: { - type: 'boolean', - description: - 'Whether the hooks should be executed sequentially instead of in parallel.', + type: "boolean", + description: "Whether the hooks should be executed sequentially instead of in parallel.", }, hooks: { - type: 'array', - description: 'The list of hook configurations to execute.', + type: "array", + description: "The list of hook configurations to execute.", required: true, items: { - type: 'object', - description: - 'A hook configuration entry that defines a command to execute.', + type: "object", + description: "A hook configuration entry that defines a command to execute.", properties: { type: { - type: 'string', - description: 'The type of hook.', - enum: ['command'], + type: "string", + description: "The type of hook.", + enum: ["command"], required: true, }, command: { - type: 'string', - description: 'The command to execute when the hook is triggered.', + type: "string", + description: "The command to execute when the hook is triggered.", required: true, }, name: { - type: 'string', - description: 'An optional name for the hook.', + type: "string", + description: "An optional name for the hook.", }, description: { - type: 'string', - description: 'An optional description of what the hook does.', + type: "string", + description: "An optional description of what the hook does.", }, timeout: { - type: 'number', - description: 'Timeout in milliseconds for the hook execution.', + type: "number", + description: "Timeout in milliseconds for the hook execution.", }, env: { - type: 'object', - description: - 'Environment variables to set when executing the hook command.', - additionalProperties: { type: 'string' }, + type: "object", + description: "Environment variables to set when executing the hook command.", + additionalProperties: { type: "string" }, }, }, }, @@ -168,8 +148,8 @@ const HOOK_DEFINITION_ITEMS: SettingItemDefinition = { }, }; -export type MemoryImportFormat = 'tree' | 'flat'; -export type DnsResolutionOrder = 'ipv4first' | 'verbatim'; +export type MemoryImportFormat = "tree" | "flat"; +export type DnsResolutionOrder = "ipv4first" | "verbatim"; /** * The canonical schema for all settings. @@ -179,59 +159,58 @@ export type DnsResolutionOrder = 'ipv4first' | 'verbatim'; const SETTINGS_SCHEMA = { // Maintained for compatibility/criticality mcpServers: { - type: 'object', - label: 'MCP Servers', - category: 'Advanced', + type: "object", + label: "MCP Servers", + category: "Advanced", requiresRestart: true, default: {} as Record, - description: 'Configuration for MCP servers.', + description: "Configuration for MCP servers.", showInDialog: false, mergeStrategy: MergeStrategy.SHALLOW_MERGE, }, // Channels configuration (Telegram, Discord, etc.) channels: { - type: 'object', - label: 'Channels', - category: 'Advanced', + type: "object", + label: "Channels", + category: "Advanced", requiresRestart: true, default: {} as Record>, - description: 'Configuration for messaging channels.', + description: "Configuration for messaging channels.", showInDialog: false, mergeStrategy: MergeStrategy.SHALLOW_MERGE, }, // Model providers configuration grouped by authType modelProviders: { - type: 'object', - label: 'Model Providers', - category: 'Model', + type: "object", + label: "Model Providers", + category: "Model", requiresRestart: false, default: {} as ModelProvidersConfig, description: - 'Model providers configuration grouped by authType. Each authType contains an array of model configurations.', + "Model providers configuration grouped by authType. Each authType contains an array of model configurations.", showInDialog: false, mergeStrategy: MergeStrategy.REPLACE, }, // Coding Plan configuration codingPlan: { - type: 'object', - label: 'Coding Plan', - category: 'Model', + type: "object", + label: "Coding Plan", + category: "Model", requiresRestart: false, default: {}, - description: 'Coding Plan template version tracking and configuration.', + description: "Coding Plan template version tracking and configuration.", showInDialog: false, properties: { version: { - type: 'string', - label: 'Coding Plan Template Version', - category: 'Model', + type: "string", + label: "Coding Plan Template Version", + category: "Model", requiresRestart: false, default: undefined as string | undefined, - description: - 'SHA256 hash of the Coding Plan template. Used to detect template updates.', + description: "SHA256 hash of the Coding Plan template. Used to detect template updates.", showInDialog: false, }, }, @@ -239,599 +218,594 @@ const SETTINGS_SCHEMA = { // Environment variables fallback env: { - type: 'object', - label: 'Environment Variables', - category: 'Advanced', + type: "object", + label: "Environment Variables", + category: "Advanced", requiresRestart: true, default: {} as Record, description: - 'Environment variables to set as fallback defaults. These are loaded with the lowest priority: system environment variables > .env files > settings.json env field.', + "Environment variables to set as fallback defaults. These are loaded with the lowest priority: system environment variables > .env files > settings.json env field.", showInDialog: false, mergeStrategy: MergeStrategy.SHALLOW_MERGE, }, general: { - type: 'object', - label: 'General', - category: 'General', + type: "object", + label: "General", + category: "General", requiresRestart: false, default: {}, - description: 'General application settings.', + description: "General application settings.", showInDialog: false, properties: { preferredEditor: { - type: 'string', - label: 'Preferred Editor', - category: 'General', + type: "string", + label: "Preferred Editor", + category: "General", requiresRestart: false, default: undefined as string | undefined, - description: 'The preferred editor to open files in.', + description: "The preferred editor to open files in.", showInDialog: true, }, vimMode: { - type: 'boolean', - label: 'Vim Mode', - category: 'General', + type: "boolean", + label: "Vim Mode", + category: "General", requiresRestart: false, default: false, - description: 'Enable Vim keybindings', + description: "Enable Vim keybindings", showInDialog: true, }, enableAutoUpdate: { - type: 'boolean', - label: 'Enable Auto Update', - category: 'General', + type: "boolean", + label: "Enable Auto Update", + category: "General", requiresRestart: false, default: true, - description: - 'Enable automatic update checks and installations on startup.', + description: "Enable automatic update checks and installations on startup.", showInDialog: true, }, gitCoAuthor: { - type: 'boolean', - label: 'Attribution: commit', - category: 'General', + type: "boolean", + label: "Attribution: commit", + category: "General", requiresRestart: false, default: true, description: - 'Automatically add a Co-authored-by trailer to git commit messages when commits are made through AIRIS Code.', + "Automatically add a Co-authored-by trailer to git commit messages when commits are made through AIRIS Code.", showInDialog: true, }, checkpointing: { - type: 'object', - label: 'Checkpointing', - category: 'General', + type: "object", + label: "Checkpointing", + category: "General", requiresRestart: true, default: {}, - description: 'Session checkpointing settings.', + description: "Session checkpointing settings.", showInDialog: false, properties: { enabled: { - type: 'boolean', - label: 'Enable Checkpointing', - category: 'General', + type: "boolean", + label: "Enable Checkpointing", + category: "General", requiresRestart: true, default: false, - description: 'Enable session checkpointing for recovery', + description: "Enable session checkpointing for recovery", showInDialog: false, }, }, }, debugKeystrokeLogging: { - type: 'boolean', - label: 'Debug Keystroke Logging', - category: 'General', + type: "boolean", + label: "Debug Keystroke Logging", + category: "General", requiresRestart: false, default: false, - description: 'Enable debug logging of keystrokes to the console.', + description: "Enable debug logging of keystrokes to the console.", showInDialog: false, }, language: { - type: 'enum', - label: 'Language: UI', - category: 'General', + type: "enum", + label: "Language: UI", + category: "General", requiresRestart: true, - default: 'auto', + default: "auto", description: 'The language for the user interface. Use "auto" to detect from system settings. ' + 'You can also use custom language codes (e.g., "es", "fr") by placing JS language files ' + - 'in ~/.airiscode/locales/ (e.g., ~/.airiscode/locales/es.js).', + "in ~/.airiscode/locales/ (e.g., ~/.airiscode/locales/es.js).", showInDialog: true, options: [] as readonly SettingEnumOption[], }, outputLanguage: { - type: 'string', - label: 'Language: Model', - category: 'General', + type: "string", + label: "Language: Model", + category: "General", requiresRestart: true, - default: 'auto', + default: "auto", description: 'The language for LLM output. Use "auto" to detect from system settings, ' + - 'or set a specific language.', + "or set a specific language.", showInDialog: true, }, terminalBell: { - type: 'boolean', - label: 'Terminal Bell Notification', - category: 'General', + type: "boolean", + label: "Terminal Bell Notification", + category: "General", requiresRestart: false, default: true, - description: - 'Play terminal bell sound when response completes or needs approval.', + description: "Play terminal bell sound when response completes or needs approval.", showInDialog: true, }, chatRecording: { - type: 'boolean', - label: 'Chat Recording', - category: 'General', + type: "boolean", + label: "Chat Recording", + category: "General", requiresRestart: true, default: true, description: - 'Enable saving chat history to disk. Disabling this will also prevent --continue and --resume from working.', + "Enable saving chat history to disk. Disabling this will also prevent --continue and --resume from working.", showInDialog: false, }, defaultFileEncoding: { - type: 'enum', - label: 'Default File Encoding', - category: 'General', + type: "enum", + label: "Default File Encoding", + category: "General", requiresRestart: false, - default: 'utf-8', + default: "utf-8", description: 'Default encoding for new files. Use "utf-8" (default) for UTF-8 without BOM, or "utf-8-bom" for UTF-8 with BOM. Only change this if your project specifically requires BOM.', showInDialog: false, options: [ - { value: 'utf-8', label: 'UTF-8 (without BOM)' }, - { value: 'utf-8-bom', label: 'UTF-8 with BOM' }, + { value: "utf-8", label: "UTF-8 (without BOM)" }, + { value: "utf-8-bom", label: "UTF-8 with BOM" }, ], }, }, }, output: { - type: 'object', - label: 'Output', - category: 'General', + type: "object", + label: "Output", + category: "General", requiresRestart: false, default: {}, - description: 'Settings for the CLI output.', + description: "Settings for the CLI output.", showInDialog: false, properties: { format: { - type: 'enum', - label: 'Output Format', - category: 'General', + type: "enum", + label: "Output Format", + category: "General", requiresRestart: false, - default: 'text', - description: 'The format of the CLI output.', + default: "text", + description: "The format of the CLI output.", showInDialog: false, options: [ - { value: 'text', label: 'Text' }, - { value: 'json', label: 'JSON' }, + { value: "text", label: "Text" }, + { value: "json", label: "JSON" }, ], }, }, }, ui: { - type: 'object', - label: 'UI', - category: 'UI', + type: "object", + label: "UI", + category: "UI", requiresRestart: false, default: {}, - description: 'User interface settings.', + description: "User interface settings.", showInDialog: false, properties: { theme: { - type: 'string', - label: 'Theme', - category: 'UI', + type: "string", + label: "Theme", + category: "UI", requiresRestart: false, - default: 'Qwen Dark' as string, - description: 'The color theme for the UI.', + default: "Qwen Dark" as string, + description: "The color theme for the UI.", showInDialog: true, }, statusLine: { - type: 'object', - label: 'Status Line', - category: 'UI', + type: "object", + label: "Status Line", + category: "UI", requiresRestart: false, - default: undefined as { type: 'command'; command: string } | undefined, - description: 'Custom status line display configuration.', + default: undefined as { type: "command"; command: string } | undefined, + description: "Custom status line display configuration.", showInDialog: false, }, customThemes: { - type: 'object', - label: 'Custom Themes', - category: 'UI', + type: "object", + label: "Custom Themes", + category: "UI", requiresRestart: false, default: {} as Record, - description: 'Custom theme definitions.', + description: "Custom theme definitions.", showInDialog: false, }, hideWindowTitle: { - type: 'boolean', - label: 'Hide Window Title', - category: 'UI', + type: "boolean", + label: "Hide Window Title", + category: "UI", requiresRestart: true, default: false, - description: 'Hide the window title bar', + description: "Hide the window title bar", showInDialog: false, }, showStatusInTitle: { - type: 'boolean', - label: 'Show Status in Title', - category: 'UI', + type: "boolean", + label: "Show Status in Title", + category: "UI", requiresRestart: false, default: false, - description: - 'Show AIRIS Code status and thoughts in the terminal window title', + description: "Show AIRIS Code status and thoughts in the terminal window title", showInDialog: false, }, hideTips: { - type: 'boolean', - label: 'Hide Tips', - category: 'UI', + type: "boolean", + label: "Hide Tips", + category: "UI", requiresRestart: false, default: false, - description: 'Hide helpful tips in the UI', + description: "Hide helpful tips in the UI", showInDialog: true, }, showLineNumbers: { - type: 'boolean', - label: 'Show Line Numbers in Code', - category: 'UI', + type: "boolean", + label: "Show Line Numbers in Code", + category: "UI", requiresRestart: false, default: true, - description: 'Show line numbers in the code output.', + description: "Show line numbers in the code output.", showInDialog: true, }, showCitations: { - type: 'boolean', - label: 'Show Citations', - category: 'UI', + type: "boolean", + label: "Show Citations", + category: "UI", requiresRestart: false, default: false, - description: 'Show citations for generated text in the chat.', + description: "Show citations for generated text in the chat.", showInDialog: false, }, customWittyPhrases: { - type: 'array', - label: 'Custom Witty Phrases', - category: 'UI', + type: "array", + label: "Custom Witty Phrases", + category: "UI", requiresRestart: false, default: [] as string[], - description: 'Custom witty phrases to display during loading.', + description: "Custom witty phrases to display during loading.", showInDialog: false, }, enableWelcomeBack: { - type: 'boolean', - label: 'Show Welcome Back Dialog', - category: 'UI', + type: "boolean", + label: "Show Welcome Back Dialog", + category: "UI", requiresRestart: false, default: true, description: - 'Show welcome back dialog when returning to a project with conversation history.', + "Show welcome back dialog when returning to a project with conversation history.", showInDialog: true, }, enableUserFeedback: { - type: 'boolean', - label: 'Enable User Feedback', - category: 'UI', + type: "boolean", + label: "Enable User Feedback", + category: "UI", requiresRestart: false, default: true, description: - 'Show optional feedback dialog after conversations to help improve Qwen performance.', + "Show optional feedback dialog after conversations to help improve Qwen performance.", showInDialog: true, }, enableFollowupSuggestions: { - type: 'boolean', - label: 'Enable Follow-up Suggestions', - category: 'UI', + type: "boolean", + label: "Enable Follow-up Suggestions", + category: "UI", requiresRestart: false, default: false, description: - 'Show context-aware follow-up suggestions after task completion. Press Tab or Right Arrow to accept, Enter to accept and submit.', + "Show context-aware follow-up suggestions after task completion. Press Tab or Right Arrow to accept, Enter to accept and submit.", showInDialog: true, }, enableCacheSharing: { - type: 'boolean', - label: 'Enable Cache Sharing for Suggestions', - category: 'UI', + type: "boolean", + label: "Enable Cache Sharing for Suggestions", + category: "UI", requiresRestart: false, default: true, description: - 'Use cache-aware forked queries for suggestion generation. Reduces cost on providers that support prefix caching (experimental).', + "Use cache-aware forked queries for suggestion generation. Reduces cost on providers that support prefix caching (experimental).", showInDialog: false, }, enableSpeculation: { - type: 'boolean', - label: 'Enable Speculative Execution', - category: 'UI', + type: "boolean", + label: "Enable Speculative Execution", + category: "UI", requiresRestart: false, default: false, description: - 'Speculatively execute accepted suggestions before submission. Results appear instantly when you accept (experimental).', + "Speculatively execute accepted suggestions before submission. Results appear instantly when you accept (experimental).", showInDialog: false, }, accessibility: { - type: 'object', - label: 'Accessibility', - category: 'UI', + type: "object", + label: "Accessibility", + category: "UI", requiresRestart: true, default: {}, - description: 'Accessibility settings.', + description: "Accessibility settings.", showInDialog: false, properties: { enableLoadingPhrases: { - type: 'boolean', - label: 'Enable Loading Phrases', - category: 'UI', + type: "boolean", + label: "Enable Loading Phrases", + category: "UI", requiresRestart: true, default: true, - description: 'Enable loading phrases (disable for accessibility)', + description: "Enable loading phrases (disable for accessibility)", showInDialog: true, }, screenReader: { - type: 'boolean', - label: 'Screen Reader Mode', - category: 'UI', + type: "boolean", + label: "Screen Reader Mode", + category: "UI", requiresRestart: true, default: undefined as boolean | undefined, - description: - 'Render output in plain-text to be more screen reader accessible', + description: "Render output in plain-text to be more screen reader accessible", showInDialog: false, }, }, }, feedbackLastShownTimestamp: { - type: 'number', - label: 'Feedback Last Shown Timestamp', - category: 'UI', + type: "number", + label: "Feedback Last Shown Timestamp", + category: "UI", requiresRestart: false, default: 0, - description: 'The last time the feedback dialog was shown.', + description: "The last time the feedback dialog was shown.", showInDialog: false, }, verboseMode: { - type: 'boolean', - label: 'Verbose Mode', - category: 'UI', + type: "boolean", + label: "Verbose Mode", + category: "UI", requiresRestart: false, default: true, - description: - 'Show full tool output and thinking in verbose mode (toggle with Ctrl+O).', + description: "Show full tool output and thinking in verbose mode (toggle with Ctrl+O).", showInDialog: false, }, }, }, ide: { - type: 'object', - label: 'IDE', - category: 'IDE', + type: "object", + label: "IDE", + category: "IDE", requiresRestart: true, default: {}, - description: 'IDE integration settings.', + description: "IDE integration settings.", showInDialog: false, properties: { enabled: { - type: 'boolean', - label: 'Auto-connect to IDE', - category: 'IDE', + type: "boolean", + label: "Auto-connect to IDE", + category: "IDE", requiresRestart: true, default: false, - description: 'Enable IDE integration mode', + description: "Enable IDE integration mode", showInDialog: true, }, hasSeenNudge: { - type: 'boolean', - label: 'Has Seen IDE Integration Nudge', - category: 'IDE', + type: "boolean", + label: "Has Seen IDE Integration Nudge", + category: "IDE", requiresRestart: false, default: false, - description: 'Whether the user has seen the IDE integration nudge.', + description: "Whether the user has seen the IDE integration nudge.", showInDialog: false, }, }, }, privacy: { - type: 'object', - label: 'Privacy', - category: 'Privacy', + type: "object", + label: "Privacy", + category: "Privacy", requiresRestart: true, default: {}, - description: 'Privacy-related settings.', + description: "Privacy-related settings.", showInDialog: false, properties: { usageStatisticsEnabled: { - type: 'boolean', - label: 'Enable Usage Statistics', - category: 'Privacy', + type: "boolean", + label: "Enable Usage Statistics", + category: "Privacy", requiresRestart: true, default: true, - description: 'Enable collection of usage statistics', + description: "Enable collection of usage statistics", showInDialog: true, }, }, }, telemetry: { - type: 'object', - label: 'Telemetry', - category: 'Advanced', + type: "object", + label: "Telemetry", + category: "Advanced", requiresRestart: true, default: undefined as TelemetrySettings | undefined, - description: 'Telemetry configuration.', + description: "Telemetry configuration.", showInDialog: false, }, fastModel: { - type: 'string', - label: 'Fast Model', - category: 'Model', + type: "string", + label: "Fast Model", + category: "Model", requiresRestart: false, - default: '', + default: "", description: - 'Model for background tasks (suggestion generation, speculation). Leave empty to use the main model. A smaller/faster model (e.g., qwen3.5-flash) reduces latency and cost.', + "Model for background tasks (suggestion generation, speculation). Leave empty to use the main model. A smaller/faster model (e.g., qwen3.5-flash) reduces latency and cost.", showInDialog: true, }, model: { - type: 'object', - label: 'Model', - category: 'Model', + type: "object", + label: "Model", + category: "Model", requiresRestart: false, default: {}, - description: 'Settings related to the generative model.', + description: "Settings related to the generative model.", showInDialog: false, properties: { name: { - type: 'string', - label: 'Model', - category: 'Model', + type: "string", + label: "Model", + category: "Model", requiresRestart: false, default: undefined as string | undefined, - description: 'The model to use for conversations.', + description: "The model to use for conversations.", showInDialog: false, }, maxSessionTurns: { - type: 'number', - label: 'Max Session Turns', - category: 'Model', + type: "number", + label: "Max Session Turns", + category: "Model", requiresRestart: false, default: -1, description: - 'Maximum number of user/model/tool turns to keep in a session. -1 means unlimited.', + "Maximum number of user/model/tool turns to keep in a session. -1 means unlimited.", showInDialog: false, }, chatCompression: { - type: 'object', - label: 'Chat Compression', - category: 'Model', + type: "object", + label: "Chat Compression", + category: "Model", requiresRestart: false, default: undefined as ChatCompressionSettings | undefined, - description: 'Chat compression settings.', + description: "Chat compression settings.", showInDialog: false, }, sessionTokenLimit: { - type: 'number', - label: 'Session Token Limit', - category: 'Model', + type: "number", + label: "Session Token Limit", + category: "Model", requiresRestart: false, default: undefined as number | undefined, - description: 'The maximum number of tokens allowed in a session.', + description: "The maximum number of tokens allowed in a session.", showInDialog: false, }, skipNextSpeakerCheck: { - type: 'boolean', - label: 'Skip Next Speaker Check', - category: 'Model', + type: "boolean", + label: "Skip Next Speaker Check", + category: "Model", requiresRestart: false, default: true, - description: 'Skip the next speaker check.', + description: "Skip the next speaker check.", showInDialog: false, }, skipLoopDetection: { - type: 'boolean', - label: 'Skip Loop Detection', - category: 'Model', + type: "boolean", + label: "Skip Loop Detection", + category: "Model", requiresRestart: false, default: true, - description: 'Disable all loop detection checks (streaming and LLM).', + description: "Disable all loop detection checks (streaming and LLM).", showInDialog: false, }, skipStartupContext: { - type: 'boolean', - label: 'Skip Startup Context', - category: 'Model', + type: "boolean", + label: "Skip Startup Context", + category: "Model", requiresRestart: true, default: false, description: - 'Avoid sending the workspace startup context at the beginning of each session.', + "Avoid sending the workspace startup context at the beginning of each session.", showInDialog: false, }, enableOpenAILogging: { - type: 'boolean', - label: 'Enable OpenAI Logging', - category: 'Model', + type: "boolean", + label: "Enable OpenAI Logging", + category: "Model", requiresRestart: false, default: false, - description: 'Enable OpenAI logging.', + description: "Enable OpenAI logging.", showInDialog: false, }, openAILoggingDir: { - type: 'string', - label: 'OpenAI Logging Directory', - category: 'Model', + type: "string", + label: "OpenAI Logging Directory", + category: "Model", requiresRestart: false, default: undefined as string | undefined, description: - 'Custom directory path for OpenAI API logs. If not specified, defaults to logs/openai in the current working directory.', + "Custom directory path for OpenAI API logs. If not specified, defaults to logs/openai in the current working directory.", showInDialog: false, }, generationConfig: { - type: 'object', - label: 'Generation Configuration', - category: 'Model', + type: "object", + label: "Generation Configuration", + category: "Model", requiresRestart: false, default: undefined as Record | undefined, - description: 'Generation configuration settings.', + description: "Generation configuration settings.", showInDialog: false, properties: { timeout: { - type: 'number', - label: 'Timeout', - category: 'Generation Configuration', + type: "number", + label: "Timeout", + category: "Generation Configuration", requiresRestart: false, default: undefined as number | undefined, - description: 'Request timeout in milliseconds.', - parentKey: 'generationConfig', + description: "Request timeout in milliseconds.", + parentKey: "generationConfig", showInDialog: false, }, maxRetries: { - type: 'number', - label: 'Max Retries', - category: 'Generation Configuration', + type: "number", + label: "Max Retries", + category: "Generation Configuration", requiresRestart: false, default: undefined as number | undefined, - description: 'Maximum number of retries for failed requests.', - parentKey: 'generationConfig', + description: "Maximum number of retries for failed requests.", + parentKey: "generationConfig", showInDialog: false, }, enableCacheControl: { - type: 'boolean', - label: 'Enable Cache Control', - category: 'Generation Configuration', + type: "boolean", + label: "Enable Cache Control", + category: "Generation Configuration", requiresRestart: false, default: true, - description: 'Enable cache control for DashScope providers.', - parentKey: 'generationConfig', + description: "Enable cache control for DashScope providers.", + parentKey: "generationConfig", showInDialog: false, }, schemaCompliance: { - type: 'enum', - label: 'Tool Schema Compliance', - category: 'Generation Configuration', + type: "enum", + label: "Tool Schema Compliance", + category: "Generation Configuration", requiresRestart: false, - default: 'auto', + default: "auto", description: 'The compliance mode for tool schemas sent to the model. Use "openapi_30" for strict OpenAPI 3.0 compatibility (e.g., for Gemini).', - parentKey: 'generationConfig', + parentKey: "generationConfig", showInDialog: false, options: [ - { value: 'auto', label: 'Auto (Default)' }, - { value: 'openapi_30', label: 'OpenAPI 3.0 Strict' }, + { value: "auto", label: "Auto (Default)" }, + { value: "openapi_30", label: "OpenAPI 3.0 Strict" }, ], }, contextWindowSize: { - type: 'number', - label: 'Context Window Size', - category: 'Generation Configuration', + type: "number", + label: "Context Window Size", + category: "Generation Configuration", requiresRestart: false, default: undefined, description: "Overrides the default context window size for the selected model. Use this setting when a provider's effective context limit differs from AIRIS Code's default. This value defines the model's assumed maximum context capacity, not a per-request token limit.", - parentKey: 'generationConfig', + parentKey: "generationConfig", showInDialog: false, }, }, @@ -840,154 +814,154 @@ const SETTINGS_SCHEMA = { }, context: { - type: 'object', - label: 'Context', - category: 'Context', + type: "object", + label: "Context", + category: "Context", requiresRestart: false, default: {}, - description: 'Settings for managing context provided to the model.', + description: "Settings for managing context provided to the model.", showInDialog: false, properties: { fileName: { - type: 'object', - label: 'Context File Name', - category: 'Context', + type: "object", + label: "Context File Name", + category: "Context", requiresRestart: false, default: undefined as string | string[] | undefined, - description: 'The name of the context file.', + description: "The name of the context file.", showInDialog: false, }, importFormat: { - type: 'string', - label: 'Memory Import Format', - category: 'Context', + type: "string", + label: "Memory Import Format", + category: "Context", requiresRestart: false, default: undefined as MemoryImportFormat | undefined, - description: 'The format to use when importing memory.', + description: "The format to use when importing memory.", showInDialog: false, }, includeDirectories: { - type: 'array', - label: 'Include Directories', - category: 'Context', + type: "array", + label: "Include Directories", + category: "Context", requiresRestart: false, default: [] as string[], description: - 'Additional directories to include in the workspace context. Missing directories will be skipped with a warning.', + "Additional directories to include in the workspace context. Missing directories will be skipped with a warning.", showInDialog: false, mergeStrategy: MergeStrategy.CONCAT, }, loadFromIncludeDirectories: { - type: 'boolean', - label: 'Load Memory From Include Directories', - category: 'Context', + type: "boolean", + label: "Load Memory From Include Directories", + category: "Context", requiresRestart: false, default: false, - description: 'Whether to load memory files from include directories.', + description: "Whether to load memory files from include directories.", showInDialog: false, }, fileFiltering: { - type: 'object', - label: 'File Filtering', - category: 'Context', + type: "object", + label: "File Filtering", + category: "Context", requiresRestart: true, default: {}, - description: 'Settings for git-aware file filtering.', + description: "Settings for git-aware file filtering.", showInDialog: false, properties: { respectGitIgnore: { - type: 'boolean', - label: 'Respect .gitignore', - category: 'Context', + type: "boolean", + label: "Respect .gitignore", + category: "Context", requiresRestart: true, default: true, - description: 'Respect .gitignore files when searching', + description: "Respect .gitignore files when searching", showInDialog: true, }, respectAiriscodeIgnore: { - type: 'boolean', - label: 'Respect .airiscodeigenore', - category: 'Context', + type: "boolean", + label: "Respect .airiscodeigenore", + category: "Context", requiresRestart: true, default: true, - description: 'Respect .airiscodeigenore files when searching', + description: "Respect .airiscodeigenore files when searching", showInDialog: true, }, enableRecursiveFileSearch: { - type: 'boolean', - label: 'Enable Recursive File Search', - category: 'Context', + type: "boolean", + label: "Enable Recursive File Search", + category: "Context", requiresRestart: true, default: true, - description: 'Enable recursive file search functionality', + description: "Enable recursive file search functionality", showInDialog: false, }, enableFuzzySearch: { - type: 'boolean', - label: 'Enable Fuzzy Search', - category: 'Context', + type: "boolean", + label: "Enable Fuzzy Search", + category: "Context", requiresRestart: true, default: true, - description: 'Enable fuzzy search when searching for files.', + description: "Enable fuzzy search when searching for files.", showInDialog: true, }, }, }, gapThresholdMinutes: { - type: 'number', - label: 'Thinking Block Idle Threshold (minutes)', - category: 'Context', + type: "number", + label: "Thinking Block Idle Threshold (minutes)", + category: "Context", requiresRestart: false, default: 5, description: - 'Minutes of inactivity after which retained thinking blocks are cleared to free context tokens. Aligns with provider prompt-cache TTL.', + "Minutes of inactivity after which retained thinking blocks are cleared to free context tokens. Aligns with provider prompt-cache TTL.", showInDialog: false, }, }, }, permissions: { - type: 'object', - label: 'Permissions', - category: 'Tools', + type: "object", + label: "Permissions", + category: "Tools", requiresRestart: true, default: {}, description: - 'Permission rules controlling tool usage. Rules are evaluated in priority order: deny > ask > allow.', + "Permission rules controlling tool usage. Rules are evaluated in priority order: deny > ask > allow.", showInDialog: false, properties: { allow: { - type: 'array', - label: 'Allow Rules', - category: 'Tools', + type: "array", + label: "Allow Rules", + category: "Tools", requiresRestart: true, default: undefined as string[] | undefined, description: - 'Tools or commands that are auto-approved without confirmation. ' + + "Tools or commands that are auto-approved without confirmation. " + 'Examples: "ShellTool", "Bash(git *)", "ReadFileTool".', showInDialog: false, mergeStrategy: MergeStrategy.UNION, }, ask: { - type: 'array', - label: 'Ask Rules', - category: 'Tools', + type: "array", + label: "Ask Rules", + category: "Tools", requiresRestart: true, default: undefined as string[] | undefined, description: - 'Tools or commands that always require user confirmation. ' + - 'Takes precedence over allow rules.', + "Tools or commands that always require user confirmation. " + + "Takes precedence over allow rules.", showInDialog: false, mergeStrategy: MergeStrategy.UNION, }, deny: { - type: 'array', - label: 'Deny Rules', - category: 'Tools', + type: "array", + label: "Deny Rules", + category: "Tools", requiresRestart: true, default: undefined as string[] | undefined, description: - 'Tools or commands that are always blocked. Highest priority rule. ' + + "Tools or commands that are always blocked. Highest priority rule. " + 'Examples: "ShellTool", "Bash(rm -rf *)".', showInDialog: false, mergeStrategy: MergeStrategy.UNION, @@ -996,60 +970,58 @@ const SETTINGS_SCHEMA = { }, tools: { - type: 'object', - label: 'Tools', - category: 'Tools', + type: "object", + label: "Tools", + category: "Tools", requiresRestart: true, default: {}, - description: 'Settings for built-in and custom tools.', + description: "Settings for built-in and custom tools.", showInDialog: false, properties: { sandbox: { - type: 'object', - label: 'Sandbox', - category: 'Tools', + type: "object", + label: "Sandbox", + category: "Tools", requiresRestart: true, default: undefined as boolean | string | undefined, - description: - 'Sandbox execution environment (can be a boolean or a path string).', + description: "Sandbox execution environment (can be a boolean or a path string).", showInDialog: false, }, shell: { - type: 'object', - label: 'Shell', - category: 'Tools', + type: "object", + label: "Shell", + category: "Tools", requiresRestart: false, default: {}, - description: 'Settings for shell execution.', + description: "Settings for shell execution.", showInDialog: false, properties: { enableInteractiveShell: { - type: 'boolean', - label: 'Interactive Shell (PTY)', - category: 'Tools', + type: "boolean", + label: "Interactive Shell (PTY)", + category: "Tools", requiresRestart: true, default: true, description: - 'Use node-pty for an interactive shell experience. Falls back to child_process if PTY is unavailable.', + "Use node-pty for an interactive shell experience. Falls back to child_process if PTY is unavailable.", showInDialog: true, }, pager: { - type: 'string', - label: 'Pager', - category: 'Tools', + type: "string", + label: "Pager", + category: "Tools", requiresRestart: false, - default: 'cat' as string | undefined, - description: - 'The pager command to use for shell output. Defaults to `cat`.', + default: "cat" as string | undefined, + description: "The pager command to use for shell output. Defaults to `cat`.", showInDialog: false, }, showColor: { - type: 'boolean', - label: 'Show Color', - category: 'Tools', + type: "boolean", + label: "Show Color", + category: "Tools", requiresRestart: false, default: false, - description: 'Show color in shell output.', + description: "Show color in shell output.", showInDialog: false, }, }, @@ -1057,91 +1029,91 @@ const SETTINGS_SCHEMA = { // Legacy tool permission fields – kept for backward compatibility. // Use permissions.{allow,ask,deny} instead. core: { - type: 'array', - label: 'Core Tools (deprecated)', - category: 'Tools', + type: "array", + label: "Core Tools (deprecated)", + category: "Tools", requiresRestart: true, default: undefined as string[] | undefined, - description: 'Deprecated. Use permissions.allow instead.', + description: "Deprecated. Use permissions.allow instead.", showInDialog: false, }, allowed: { - type: 'array', - label: 'Allowed Tools (deprecated)', - category: 'Advanced', + type: "array", + label: "Allowed Tools (deprecated)", + category: "Advanced", requiresRestart: true, default: undefined as string[] | undefined, - description: 'Deprecated. Use permissions.allow instead.', + description: "Deprecated. Use permissions.allow instead.", showInDialog: false, }, exclude: { - type: 'array', - label: 'Exclude Tools (deprecated)', - category: 'Tools', + type: "array", + label: "Exclude Tools (deprecated)", + category: "Tools", requiresRestart: true, default: undefined as string[] | undefined, - description: 'Deprecated. Use permissions.deny instead.', + description: "Deprecated. Use permissions.deny instead.", showInDialog: false, mergeStrategy: MergeStrategy.UNION, }, approvalMode: { - type: 'enum', - label: 'Tool Approval Mode', - category: 'Tools', + type: "enum", + label: "Tool Approval Mode", + category: "Tools", requiresRestart: false, default: ApprovalMode.DEFAULT, description: - 'Approval mode for tool usage. Controls how tools are approved before execution.', + "Approval mode for tool usage. Controls how tools are approved before execution.", showInDialog: true, options: [ - { value: ApprovalMode.PLAN, label: 'Plan' }, - { value: ApprovalMode.DEFAULT, label: 'Default' }, - { value: ApprovalMode.AUTO_EDIT, label: 'Auto Edit' }, - { value: ApprovalMode.YOLO, label: 'YOLO' }, + { value: ApprovalMode.PLAN, label: "Plan" }, + { value: ApprovalMode.DEFAULT, label: "Default" }, + { value: ApprovalMode.AUTO_EDIT, label: "Auto Edit" }, + { value: ApprovalMode.YOLO, label: "YOLO" }, ], }, autoAccept: { - type: 'boolean', - label: 'Auto Accept', - category: 'Tools', + type: "boolean", + label: "Auto Accept", + category: "Tools", requiresRestart: false, default: false, description: - 'Automatically accept and execute tool calls that are considered safe (e.g., read-only operations) without explicit user confirmation.', + "Automatically accept and execute tool calls that are considered safe (e.g., read-only operations) without explicit user confirmation.", showInDialog: false, }, discoveryCommand: { - type: 'string', - label: 'Tool Discovery Command', - category: 'Tools', + type: "string", + label: "Tool Discovery Command", + category: "Tools", requiresRestart: true, default: undefined as string | undefined, - description: 'Command to run for tool discovery.', + description: "Command to run for tool discovery.", showInDialog: false, }, callCommand: { - type: 'string', - label: 'Tool Call Command', - category: 'Tools', + type: "string", + label: "Tool Call Command", + category: "Tools", requiresRestart: true, default: undefined as string | undefined, - description: 'Command to run for tool calls.', + description: "Command to run for tool calls.", showInDialog: false, }, useRipgrep: { - type: 'boolean', - label: 'Use Ripgrep', - category: 'Tools', + type: "boolean", + label: "Use Ripgrep", + category: "Tools", requiresRestart: false, default: true, description: - 'Use ripgrep for file content search instead of the fallback implementation. Provides faster search performance.', + "Use ripgrep for file content search instead of the fallback implementation. Provides faster search performance.", showInDialog: false, }, useBuiltinRipgrep: { - type: 'boolean', - label: 'Use Builtin Ripgrep', - category: 'Tools', + type: "boolean", + label: "Use Builtin Ripgrep", + category: "Tools", requiresRestart: false, default: true, description: @@ -1149,149 +1121,149 @@ const SETTINGS_SCHEMA = { showInDialog: false, }, truncateToolOutputThreshold: { - type: 'number', - label: 'Tool Output Truncation Threshold', - category: 'General', + type: "number", + label: "Tool Output Truncation Threshold", + category: "General", requiresRestart: true, default: DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD, description: - 'Truncate tool output if it is larger than this many characters. Set to -1 to disable.', + "Truncate tool output if it is larger than this many characters. Set to -1 to disable.", showInDialog: false, }, truncateToolOutputLines: { - type: 'number', - label: 'Tool Output Truncation Lines', - category: 'General', + type: "number", + label: "Tool Output Truncation Lines", + category: "General", requiresRestart: true, default: DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES, - description: 'The number of lines to keep when truncating tool output.', + description: "The number of lines to keep when truncating tool output.", showInDialog: false, }, }, }, mcp: { - type: 'object', - label: 'MCP', - category: 'MCP', + type: "object", + label: "MCP", + category: "MCP", requiresRestart: true, default: {}, - description: 'Settings for Model Context Protocol (MCP) servers.', + description: "Settings for Model Context Protocol (MCP) servers.", showInDialog: false, properties: { serverCommand: { - type: 'string', - label: 'MCP Server Command', - category: 'MCP', + type: "string", + label: "MCP Server Command", + category: "MCP", requiresRestart: true, default: undefined as string | undefined, - description: 'Command to start an MCP server.', + description: "Command to start an MCP server.", showInDialog: false, }, allowed: { - type: 'array', - label: 'Allow MCP Servers', - category: 'MCP', + type: "array", + label: "Allow MCP Servers", + category: "MCP", requiresRestart: true, default: undefined as string[] | undefined, - description: 'A list of MCP servers to allow.', + description: "A list of MCP servers to allow.", showInDialog: false, mergeStrategy: MergeStrategy.CONCAT, }, excluded: { - type: 'array', - label: 'Exclude MCP Servers', - category: 'MCP', + type: "array", + label: "Exclude MCP Servers", + category: "MCP", requiresRestart: true, default: undefined as string[] | undefined, - description: 'A list of MCP servers to exclude.', + description: "A list of MCP servers to exclude.", showInDialog: false, mergeStrategy: MergeStrategy.CONCAT, }, }, }, security: { - type: 'object', - label: 'Security', - category: 'Security', + type: "object", + label: "Security", + category: "Security", requiresRestart: true, default: {}, - description: 'Security-related settings.', + description: "Security-related settings.", showInDialog: false, properties: { folderTrust: { - type: 'object', - label: 'Folder Trust', - category: 'Security', + type: "object", + label: "Folder Trust", + category: "Security", requiresRestart: false, default: {}, - description: 'Settings for folder trust.', + description: "Settings for folder trust.", showInDialog: false, properties: { enabled: { - type: 'boolean', - label: 'Folder Trust', - category: 'Security', + type: "boolean", + label: "Folder Trust", + category: "Security", requiresRestart: true, default: false, - description: 'Setting to track whether Folder trust is enabled.', + description: "Setting to track whether Folder trust is enabled.", showInDialog: false, }, }, }, auth: { - type: 'object', - label: 'Authentication', - category: 'Security', + type: "object", + label: "Authentication", + category: "Security", requiresRestart: true, default: {}, - description: 'Authentication settings.', + description: "Authentication settings.", showInDialog: false, properties: { selectedType: { - type: 'string', - label: 'Selected Auth Type', - category: 'Security', + type: "string", + label: "Selected Auth Type", + category: "Security", requiresRestart: true, default: undefined as AuthType | undefined, - description: 'The currently selected authentication type.', + description: "The currently selected authentication type.", showInDialog: false, }, enforcedType: { - type: 'string', - label: 'Enforced Auth Type', - category: 'Advanced', + type: "string", + label: "Enforced Auth Type", + category: "Advanced", requiresRestart: true, default: undefined as AuthType | undefined, description: - 'The required auth type. If this does not match the selected auth type, the user will be prompted to re-authenticate.', + "The required auth type. If this does not match the selected auth type, the user will be prompted to re-authenticate.", showInDialog: false, }, useExternal: { - type: 'boolean', - label: 'Use External Auth', - category: 'Security', + type: "boolean", + label: "Use External Auth", + category: "Security", requiresRestart: true, default: undefined as boolean | undefined, - description: 'Whether to use an external authentication flow.', + description: "Whether to use an external authentication flow.", showInDialog: false, }, apiKey: { - type: 'string', - label: 'API Key', - category: 'Security', + type: "string", + label: "API Key", + category: "Security", requiresRestart: true, default: undefined as string | undefined, - description: 'API key for OpenAI compatible authentication.', + description: "API key for OpenAI compatible authentication.", showInDialog: false, }, baseUrl: { - type: 'string', - label: 'Base URL', - category: 'Security', + type: "string", + label: "Base URL", + category: "Security", requiresRestart: true, default: undefined as string | undefined, - description: 'Base URL for OpenAI compatible API.', + description: "Base URL for OpenAI compatible API.", showInDialog: false, }, }, @@ -1300,346 +1272,342 @@ const SETTINGS_SCHEMA = { }, advanced: { - type: 'object', - label: 'Advanced', - category: 'Advanced', + type: "object", + label: "Advanced", + category: "Advanced", requiresRestart: true, default: {}, - description: 'Advanced settings for power users.', + description: "Advanced settings for power users.", showInDialog: false, properties: { autoConfigureMemory: { - type: 'boolean', - label: 'Auto Configure Max Old Space Size', - category: 'Advanced', + type: "boolean", + label: "Auto Configure Max Old Space Size", + category: "Advanced", requiresRestart: true, default: false, - description: 'Automatically configure Node.js memory limits', + description: "Automatically configure Node.js memory limits", showInDialog: false, }, dnsResolutionOrder: { - type: 'string', - label: 'DNS Resolution Order', - category: 'Advanced', + type: "string", + label: "DNS Resolution Order", + category: "Advanced", requiresRestart: true, default: undefined as DnsResolutionOrder | undefined, - description: 'The DNS resolution order.', + description: "The DNS resolution order.", showInDialog: false, }, excludedEnvVars: { - type: 'array', - label: 'Excluded Project Environment Variables', - category: 'Advanced', + type: "array", + label: "Excluded Project Environment Variables", + category: "Advanced", requiresRestart: false, - default: ['DEBUG', 'DEBUG_MODE'] as string[], - description: 'Environment variables to exclude from project context.', + default: ["DEBUG", "DEBUG_MODE"] as string[], + description: "Environment variables to exclude from project context.", showInDialog: false, mergeStrategy: MergeStrategy.UNION, }, bugCommand: { - type: 'object', - label: 'Bug Command', - category: 'Advanced', + type: "object", + label: "Bug Command", + category: "Advanced", requiresRestart: false, default: undefined as BugCommandSettings | undefined, - description: 'Configuration for the bug report command.', + description: "Configuration for the bug report command.", showInDialog: false, }, runtimeOutputDir: { - type: 'string', - label: 'Runtime Output Directory', - category: 'Advanced', + type: "string", + label: "Runtime Output Directory", + category: "Advanced", requiresRestart: true, default: undefined as string | undefined, description: - 'Custom directory for runtime output (temp files, debug logs, session data, todos, etc.). ' + - 'Config files remain at ~/.qwen. Env var QWEN_RUNTIME_DIR takes priority.', + "Custom directory for runtime output (temp files, debug logs, session data, todos, etc.). " + + "Config files remain at ~/.qwen. Env var QWEN_RUNTIME_DIR takes priority.", showInDialog: false, }, tavilyApiKey: { - type: 'string', - label: 'Tavily API Key (Deprecated)', - category: 'Advanced', + type: "string", + label: "Tavily API Key (Deprecated)", + category: "Advanced", requiresRestart: false, default: undefined as string | undefined, description: - '⚠️ DEPRECATED: Please use webSearch.provider configuration instead. Legacy API key for the Tavily API.', + "⚠️ DEPRECATED: Please use webSearch.provider configuration instead. Legacy API key for the Tavily API.", showInDialog: false, }, }, }, webSearch: { - type: 'object', - label: 'Web Search', - category: 'Advanced', + type: "object", + label: "Web Search", + category: "Advanced", requiresRestart: true, default: undefined as | { provider: Array<{ - type: 'tavily' | 'google' | 'dashscope'; + type: "tavily" | "google" | "dashscope"; apiKey?: string; searchEngineId?: string; }>; default: string; } | undefined, - description: 'Configuration for web search providers.', + description: "Configuration for web search providers.", showInDialog: false, }, agents: { - type: 'object', - label: 'Agents', - category: 'Advanced', + type: "object", + label: "Agents", + category: "Advanced", requiresRestart: false, default: {}, - description: - 'Settings for multi-agent collaboration features (Arena, Team, Swarm).', + description: "Settings for multi-agent collaboration features (Arena, Team, Swarm).", showInDialog: false, properties: { displayMode: { - type: 'enum', - label: 'Display Mode', - category: 'Advanced', + type: "enum", + label: "Display Mode", + category: "Advanced", requiresRestart: false, default: undefined as string | undefined, description: 'Display mode for multi-agent sessions. Currently only "in-process" is supported.', showInDialog: false, options: [ - { value: 'in-process', label: 'In-process' }, + { value: "in-process", label: "In-process" }, // { value: 'tmux', label: 'tmux' }, // { value: 'iterm2', label: 'iTerm2' }, ], }, arena: { - type: 'object', - label: 'Arena', - category: 'Advanced', + type: "object", + label: "Arena", + category: "Advanced", requiresRestart: false, default: {}, - description: 'Settings for Arena (multi-model competitive execution).', + description: "Settings for Arena (multi-model competitive execution).", showInDialog: false, properties: { worktreeBaseDir: { - type: 'string', - label: 'Worktree Base Directory', - category: 'Advanced', + type: "string", + label: "Worktree Base Directory", + category: "Advanced", requiresRestart: true, default: undefined as string | undefined, description: - 'Custom base directory for Arena worktrees. Defaults to ~/.airiscode/arena.', + "Custom base directory for Arena worktrees. Defaults to ~/.airiscode/arena.", showInDialog: false, }, preserveArtifacts: { - type: 'boolean', - label: 'Preserve Arena Artifacts', - category: 'Advanced', + type: "boolean", + label: "Preserve Arena Artifacts", + category: "Advanced", requiresRestart: false, default: false, description: - 'When enabled, Arena worktrees and session state files are preserved after the session ends or the main agent exits.', + "When enabled, Arena worktrees and session state files are preserved after the session ends or the main agent exits.", showInDialog: true, }, maxRoundsPerAgent: { - type: 'number', - label: 'Max Rounds Per Agent', - category: 'Advanced', + type: "number", + label: "Max Rounds Per Agent", + category: "Advanced", requiresRestart: false, default: undefined as number | undefined, description: - 'Maximum number of rounds (turns) each agent can execute. No limit if unset.', + "Maximum number of rounds (turns) each agent can execute. No limit if unset.", showInDialog: false, }, timeoutSeconds: { - type: 'number', - label: 'Timeout (seconds)', - category: 'Advanced', + type: "number", + label: "Timeout (seconds)", + category: "Advanced", requiresRestart: false, default: undefined as number | undefined, - description: - 'Total timeout in seconds for the Arena session. No limit if unset.', + description: "Total timeout in seconds for the Arena session. No limit if unset.", showInDialog: false, }, }, }, team: { - type: 'object', - label: 'Team', - category: 'Advanced', + type: "object", + label: "Team", + category: "Advanced", requiresRestart: false, default: {}, description: - 'Settings for Agent Team (role-based collaborative execution). Reserved for future use.', + "Settings for Agent Team (role-based collaborative execution). Reserved for future use.", showInDialog: false, }, swarm: { - type: 'object', - label: 'Swarm', - category: 'Advanced', + type: "object", + label: "Swarm", + category: "Advanced", requiresRestart: false, default: {}, description: - 'Settings for Agent Swarm (parallel sub-agent execution). Reserved for future use.', + "Settings for Agent Swarm (parallel sub-agent execution). Reserved for future use.", showInDialog: false, }, }, }, disableAllHooks: { - type: 'boolean', - label: 'Disable All Hooks', - category: 'Advanced', + type: "boolean", + label: "Disable All Hooks", + category: "Advanced", requiresRestart: true, // Future enhancement: consider supporting mid-session toggle for better UX default: false, description: - 'Temporarily disable all hooks without deleting configurations. Default is false (hooks enabled).', + "Temporarily disable all hooks without deleting configurations. Default is false (hooks enabled).", showInDialog: false, }, hooks: { - type: 'object', - label: 'Hooks', - category: 'Advanced', + type: "object", + label: "Hooks", + category: "Advanced", requiresRestart: false, default: {}, description: - 'Hook event configurations for extending CLI behavior at various lifecycle points.', + "Hook event configurations for extending CLI behavior at various lifecycle points.", showInDialog: false, properties: { UserPromptSubmit: { - type: 'array', - label: 'Before Agent Hooks', - category: 'Advanced', + type: "array", + label: "Before Agent Hooks", + category: "Advanced", requiresRestart: false, default: [], description: - 'Hooks that execute before agent processing. Can modify prompts or inject context.', + "Hooks that execute before agent processing. Can modify prompts or inject context.", showInDialog: false, mergeStrategy: MergeStrategy.CONCAT, items: HOOK_DEFINITION_ITEMS, }, Stop: { - type: 'array', - label: 'After Agent Hooks', - category: 'Advanced', + type: "array", + label: "After Agent Hooks", + category: "Advanced", requiresRestart: false, default: [], description: - 'Hooks that execute after agent processing. Can post-process responses or log interactions.', + "Hooks that execute after agent processing. Can post-process responses or log interactions.", showInDialog: false, mergeStrategy: MergeStrategy.CONCAT, items: HOOK_DEFINITION_ITEMS, }, Notification: { - type: 'array', - label: 'Notification Hooks', - category: 'Advanced', + type: "array", + label: "Notification Hooks", + category: "Advanced", requiresRestart: false, default: [], - description: 'Hooks that execute when notifications are sent.', + description: "Hooks that execute when notifications are sent.", showInDialog: false, mergeStrategy: MergeStrategy.CONCAT, items: HOOK_DEFINITION_ITEMS, }, PreToolUse: { - type: 'array', - label: 'Pre Tool Use Hooks', - category: 'Advanced', + type: "array", + label: "Pre Tool Use Hooks", + category: "Advanced", requiresRestart: false, default: [], - description: 'Hooks that execute before tool execution.', + description: "Hooks that execute before tool execution.", showInDialog: false, mergeStrategy: MergeStrategy.CONCAT, items: HOOK_DEFINITION_ITEMS, }, PostToolUse: { - type: 'array', - label: 'Post Tool Use Hooks', - category: 'Advanced', + type: "array", + label: "Post Tool Use Hooks", + category: "Advanced", requiresRestart: false, default: [], - description: 'Hooks that execute after successful tool execution.', + description: "Hooks that execute after successful tool execution.", showInDialog: false, mergeStrategy: MergeStrategy.CONCAT, items: HOOK_DEFINITION_ITEMS, }, PostToolUseFailure: { - type: 'array', - label: 'Post Tool Use Failure Hooks', - category: 'Advanced', + type: "array", + label: "Post Tool Use Failure Hooks", + category: "Advanced", requiresRestart: false, default: [], - description: 'Hooks that execute when tool execution fails. ', + description: "Hooks that execute when tool execution fails. ", showInDialog: false, mergeStrategy: MergeStrategy.CONCAT, items: HOOK_DEFINITION_ITEMS, }, SessionStart: { - type: 'array', - label: 'Session Start Hooks', - category: 'Advanced', + type: "array", + label: "Session Start Hooks", + category: "Advanced", requiresRestart: false, default: [], - description: 'Hooks that execute when a new session starts or resumes.', + description: "Hooks that execute when a new session starts or resumes.", showInDialog: false, mergeStrategy: MergeStrategy.CONCAT, items: HOOK_DEFINITION_ITEMS, }, SessionEnd: { - type: 'array', - label: 'Session End Hooks', - category: 'Advanced', + type: "array", + label: "Session End Hooks", + category: "Advanced", requiresRestart: false, default: [], - description: 'Hooks that execute when a session ends.', + description: "Hooks that execute when a session ends.", showInDialog: false, mergeStrategy: MergeStrategy.CONCAT, items: HOOK_DEFINITION_ITEMS, }, PreCompact: { - type: 'array', - label: 'Pre Compact Hooks', - category: 'Advanced', + type: "array", + label: "Pre Compact Hooks", + category: "Advanced", requiresRestart: false, default: [], - description: 'Hooks that execute before conversation compaction.', + description: "Hooks that execute before conversation compaction.", showInDialog: false, mergeStrategy: MergeStrategy.CONCAT, items: HOOK_DEFINITION_ITEMS, }, SubagentStart: { - type: 'array', - label: 'Subagent Start Hooks', - category: 'Advanced', + type: "array", + label: "Subagent Start Hooks", + category: "Advanced", requiresRestart: false, default: [], - description: - 'Hooks that execute when a subagent (Task tool call) is started.', + description: "Hooks that execute when a subagent (Task tool call) is started.", showInDialog: false, mergeStrategy: MergeStrategy.CONCAT, items: HOOK_DEFINITION_ITEMS, }, SubagentStop: { - type: 'array', - label: 'Subagent Stop Hooks', - category: 'Advanced', + type: "array", + label: "Subagent Stop Hooks", + category: "Advanced", requiresRestart: false, default: [], description: - 'Hooks that execute right before a subagent (Task tool call) concludes its response.', + "Hooks that execute right before a subagent (Task tool call) concludes its response.", showInDialog: false, mergeStrategy: MergeStrategy.CONCAT, items: HOOK_DEFINITION_ITEMS, }, PermissionRequest: { - type: 'array', - label: 'Permission Request Hooks', - category: 'Advanced', + type: "array", + label: "Permission Request Hooks", + category: "Advanced", requiresRestart: false, default: [], - description: - 'Hooks that execute when a permission dialog is displayed.', + description: "Hooks that execute when a permission dialog is displayed.", showInDialog: false, mergeStrategy: MergeStrategy.CONCAT, items: HOOK_DEFINITION_ITEMS, @@ -1648,22 +1616,22 @@ const SETTINGS_SCHEMA = { }, experimental: { - type: 'object', - label: 'Experimental', - category: 'Experimental', + type: "object", + label: "Experimental", + category: "Experimental", requiresRestart: true, default: {}, - description: 'Settings to enable experimental features.', + description: "Settings to enable experimental features.", showInDialog: false, properties: { cron: { - type: 'boolean', - label: 'Enable Cron/Loop Tools', - category: 'Experimental', + type: "boolean", + label: "Enable Cron/Loop Tools", + category: "Experimental", requiresRestart: true, default: false, description: - 'Enable in-session cron/loop tools (experimental). When enabled, the model can create recurring prompts using cron_create, cron_list, and cron_delete tools. Can also be enabled via AIRISCODE_ENABLE_CRON=1 environment variable.', + "Enable in-session cron/loop tools (experimental). When enabled, the model can create recurring prompts using cron_create, cron_list, and cron_delete tools. Can also be enabled via AIRISCODE_ENABLE_CRON=1 environment variable.", showInDialog: true, }, }, @@ -1675,9 +1643,9 @@ export type SettingsSchemaType = typeof SETTINGS_SCHEMA; export function getSettingsSchema(): SettingsSchemaType { // Inject dynamic language options const schema = SETTINGS_SCHEMA as unknown as SettingsSchema; - if (schema['general']?.properties?.['language']) { + if (schema["general"]?.properties?.["language"]) { ( - schema['general'].properties['language'] as unknown as { + schema["general"].properties["language"] as unknown as { options?: SettingEnumOption[]; } ).options = getLanguageSettingsOptions(); @@ -1687,14 +1655,14 @@ export function getSettingsSchema(): SettingsSchemaType { type InferSettings = { -readonly [K in keyof T]?: T[K] extends { properties: SettingsSchema } - ? InferSettings - : T[K]['type'] extends 'enum' - ? T[K]['options'] extends readonly SettingEnumOption[] - ? T[K]['options'][number]['value'] - : T[K]['default'] - : T[K]['default'] extends boolean + ? InferSettings + : T[K]["type"] extends "enum" + ? T[K]["options"] extends readonly SettingEnumOption[] + ? T[K]["options"][number]["value"] + : T[K]["default"] + : T[K]["default"] extends boolean ? boolean - : T[K]['default']; + : T[K]["default"]; }; export type Settings = InferSettings; diff --git a/apps/airiscode-cli/src/config/trustedFolders.ts b/apps/airiscode-cli/src/config/trustedFolders.ts index d03c87c43..d1f12982b 100644 --- a/apps/airiscode-cli/src/config/trustedFolders.ts +++ b/apps/airiscode-cli/src/config/trustedFolders.ts @@ -4,34 +4,29 @@ * SPDX-License-Identifier: Apache-2.0 */ -import * as fs from 'node:fs'; -import * as path from 'node:path'; -import { homedir } from 'node:os'; -import { - FatalConfigError, - getErrorMessage, - isWithinRoot, - ideContextStore, -} from '@airiscode/core'; -import type { Settings } from './settings.js'; -import stripJsonComments from 'strip-json-comments'; -import { writeStderrLine } from '../utils/stdioHelpers.js'; - -export const TRUSTED_FOLDERS_FILENAME = 'trustedFolders.json'; -export const SETTINGS_DIRECTORY_NAME = '.airiscode'; +import * as fs from "node:fs"; +import { homedir } from "node:os"; +import * as path from "node:path"; +import { FatalConfigError, getErrorMessage, ideContextStore, isWithinRoot } from "@airiscode/core"; +import stripJsonComments from "strip-json-comments"; +import { writeStderrLine } from "../utils/stdioHelpers.js"; +import type { Settings } from "./settings.js"; + +export const TRUSTED_FOLDERS_FILENAME = "trustedFolders.json"; +export const SETTINGS_DIRECTORY_NAME = ".airiscode"; export const USER_SETTINGS_DIR = path.join(homedir(), SETTINGS_DIRECTORY_NAME); export function getTrustedFoldersPath(): string { - if (process.env['AIRISCODE_TRUSTED_FOLDERS_PATH']) { - return process.env['AIRISCODE_TRUSTED_FOLDERS_PATH']; + if (process.env["AIRISCODE_TRUSTED_FOLDERS_PATH"]) { + return process.env["AIRISCODE_TRUSTED_FOLDERS_PATH"]; } return path.join(USER_SETTINGS_DIR, TRUSTED_FOLDERS_FILENAME); } export enum TrustLevel { - TRUST_FOLDER = 'TRUST_FOLDER', - TRUST_PARENT = 'TRUST_PARENT', - DO_NOT_TRUST = 'DO_NOT_TRUST', + TRUST_FOLDER = "TRUST_FOLDER", + TRUST_PARENT = "TRUST_PARENT", + DO_NOT_TRUST = "DO_NOT_TRUST", } export interface TrustRule { @@ -51,7 +46,7 @@ export interface TrustedFoldersFile { export interface TrustResult { isTrusted: boolean | undefined; - source: 'ide' | 'file' | undefined; + source: "ide" | "file" | undefined; } export class LoadedTrustedFolders { @@ -139,16 +134,12 @@ export function loadTrustedFolders(): LoadedTrustedFolders { // Load user trusted folders try { if (fs.existsSync(userPath)) { - const content = fs.readFileSync(userPath, 'utf-8'); + const content = fs.readFileSync(userPath, "utf-8"); const parsed: unknown = JSON.parse(stripJsonComments(content)); - if ( - typeof parsed !== 'object' || - parsed === null || - Array.isArray(parsed) - ) { + if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) { errors.push({ - message: 'Trusted folders file is not a valid JSON object.', + message: "Trusted folders file is not a valid JSON object.", path: userPath, }); } else { @@ -162,16 +153,11 @@ export function loadTrustedFolders(): LoadedTrustedFolders { }); } - loadedTrustedFolders = new LoadedTrustedFolders( - { path: userPath, config: userConfig }, - errors, - ); + loadedTrustedFolders = new LoadedTrustedFolders({ path: userPath, config: userConfig }, errors); return loadedTrustedFolders; } -export function saveTrustedFolders( - trustedFoldersFile: TrustedFoldersFile, -): void { +export function saveTrustedFolders(trustedFoldersFile: TrustedFoldersFile): void { try { // Ensure the directory exists const dirPath = path.dirname(trustedFoldersFile.path); @@ -179,13 +165,12 @@ export function saveTrustedFolders( fs.mkdirSync(dirPath, { recursive: true }); } - fs.writeFileSync( - trustedFoldersFile.path, - JSON.stringify(trustedFoldersFile.config, null, 2), - { encoding: 'utf-8', mode: 0o600 }, - ); + fs.writeFileSync(trustedFoldersFile.path, JSON.stringify(trustedFoldersFile.config, null, 2), { + encoding: "utf-8", + mode: 0o600, + }); } catch (error) { - writeStderrLine('Error saving trusted folders file.'); + writeStderrLine("Error saving trusted folders file."); writeStderrLine(error instanceof Error ? error.message : String(error)); } } @@ -196,9 +181,7 @@ export function isFolderTrustEnabled(settings: Settings): boolean { return folderTrustSetting; } -function getWorkspaceTrustFromLocalConfig( - trustConfig?: Record, -): TrustResult { +function getWorkspaceTrustFromLocalConfig(trustConfig?: Record): TrustResult { const folders = loadTrustedFolders(); if (trustConfig) { @@ -206,18 +189,16 @@ function getWorkspaceTrustFromLocalConfig( } if (folders.errors.length > 0) { - const errorMessages = folders.errors.map( - (error) => `Error in ${error.path}: ${error.message}`, - ); + const errorMessages = folders.errors.map((error) => `Error in ${error.path}: ${error.message}`); throw new FatalConfigError( - `${errorMessages.join('\n')}\nPlease fix the configuration file and try again.`, + `${errorMessages.join("\n")}\nPlease fix the configuration file and try again.`, ); } const isTrusted = folders.isPathTrusted(process.cwd()); return { isTrusted, - source: isTrusted !== undefined ? 'file' : undefined, + source: isTrusted !== undefined ? "file" : undefined, }; } @@ -231,7 +212,7 @@ export function isWorkspaceTrusted( const ideTrust = ideContextStore.get()?.workspaceState?.isTrusted; if (ideTrust !== undefined) { - return { isTrusted: ideTrust, source: 'ide' }; + return { isTrusted: ideTrust, source: "ide" }; } // Fall back to the local user configuration diff --git a/apps/airiscode-cli/src/config/webSearch.ts b/apps/airiscode-cli/src/config/webSearch.ts index 4d6eb4d95..9e98b8d33 100644 --- a/apps/airiscode-cli/src/config/webSearch.ts +++ b/apps/airiscode-cli/src/config/webSearch.ts @@ -4,8 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { WebSearchProviderConfig } from '@airiscode/core'; -import type { Settings } from './settings.js'; +import type { WebSearchProviderConfig } from "@airiscode/core"; +import type { Settings } from "./settings.js"; /** * CLI arguments related to web search configuration @@ -52,22 +52,19 @@ export function buildWebSearchConfig( } else { // Build providers from command line args and environment variables const tavilyKey = - argv.tavilyApiKey || - settings.advanced?.tavilyApiKey || - process.env['TAVILY_API_KEY']; + argv.tavilyApiKey || settings.advanced?.tavilyApiKey || process.env["TAVILY_API_KEY"]; if (tavilyKey) { providers.push({ - type: 'tavily', + type: "tavily", apiKey: tavilyKey, } as WebSearchProviderConfig); } - const googleKey = argv.googleApiKey || process.env['GOOGLE_API_KEY']; - const googleEngineId = - argv.googleSearchEngineId || process.env['GOOGLE_SEARCH_ENGINE_ID']; + const googleKey = argv.googleApiKey || process.env["GOOGLE_API_KEY"]; + const googleEngineId = argv.googleSearchEngineId || process.env["GOOGLE_SEARCH_ENGINE_ID"]; if (googleKey && googleEngineId) { providers.push({ - type: 'google', + type: "google", apiKey: googleKey, searchEngineId: googleEngineId, } as WebSearchProviderConfig); @@ -81,10 +78,10 @@ export function buildWebSearchConfig( // Step 4: Determine default provider // Priority: user explicit config > CLI arg > first available provider (tavily > google > dashscope) - const providerPriority: Array<'tavily' | 'google' | 'dashscope'> = [ - 'tavily', - 'google', - 'dashscope', + const providerPriority: Array<"tavily" | "google" | "dashscope"> = [ + "tavily", + "google", + "dashscope", ]; // Determine default provider based on availability @@ -99,7 +96,7 @@ export function buildWebSearchConfig( } // Fallback to first available provider if none found in priority list if (!defaultProvider) { - defaultProvider = providers[0]?.type || 'dashscope'; + defaultProvider = providers[0]?.type || "dashscope"; } } diff --git a/apps/airiscode-cli/src/constants/alibabaStandardApiKey.ts b/apps/airiscode-cli/src/constants/alibabaStandardApiKey.ts index cb1c6170c..d48dd5a29 100644 --- a/apps/airiscode-cli/src/constants/alibabaStandardApiKey.ts +++ b/apps/airiscode-cli/src/constants/alibabaStandardApiKey.ts @@ -4,21 +4,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -export type AlibabaStandardRegion = - | 'cn-beijing' - | 'sg-singapore' - | 'us-virginia' - | 'cn-hongkong'; +export type AlibabaStandardRegion = "cn-beijing" | "sg-singapore" | "us-virginia" | "cn-hongkong"; -export const DASHSCOPE_STANDARD_API_KEY_ENV_KEY = 'DASHSCOPE_API_KEY'; +export const DASHSCOPE_STANDARD_API_KEY_ENV_KEY = "DASHSCOPE_API_KEY"; -export const ALIBABA_STANDARD_API_KEY_ENDPOINTS: Record< - AlibabaStandardRegion, - string -> = { - 'cn-beijing': 'https://dashscope.aliyuncs.com/compatible-mode/v1', - 'sg-singapore': 'https://dashscope-intl.aliyuncs.com/compatible-mode/v1', - 'us-virginia': 'https://dashscope-us.aliyuncs.com/compatible-mode/v1', - 'cn-hongkong': - 'https://cn-hongkong.dashscope.aliyuncs.com/compatible-mode/v1', +export const ALIBABA_STANDARD_API_KEY_ENDPOINTS: Record = { + "cn-beijing": "https://dashscope.aliyuncs.com/compatible-mode/v1", + "sg-singapore": "https://dashscope-intl.aliyuncs.com/compatible-mode/v1", + "us-virginia": "https://dashscope-us.aliyuncs.com/compatible-mode/v1", + "cn-hongkong": "https://cn-hongkong.dashscope.aliyuncs.com/compatible-mode/v1", }; diff --git a/apps/airiscode-cli/src/constants/codingPlan.ts b/apps/airiscode-cli/src/constants/codingPlan.ts index fb207a0b5..538cd1479 100644 --- a/apps/airiscode-cli/src/constants/codingPlan.ts +++ b/apps/airiscode-cli/src/constants/codingPlan.ts @@ -4,15 +4,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { createHash } from 'node:crypto'; -import type { ProviderModelConfig as ModelConfig } from '@airiscode/core'; +import { createHash } from "node:crypto"; +import type { ProviderModelConfig as ModelConfig } from "@airiscode/core"; /** * Coding plan regions */ export enum CodingPlanRegion { - CHINA = 'china', - GLOBAL = 'global', + CHINA = "china", + GLOBAL = "global", } /** @@ -25,7 +25,7 @@ export type CodingPlanTemplate = ModelConfig[]; * Environment variable key for storing the coding plan API key. * Unified key for both regions since they are mutually exclusive. */ -export const CODING_PLAN_ENV_KEY = 'BAILIAN_CODING_PLAN_API_KEY'; +export const CODING_PLAN_ENV_KEY = "BAILIAN_CODING_PLAN_API_KEY"; /** * Computes the version hash for the coding plan template. @@ -35,7 +35,7 @@ export const CODING_PLAN_ENV_KEY = 'BAILIAN_CODING_PLAN_API_KEY'; */ export function computeCodingPlanVersion(template: CodingPlanTemplate): string { const templateString = JSON.stringify(template); - return createHash('sha256').update(templateString).digest('hex'); + return createHash("sha256").update(templateString).digest("hex"); } /** @@ -45,17 +45,15 @@ export function computeCodingPlanVersion(template: CodingPlanTemplate): string { * @param region - The region to generate template for * @returns Complete model configuration array for the region */ -export function generateCodingPlanTemplate( - region: CodingPlanRegion, -): CodingPlanTemplate { +export function generateCodingPlanTemplate(region: CodingPlanRegion): CodingPlanTemplate { if (region === CodingPlanRegion.CHINA) { // China region uses legacy fields to maintain backward compatibility // This ensures existing users don't get prompted for unnecessary updates return [ { - id: 'qwen3.6-plus', - name: '[ModelStudio Coding Plan] qwen3.6-plus', - baseUrl: 'https://coding.dashscope.aliyuncs.com/v1', + id: "qwen3.6-plus", + name: "[ModelStudio Coding Plan] qwen3.6-plus", + baseUrl: "https://coding.dashscope.aliyuncs.com/v1", envKey: CODING_PLAN_ENV_KEY, generationConfig: { extra_body: { @@ -65,9 +63,9 @@ export function generateCodingPlanTemplate( }, }, { - id: 'qwen3.5-plus', - name: '[ModelStudio Coding Plan] qwen3.5-plus', - baseUrl: 'https://coding.dashscope.aliyuncs.com/v1', + id: "qwen3.5-plus", + name: "[ModelStudio Coding Plan] qwen3.5-plus", + baseUrl: "https://coding.dashscope.aliyuncs.com/v1", envKey: CODING_PLAN_ENV_KEY, generationConfig: { extra_body: { @@ -77,9 +75,9 @@ export function generateCodingPlanTemplate( }, }, { - id: 'glm-5', - name: '[ModelStudio Coding Plan] glm-5', - baseUrl: 'https://coding.dashscope.aliyuncs.com/v1', + id: "glm-5", + name: "[ModelStudio Coding Plan] glm-5", + baseUrl: "https://coding.dashscope.aliyuncs.com/v1", envKey: CODING_PLAN_ENV_KEY, generationConfig: { extra_body: { @@ -89,9 +87,9 @@ export function generateCodingPlanTemplate( }, }, { - id: 'kimi-k2.5', - name: '[ModelStudio Coding Plan] kimi-k2.5', - baseUrl: 'https://coding.dashscope.aliyuncs.com/v1', + id: "kimi-k2.5", + name: "[ModelStudio Coding Plan] kimi-k2.5", + baseUrl: "https://coding.dashscope.aliyuncs.com/v1", envKey: CODING_PLAN_ENV_KEY, generationConfig: { extra_body: { @@ -101,9 +99,9 @@ export function generateCodingPlanTemplate( }, }, { - id: 'MiniMax-M2.5', - name: '[ModelStudio Coding Plan] MiniMax-M2.5', - baseUrl: 'https://coding.dashscope.aliyuncs.com/v1', + id: "MiniMax-M2.5", + name: "[ModelStudio Coding Plan] MiniMax-M2.5", + baseUrl: "https://coding.dashscope.aliyuncs.com/v1", envKey: CODING_PLAN_ENV_KEY, generationConfig: { extra_body: { @@ -113,27 +111,27 @@ export function generateCodingPlanTemplate( }, }, { - id: 'qwen3-coder-plus', - name: '[ModelStudio Coding Plan] qwen3-coder-plus', - baseUrl: 'https://coding.dashscope.aliyuncs.com/v1', + id: "qwen3-coder-plus", + name: "[ModelStudio Coding Plan] qwen3-coder-plus", + baseUrl: "https://coding.dashscope.aliyuncs.com/v1", envKey: CODING_PLAN_ENV_KEY, generationConfig: { contextWindowSize: 1000000, }, }, { - id: 'qwen3-coder-next', - name: '[ModelStudio Coding Plan] qwen3-coder-next', - baseUrl: 'https://coding.dashscope.aliyuncs.com/v1', + id: "qwen3-coder-next", + name: "[ModelStudio Coding Plan] qwen3-coder-next", + baseUrl: "https://coding.dashscope.aliyuncs.com/v1", envKey: CODING_PLAN_ENV_KEY, generationConfig: { contextWindowSize: 262144, }, }, { - id: 'qwen3-max-2026-01-23', - name: '[ModelStudio Coding Plan] qwen3-max-2026-01-23', - baseUrl: 'https://coding.dashscope.aliyuncs.com/v1', + id: "qwen3-max-2026-01-23", + name: "[ModelStudio Coding Plan] qwen3-max-2026-01-23", + baseUrl: "https://coding.dashscope.aliyuncs.com/v1", envKey: CODING_PLAN_ENV_KEY, generationConfig: { extra_body: { @@ -143,9 +141,9 @@ export function generateCodingPlanTemplate( }, }, { - id: 'glm-4.7', - name: '[ModelStudio Coding Plan] glm-4.7', - baseUrl: 'https://coding.dashscope.aliyuncs.com/v1', + id: "glm-4.7", + name: "[ModelStudio Coding Plan] glm-4.7", + baseUrl: "https://coding.dashscope.aliyuncs.com/v1", envKey: CODING_PLAN_ENV_KEY, generationConfig: { extra_body: { @@ -160,9 +158,9 @@ export function generateCodingPlanTemplate( // Global region uses ModelStudio Coding Plan branding for Global/Intl return [ { - id: 'qwen3.6-plus', - name: '[ModelStudio Coding Plan for Global/Intl] qwen3.6-plus', - baseUrl: 'https://coding-intl.dashscope.aliyuncs.com/v1', + id: "qwen3.6-plus", + name: "[ModelStudio Coding Plan for Global/Intl] qwen3.6-plus", + baseUrl: "https://coding-intl.dashscope.aliyuncs.com/v1", envKey: CODING_PLAN_ENV_KEY, generationConfig: { extra_body: { @@ -172,9 +170,9 @@ export function generateCodingPlanTemplate( }, }, { - id: 'qwen3.5-plus', - name: '[ModelStudio Coding Plan for Global/Intl] qwen3.5-plus', - baseUrl: 'https://coding-intl.dashscope.aliyuncs.com/v1', + id: "qwen3.5-plus", + name: "[ModelStudio Coding Plan for Global/Intl] qwen3.5-plus", + baseUrl: "https://coding-intl.dashscope.aliyuncs.com/v1", envKey: CODING_PLAN_ENV_KEY, generationConfig: { extra_body: { @@ -184,27 +182,27 @@ export function generateCodingPlanTemplate( }, }, { - id: 'qwen3-coder-plus', - name: '[ModelStudio Coding Plan for Global/Intl] qwen3-coder-plus', - baseUrl: 'https://coding-intl.dashscope.aliyuncs.com/v1', + id: "qwen3-coder-plus", + name: "[ModelStudio Coding Plan for Global/Intl] qwen3-coder-plus", + baseUrl: "https://coding-intl.dashscope.aliyuncs.com/v1", envKey: CODING_PLAN_ENV_KEY, generationConfig: { contextWindowSize: 1000000, }, }, { - id: 'qwen3-coder-next', - name: '[ModelStudio Coding Plan for Global/Intl] qwen3-coder-next', - baseUrl: 'https://coding-intl.dashscope.aliyuncs.com/v1', + id: "qwen3-coder-next", + name: "[ModelStudio Coding Plan for Global/Intl] qwen3-coder-next", + baseUrl: "https://coding-intl.dashscope.aliyuncs.com/v1", envKey: CODING_PLAN_ENV_KEY, generationConfig: { contextWindowSize: 262144, }, }, { - id: 'qwen3-max-2026-01-23', - name: '[ModelStudio Coding Plan for Global/Intl] qwen3-max-2026-01-23', - baseUrl: 'https://coding-intl.dashscope.aliyuncs.com/v1', + id: "qwen3-max-2026-01-23", + name: "[ModelStudio Coding Plan for Global/Intl] qwen3-max-2026-01-23", + baseUrl: "https://coding-intl.dashscope.aliyuncs.com/v1", envKey: CODING_PLAN_ENV_KEY, generationConfig: { extra_body: { @@ -214,9 +212,9 @@ export function generateCodingPlanTemplate( }, }, { - id: 'glm-4.7', - name: '[ModelStudio Coding Plan for Global/Intl] glm-4.7', - baseUrl: 'https://coding-intl.dashscope.aliyuncs.com/v1', + id: "glm-4.7", + name: "[ModelStudio Coding Plan for Global/Intl] glm-4.7", + baseUrl: "https://coding-intl.dashscope.aliyuncs.com/v1", envKey: CODING_PLAN_ENV_KEY, generationConfig: { extra_body: { @@ -226,9 +224,9 @@ export function generateCodingPlanTemplate( }, }, { - id: 'glm-5', - name: '[ModelStudio Coding Plan for Global/Intl] glm-5', - baseUrl: 'https://coding-intl.dashscope.aliyuncs.com/v1', + id: "glm-5", + name: "[ModelStudio Coding Plan for Global/Intl] glm-5", + baseUrl: "https://coding-intl.dashscope.aliyuncs.com/v1", envKey: CODING_PLAN_ENV_KEY, generationConfig: { extra_body: { @@ -238,9 +236,9 @@ export function generateCodingPlanTemplate( }, }, { - id: 'MiniMax-M2.5', - name: '[ModelStudio Coding Plan for Global/Intl] MiniMax-M2.5', - baseUrl: 'https://coding-intl.dashscope.aliyuncs.com/v1', + id: "MiniMax-M2.5", + name: "[ModelStudio Coding Plan for Global/Intl] MiniMax-M2.5", + baseUrl: "https://coding-intl.dashscope.aliyuncs.com/v1", envKey: CODING_PLAN_ENV_KEY, generationConfig: { extra_body: { @@ -250,9 +248,9 @@ export function generateCodingPlanTemplate( }, }, { - id: 'kimi-k2.5', - name: '[ModelStudio Coding Plan for Global/Intl] kimi-k2.5', - baseUrl: 'https://coding-intl.dashscope.aliyuncs.com/v1', + id: "kimi-k2.5", + name: "[ModelStudio Coding Plan for Global/Intl] kimi-k2.5", + baseUrl: "https://coding-intl.dashscope.aliyuncs.com/v1", envKey: CODING_PLAN_ENV_KEY, generationConfig: { extra_body: { @@ -273,8 +271,8 @@ export function getCodingPlanConfig(region: CodingPlanRegion) { const template = generateCodingPlanTemplate(region); const baseUrl = region === CodingPlanRegion.CHINA - ? 'https://coding.dashscope.aliyuncs.com/v1' - : 'https://coding-intl.dashscope.aliyuncs.com/v1'; + ? "https://coding.dashscope.aliyuncs.com/v1" + : "https://coding-intl.dashscope.aliyuncs.com/v1"; return { template, baseUrl, @@ -288,8 +286,8 @@ export function getCodingPlanConfig(region: CodingPlanRegion) { */ export function getCodingPlanBaseUrls(): string[] { return [ - 'https://coding.dashscope.aliyuncs.com/v1', - 'https://coding-intl.dashscope.aliyuncs.com/v1', + "https://coding.dashscope.aliyuncs.com/v1", + "https://coding-intl.dashscope.aliyuncs.com/v1", ]; } @@ -314,10 +312,10 @@ export function isCodingPlanConfig( } // Check which region's baseUrl matches - if (baseUrl === 'https://coding.dashscope.aliyuncs.com/v1') { + if (baseUrl === "https://coding.dashscope.aliyuncs.com/v1") { return CodingPlanRegion.CHINA; } - if (baseUrl === 'https://coding-intl.dashscope.aliyuncs.com/v1') { + if (baseUrl === "https://coding-intl.dashscope.aliyuncs.com/v1") { return CodingPlanRegion.GLOBAL; } @@ -329,15 +327,13 @@ export function isCodingPlanConfig( * @param baseUrl - The baseUrl to check * @returns The region if matched, null otherwise */ -export function getRegionFromBaseUrl( - baseUrl: string | undefined, -): CodingPlanRegion | null { +export function getRegionFromBaseUrl(baseUrl: string | undefined): CodingPlanRegion | null { if (!baseUrl) return null; - if (baseUrl === 'https://coding.dashscope.aliyuncs.com/v1') { + if (baseUrl === "https://coding.dashscope.aliyuncs.com/v1") { return CodingPlanRegion.CHINA; } - if (baseUrl === 'https://coding-intl.dashscope.aliyuncs.com/v1') { + if (baseUrl === "https://coding-intl.dashscope.aliyuncs.com/v1") { return CodingPlanRegion.GLOBAL; } diff --git a/apps/airiscode-cli/src/core/auth.ts b/apps/airiscode-cli/src/core/auth.ts index 9c1a8bd94..009428461 100644 --- a/apps/airiscode-cli/src/core/auth.ts +++ b/apps/airiscode-cli/src/core/auth.ts @@ -4,13 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { - type AuthType, - type Config, - getErrorMessage, - logAuth, - AuthEvent, -} from '@airiscode/core'; +import { AuthEvent, type AuthType, type Config, getErrorMessage, logAuth } from "@airiscode/core"; /** * Handles the initial authentication flow. @@ -32,13 +26,13 @@ export async function performInitialAuth( // We can add a dedicated startup message later if needed. // Log authentication success - const authEvent = new AuthEvent(authType, 'auto', 'success'); + const authEvent = new AuthEvent(authType, "auto", "success"); logAuth(config, authEvent); } catch (e) { const errorMessage = `Failed to login. Message: ${getErrorMessage(e)}`; // Log authentication failure - const authEvent = new AuthEvent(authType, 'auto', 'error', errorMessage); + const authEvent = new AuthEvent(authType, "auto", "error", errorMessage); logAuth(config, authEvent); return errorMessage; diff --git a/apps/airiscode-cli/src/core/initializer.ts b/apps/airiscode-cli/src/core/initializer.ts index bc08e61d7..5e54d3058 100644 --- a/apps/airiscode-cli/src/core/initializer.ts +++ b/apps/airiscode-cli/src/core/initializer.ts @@ -5,16 +5,16 @@ */ import { + type Config, IdeClient, IdeConnectionEvent, IdeConnectionType, logIdeConnection, - type Config, -} from '@airiscode/core'; -import { type LoadedSettings } from '../config/settings.js'; -import { performInitialAuth } from './auth.js'; -import { validateTheme } from './theme.js'; -import { initializeI18n, type SupportedLanguage } from '../i18n/index.js'; +} from "@airiscode/core"; +import { type LoadedSettings } from "../config/settings.js"; +import { initializeI18n, type SupportedLanguage } from "../i18n/index.js"; +import { performInitialAuth } from "./auth.js"; +import { validateTheme } from "./theme.js"; export interface InitializationResult { authError: string | null; @@ -36,10 +36,8 @@ export async function initializeApp( ): Promise { // Initialize i18n system const languageSetting = - process.env['AIRISCODE_LANG'] || - (settings.merged.general?.language as string) || - 'auto'; - await initializeI18n(languageSetting as SupportedLanguage | 'auto'); + process.env["AIRISCODE_LANG"] || (settings.merged.general?.language as string) || "auto"; + await initializeI18n(languageSetting as SupportedLanguage | "auto"); // Use authType from modelsConfig which respects CLI --auth-type argument // over settings.security.auth.selectedType diff --git a/apps/airiscode-cli/src/core/theme.ts b/apps/airiscode-cli/src/core/theme.ts index 7acb4abd2..d618f1862 100644 --- a/apps/airiscode-cli/src/core/theme.ts +++ b/apps/airiscode-cli/src/core/theme.ts @@ -4,9 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { themeManager } from '../ui/themes/theme-manager.js'; -import { type LoadedSettings } from '../config/settings.js'; -import { t } from '../i18n/index.js'; +import { type LoadedSettings } from "../config/settings.js"; +import { t } from "../i18n/index.js"; +import { themeManager } from "../ui/themes/theme-manager.js"; /** * Validates the configured theme. diff --git a/apps/airiscode-cli/src/gemini.tsx b/apps/airiscode-cli/src/gemini.tsx index d41745bb4..0cb46cd07 100644 --- a/apps/airiscode-cli/src/gemini.tsx +++ b/apps/airiscode-cli/src/gemini.tsx @@ -4,74 +4,62 @@ * SPDX-License-Identifier: Apache-2.0 */ +import dns from "node:dns"; +import os from "node:os"; +import { basename } from "node:path"; +import v8 from "node:v8"; import { + type Config, + createDebugLogger, InputFormat, isDebugLoggingDegraded, logUserPrompt, Storage, - type Config, - createDebugLogger, -} from '@airiscode/core'; -import { render } from 'ink'; -import dns from 'node:dns'; -import os from 'node:os'; -import { basename } from 'node:path'; -import v8 from 'node:v8'; -import React from 'react'; -import { validateAuthMethod } from './config/auth.js'; -import * as cliConfig from './config/config.js'; -import { loadCliConfig, parseArguments } from './config/config.js'; -import type { DnsResolutionOrder, LoadedSettings } from './config/settings.js'; -import { getSettingsWarnings, loadSettings } from './config/settings.js'; -import { - initializeApp, - type InitializationResult, -} from './core/initializer.js'; -import { runNonInteractive } from './nonInteractiveCli.js'; -import { runNonInteractiveStreamJson } from './nonInteractive/session.js'; -import { AppContainer } from './ui/AppContainer.js'; -import { setMaxSizedBoxDebugging } from './ui/components/shared/MaxSizedBox.js'; -import { KeypressProvider } from './ui/contexts/KeypressContext.js'; -import { SessionStatsProvider } from './ui/contexts/SessionContext.js'; -import { SettingsContext } from './ui/contexts/SettingsContext.js'; -import { VimModeProvider } from './ui/contexts/VimModeContext.js'; -import { AgentViewProvider } from './ui/contexts/AgentViewContext.js'; -import { useKittyKeyboardProtocol } from './ui/hooks/useKittyKeyboardProtocol.js'; -import { themeManager } from './ui/themes/theme-manager.js'; -import { detectAndEnableKittyProtocol } from './ui/utils/kittyProtocolDetector.js'; -import { checkForUpdates } from './ui/utils/updateCheck.js'; -import { - cleanupCheckpoints, - registerCleanup, - runExitCleanup, -} from './utils/cleanup.js'; -import { AppEvent, appEvents } from './utils/events.js'; -import { handleAutoUpdate } from './utils/handleAutoUpdate.js'; -import { readStdin } from './utils/readStdin.js'; -import { - relaunchAppInChildProcess, - relaunchOnExitCode, -} from './utils/relaunch.js'; -import { start_sandbox } from './utils/sandbox.js'; -import { getStartupWarnings } from './utils/startupWarnings.js'; -import { getUserStartupWarnings } from './utils/userStartupWarnings.js'; -import { getCliVersion } from './utils/version.js'; -import { writeStderrLine } from './utils/stdioHelpers.js'; -import { computeWindowTitle } from './utils/windowTitle.js'; -import { validateNonInteractiveAuth } from './validateNonInterActiveAuth.js'; -import { showResumeSessionPicker } from './ui/components/StandaloneSessionPicker.js'; -import { initializeLlmOutputLanguage } from './utils/languageUtils.js'; - -const debugLogger = createDebugLogger('STARTUP'); - -export function validateDnsResolutionOrder( - order: string | undefined, -): DnsResolutionOrder { - const defaultValue: DnsResolutionOrder = 'ipv4first'; +} from "@airiscode/core"; +import { render } from "ink"; +import React from "react"; +import { validateAuthMethod } from "./config/auth.js"; +import * as cliConfig from "./config/config.js"; +import { loadCliConfig, parseArguments } from "./config/config.js"; +import type { DnsResolutionOrder, LoadedSettings } from "./config/settings.js"; +import { getSettingsWarnings, loadSettings } from "./config/settings.js"; +import { type InitializationResult, initializeApp } from "./core/initializer.js"; +import { runNonInteractiveStreamJson } from "./nonInteractive/session.js"; +import { runNonInteractive } from "./nonInteractiveCli.js"; +import { AppContainer } from "./ui/AppContainer.js"; +import { showResumeSessionPicker } from "./ui/components/StandaloneSessionPicker.js"; +import { setMaxSizedBoxDebugging } from "./ui/components/shared/MaxSizedBox.js"; +import { AgentViewProvider } from "./ui/contexts/AgentViewContext.js"; +import { KeypressProvider } from "./ui/contexts/KeypressContext.js"; +import { SessionStatsProvider } from "./ui/contexts/SessionContext.js"; +import { SettingsContext } from "./ui/contexts/SettingsContext.js"; +import { VimModeProvider } from "./ui/contexts/VimModeContext.js"; +import { useKittyKeyboardProtocol } from "./ui/hooks/useKittyKeyboardProtocol.js"; +import { themeManager } from "./ui/themes/theme-manager.js"; +import { detectAndEnableKittyProtocol } from "./ui/utils/kittyProtocolDetector.js"; +import { checkForUpdates } from "./ui/utils/updateCheck.js"; +import { cleanupCheckpoints, registerCleanup, runExitCleanup } from "./utils/cleanup.js"; +import { AppEvent, appEvents } from "./utils/events.js"; +import { handleAutoUpdate } from "./utils/handleAutoUpdate.js"; +import { initializeLlmOutputLanguage } from "./utils/languageUtils.js"; +import { readStdin } from "./utils/readStdin.js"; +import { relaunchAppInChildProcess, relaunchOnExitCode } from "./utils/relaunch.js"; +import { start_sandbox } from "./utils/sandbox.js"; +import { getStartupWarnings } from "./utils/startupWarnings.js"; +import { writeStderrLine } from "./utils/stdioHelpers.js"; +import { getUserStartupWarnings } from "./utils/userStartupWarnings.js"; +import { getCliVersion } from "./utils/version.js"; +import { computeWindowTitle } from "./utils/windowTitle.js"; +import { validateNonInteractiveAuth } from "./validateNonInterActiveAuth.js"; + +const debugLogger = createDebugLogger("STARTUP"); + +export function validateDnsResolutionOrder(order: string | undefined): DnsResolutionOrder { + const defaultValue: DnsResolutionOrder = "ipv4first"; if (order === undefined) { return defaultValue; } - if (order === 'ipv4first' || order === 'verbatim') { + if (order === "ipv4first" || order === "verbatim") { return order; } // We don't want to throw here, just warn and use the default. @@ -84,19 +72,15 @@ export function validateDnsResolutionOrder( function getNodeMemoryArgs(isDebugMode: boolean): string[] { const totalMemoryMB = os.totalmem() / (1024 * 1024); const heapStats = v8.getHeapStatistics(); - const currentMaxOldSpaceSizeMb = Math.floor( - heapStats.heap_size_limit / 1024 / 1024, - ); + const currentMaxOldSpaceSizeMb = Math.floor(heapStats.heap_size_limit / 1024 / 1024); // Set target to 50% of total memory const targetMaxOldSpaceSizeInMB = Math.floor(totalMemoryMB * 0.5); if (isDebugMode) { - writeStderrLine( - `Current heap size ${currentMaxOldSpaceSizeMb.toFixed(2)} MB`, - ); + writeStderrLine(`Current heap size ${currentMaxOldSpaceSizeMb.toFixed(2)} MB`); } - if (process.env['AIRISCODE_NO_RELAUNCH']) { + if (process.env["AIRISCODE_NO_RELAUNCH"]) { return []; } @@ -112,12 +96,13 @@ function getNodeMemoryArgs(isDebugMode: boolean): string[] { return []; } -import { loadSandboxConfig } from './config/sandboxConfig.js'; +import { loadSandboxConfig } from "./config/sandboxConfig.js"; + const runAcpAgent = null as any; export function setupUnhandledRejectionHandler() { let unhandledRejectionOccurred = false; - process.on('unhandledRejection', (reason, _promise) => { + process.on("unhandledRejection", (reason, _promise) => { const errorMessage = `========================================= This is an unexpected error. Please file a bug report using the /bug tool. CRITICAL: Unhandled Promise Rejection! @@ -127,7 +112,7 @@ Reason: ${reason}${ ? ` Stack trace: ${reason.stack}` - : '' + : "" }`; appEvents.emit(AppEvent.LogError, errorMessage); if (!unhandledRejectionOccurred) { @@ -150,16 +135,14 @@ export async function startInteractiveUI( // Create wrapper component to use hooks inside render const AppWrapper = () => { const kittyProtocolStatus = useKittyKeyboardProtocol(); - const nodeMajorVersion = parseInt(process.versions.node.split('.')[0], 10); + const nodeMajorVersion = parseInt(process.versions.node.split(".")[0], 10); return ( @@ -180,7 +163,7 @@ export async function startInteractiveUI( }; const instance = render( - process.env['DEBUG'] ? ( + process.env["DEBUG"] ? ( @@ -219,7 +202,7 @@ export async function main() { // Check for invalid input combinations early to prevent crashes if (argv.promptInteractive && !process.stdin.isTTY) { writeStderrLine( - 'Error: The --prompt-interactive flag cannot be used when input is piped from stdin.', + "Error: The --prompt-interactive flag cannot be used when input is piped from stdin.", ); process.exit(1); } @@ -237,14 +220,12 @@ export async function main() { if (!themeManager.setActiveTheme(settings.merged.ui?.theme)) { // If the theme is not found during initial load, log a warning and continue. // The useThemeCommand hook in AppContainer.tsx will handle opening the dialog. - writeStderrLine( - `Warning: Theme "${settings.merged.ui?.theme}" not found.`, - ); + writeStderrLine(`Warning: Theme "${settings.merged.ui?.theme}" not found.`); } } // hop into sandbox if we are outside and sandboxing is enabled - if (!process.env['SANDBOX']) { + if (!process.env["SANDBOX"]) { const memoryArgs = settings.merged.advanced?.autoConfigureMemory ? getNodeMemoryArgs(isDebugMode) : []; @@ -256,12 +237,7 @@ export async function main() { // another way to decouple refreshAuth from requiring a config. if (sandboxConfig) { - const partialConfig = await loadCliConfig( - settings.merged, - argv, - undefined, - [], - ); + const partialConfig = await loadCliConfig(settings.merged, argv, undefined, []); if (!settings.merged.security?.auth?.useExternal) { // Validate authentication here because the sandbox will interfere with the Oauth2 web redirect. @@ -287,29 +263,23 @@ export async function main() { // intact via stdio: 'inherit'. const inputFormat = argv.inputFormat as string | undefined; const isAcpMode = argv.acp || argv.experimentalAcp; - let stdinData = ''; - if (!process.stdin.isTTY && inputFormat !== 'stream-json' && !isAcpMode) { + let stdinData = ""; + if (!process.stdin.isTTY && inputFormat !== "stream-json" && !isAcpMode) { stdinData = await readStdin(); } // This function is a copy of the one from sandbox.ts // It is moved here to decouple sandbox.ts from the CLI's argument structure. - const injectStdinIntoArgs = ( - args: string[], - stdinData?: string, - ): string[] => { + const injectStdinIntoArgs = (args: string[], stdinData?: string): string[] => { const finalArgs = [...args]; if (stdinData) { - const promptIndex = finalArgs.findIndex( - (arg) => arg === '--prompt' || arg === '-p', - ); + const promptIndex = finalArgs.findIndex((arg) => arg === "--prompt" || arg === "-p"); if (promptIndex > -1 && finalArgs.length > promptIndex + 1) { // If there's a prompt argument, prepend stdin to it - finalArgs[promptIndex + 1] = - `${stdinData}\n\n${finalArgs[promptIndex + 1]}`; + finalArgs[promptIndex + 1] = `${stdinData}\n\n${finalArgs[promptIndex + 1]}`; } else { // If there's no prompt argument, add stdin as the prompt - finalArgs.push('--prompt', stdinData); + finalArgs.push("--prompt", stdinData); } } return finalArgs; @@ -332,11 +302,8 @@ export async function main() { // Set the runtime output dir early so the picker can find sessions stored // under a custom runtimeOutputDir (setRuntimeBaseDir is idempotent and will // be called again inside loadCliConfig). - if (argv.resume === '') { - Storage.setRuntimeBaseDir( - settings.merged.advanced?.runtimeOutputDir, - process.cwd(), - ); + if (argv.resume === "") { + Storage.setRuntimeBaseDir(settings.merged.advanced?.runtimeOutputDir, process.cwd()); const selectedSessionId = await showResumeSessionPicker(); if (!selectedSessionId) { // User cancelled or no sessions available @@ -355,12 +322,7 @@ export async function main() { initializeLlmOutputLanguage(settings.merged.general?.outputLanguage); { - const config = await loadCliConfig( - settings.merged, - argv, - process.cwd(), - argv.extensions, - ); + const config = await loadCliConfig(settings.merged, argv, process.cwd(), argv.extensions); // Register cleanup for MCP clients as early as possible // This ensures MCP server subprocesses are properly terminated on exit @@ -383,10 +345,10 @@ export async function main() { process.stdin.setRawMode(true); // This cleanup isn't strictly needed but may help in certain situations. - process.on('SIGTERM', () => { + process.on("SIGTERM", () => { process.stdin.setRawMode(wasRaw); }); - process.on('SIGINT', () => { + process.on("SIGINT", () => { process.stdin.setRawMode(wasRaw); }); @@ -400,7 +362,7 @@ export async function main() { // In TTY mode, ignore stream-json input format to prevent process from hanging const inputFormat = process.stdin.isTTY ? InputFormat.TEXT - : typeof config.getInputFormat === 'function' + : typeof config.getInputFormat === "function" ? config.getInputFormat() : InputFormat.TEXT; @@ -445,14 +407,10 @@ export async function main() { // Print debug mode notice to stderr for non-interactive mode if (config.getDebugMode()) { - writeStderrLine('Debug mode enabled'); - writeStderrLine( - `Logging to: ${Storage.getDebugLogPath(config.getSessionId())}`, - ); + writeStderrLine("Debug mode enabled"); + writeStderrLine(`Logging to: ${Storage.getDebugLogPath(config.getSessionId())}`); if (isDebugLoggingDegraded()) { - writeStderrLine( - 'Warning: Debug logging is degraded (write failures occurred)', - ); + writeStderrLine("Warning: Debug logging is degraded (write failures occurred)"); } } @@ -480,11 +438,11 @@ export async function main() { const prompt_id = Math.random().toString(16).slice(2); if (inputFormat === InputFormat.STREAM_JSON) { - const trimmedInput = (input ?? '').trim(); + const trimmedInput = (input ?? "").trim(); await runNonInteractiveStreamJson( nonInteractiveConfig, - trimmedInput.length > 0 ? trimmedInput : '', + trimmedInput.length > 0 ? trimmedInput : "", ); await runExitCleanup(); process.exit(0); @@ -498,8 +456,8 @@ export async function main() { } logUserPrompt(config, { - 'event.name': 'user_prompt', - 'event.timestamp': new Date().toISOString(), + "event.name": "user_prompt", + "event.timestamp": new Date().toISOString(), prompt: input, prompt_id, auth_type: config.getContentGeneratorConfig()?.authType, @@ -520,7 +478,7 @@ function setWindowTitle(title: string, settings: LoadedSettings) { const windowTitle = computeWindowTitle(title); process.stdout.write(`\x1b]2;${windowTitle}\x07`); - process.on('exit', () => { + process.on("exit", () => { process.stdout.write(`\x1b]2;\x07`); }); } diff --git a/apps/airiscode-cli/src/i18n/index.ts b/apps/airiscode-cli/src/i18n/index.ts index 4436b9b52..bd535c87d 100644 --- a/apps/airiscode-cli/src/i18n/index.ts +++ b/apps/airiscode-cli/src/i18n/index.ts @@ -4,22 +4,22 @@ * SPDX-License-Identifier: Apache-2.0 */ -import * as fs from 'node:fs'; -import * as path from 'node:path'; -import { fileURLToPath, pathToFileURL } from 'node:url'; -import { homedir } from 'node:os'; -import { writeStderrLine } from '../utils/stdioHelpers.js'; +import * as fs from "node:fs"; +import { homedir } from "node:os"; +import * as path from "node:path"; +import { fileURLToPath, pathToFileURL } from "node:url"; +import { writeStderrLine } from "../utils/stdioHelpers.js"; import { - type SupportedLanguage, - SUPPORTED_LANGUAGES, getLanguageNameFromLocale, -} from './languages.js'; + SUPPORTED_LANGUAGES, + type SupportedLanguage, +} from "./languages.js"; export type { SupportedLanguage }; export { getLanguageNameFromLocale }; // State -let currentLanguage: SupportedLanguage = 'en'; +let currentLanguage: SupportedLanguage = "en"; let translations: Record = {}; // Cache @@ -30,11 +30,10 @@ const loadingPromises: Record> = {}; // Path helpers const getBuiltinLocalesDir = (): string => { const __filename = fileURLToPath(import.meta.url); - return path.join(path.dirname(__filename), 'locales'); + return path.join(path.dirname(__filename), "locales"); }; -const getUserLocalesDir = (): string => - path.join(homedir(), '.airiscode', 'locales'); +const getUserLocalesDir = (): string => path.join(homedir(), ".airiscode", "locales"); /** * Get the path to the user's custom locales directory. @@ -45,17 +44,14 @@ export function getUserLocalesDirectory(): string { return getUserLocalesDir(); } -const getLocalePath = ( - lang: SupportedLanguage, - useUserDir: boolean = false, -): string => { +const getLocalePath = (lang: SupportedLanguage, useUserDir: boolean = false): string => { const baseDir = useUserDir ? getUserLocalesDir() : getBuiltinLocalesDir(); return path.join(baseDir, `${lang}.js`); }; // Language detection export function detectSystemLanguage(): SupportedLanguage { - const envLang = process.env['AIRISCODE_LANG'] || process.env['LANG']; + const envLang = process.env["AIRISCODE_LANG"] || process.env["LANG"]; if (envLang) { for (const lang of SUPPORTED_LANGUAGES) { if (envLang.startsWith(lang.code)) return lang.code; @@ -71,13 +67,11 @@ export function detectSystemLanguage(): SupportedLanguage { // Fallback to default } - return 'en'; + return "en"; } // Translation loading -async function loadTranslationsAsync( - lang: SupportedLanguage, -): Promise { +async function loadTranslationsAsync(lang: SupportedLanguage): Promise { if (translationCache[lang]) { return translationCache[lang]; } @@ -111,15 +105,11 @@ async function loadTranslationsAsync( try { const module = await import(fileUrl); const result = module.default || module; - if ( - result && - typeof result === 'object' && - Object.keys(result).length > 0 - ) { + if (result && typeof result === "object" && Object.keys(result).length > 0) { translationCache[lang] = result; return result; } else { - throw new Error('Module loaded but result is empty or invalid'); + throw new Error("Module loaded but result is empty or invalid"); } } catch { // For builtin locales, try alternative import method (relative path) @@ -127,11 +117,7 @@ async function loadTranslationsAsync( try { const module = await import(`./locales/${lang}.js`); const result = module.default || module; - if ( - result && - typeof result === 'object' && - Object.keys(result).length > 0 - ) { + if (result && typeof result === "object" && Object.keys(result).length > 0) { translationCache[lang] = result; return result; } @@ -180,24 +166,18 @@ function loadTranslations(lang: SupportedLanguage): TranslationDict { } // String interpolation -function interpolate( - template: string, - params?: Record, -): string { +function interpolate(template: string, params?: Record): string { if (!params) return template; - return template.replace( - /\{\{(\w+)\}\}/g, - (match, key) => params[key] ?? match, - ); + return template.replace(/\{\{(\w+)\}\}/g, (match, key) => params[key] ?? match); } // Language setting helpers -function resolveLanguage(lang: SupportedLanguage | 'auto'): SupportedLanguage { - return lang === 'auto' ? detectSystemLanguage() : lang; +function resolveLanguage(lang: SupportedLanguage | "auto"): SupportedLanguage { + return lang === "auto" ? detectSystemLanguage() : lang; } // Public API -export function setLanguage(lang: SupportedLanguage | 'auto'): void { +export function setLanguage(lang: SupportedLanguage | "auto"): void { const resolvedLang = resolveLanguage(lang); currentLanguage = resolvedLang; @@ -218,9 +198,7 @@ export function setLanguage(lang: SupportedLanguage | 'auto'): void { } } -export async function setLanguageAsync( - lang: SupportedLanguage | 'auto', -): Promise { +export async function setLanguageAsync(lang: SupportedLanguage | "auto"): Promise { currentLanguage = resolveLanguage(lang); translations = await loadTranslationsAsync(currentLanguage); } @@ -250,8 +228,6 @@ export function ta(key: string): string[] { return []; } -export async function initializeI18n( - lang?: SupportedLanguage | 'auto', -): Promise { - await setLanguageAsync(lang ?? 'auto'); +export async function initializeI18n(lang?: SupportedLanguage | "auto"): Promise { + await setLanguageAsync(lang ?? "auto"); } diff --git a/apps/airiscode-cli/src/i18n/languages.ts b/apps/airiscode-cli/src/i18n/languages.ts index 733f11863..e28e0b309 100644 --- a/apps/airiscode-cli/src/i18n/languages.ts +++ b/apps/airiscode-cli/src/i18n/languages.ts @@ -4,14 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -export type SupportedLanguage = - | 'en' - | 'zh' - | 'ru' - | 'de' - | 'ja' - | 'pt' - | string; +export type SupportedLanguage = "en" | "zh" | "ru" | "de" | "ja" | "pt" | string; export interface LanguageDefinition { /** The internal locale code used by the i18n system (e.g., 'en', 'zh'). */ @@ -26,40 +19,40 @@ export interface LanguageDefinition { export const SUPPORTED_LANGUAGES: readonly LanguageDefinition[] = [ { - code: 'en', - id: 'en-US', - fullName: 'English', - nativeName: 'English', + code: "en", + id: "en-US", + fullName: "English", + nativeName: "English", }, { - code: 'zh', - id: 'zh-CN', - fullName: 'Chinese', - nativeName: '中文', + code: "zh", + id: "zh-CN", + fullName: "Chinese", + nativeName: "中文", }, { - code: 'ru', - id: 'ru-RU', - fullName: 'Russian', - nativeName: 'Русский', + code: "ru", + id: "ru-RU", + fullName: "Russian", + nativeName: "Русский", }, { - code: 'de', - id: 'de-DE', - fullName: 'German', - nativeName: 'Deutsch', + code: "de", + id: "de-DE", + fullName: "German", + nativeName: "Deutsch", }, { - code: 'ja', - id: 'ja-JP', - fullName: 'Japanese', - nativeName: '日本語', + code: "ja", + id: "ja-JP", + fullName: "Japanese", + nativeName: "日本語", }, { - code: 'pt', - id: 'pt-BR', - fullName: 'Portuguese', - nativeName: 'Português', + code: "pt", + id: "pt-BR", + fullName: "Portuguese", + nativeName: "Português", }, ]; @@ -69,7 +62,7 @@ export const SUPPORTED_LANGUAGES: readonly LanguageDefinition[] = [ */ export function getLanguageNameFromLocale(locale: SupportedLanguage): string { const lang = SUPPORTED_LANGUAGES.find((l) => l.code === locale); - return lang?.fullName || 'English'; + return lang?.fullName || "English"; } /** @@ -80,12 +73,10 @@ export function getLanguageSettingsOptions(): Array<{ label: string; }> { return [ - { value: 'auto', label: 'Auto (detect from system)' }, + { value: "auto", label: "Auto (detect from system)" }, ...SUPPORTED_LANGUAGES.map((l) => ({ value: l.code, - label: l.nativeName - ? `${l.nativeName} (${l.fullName})` - : `${l.fullName} (${l.id})`, + label: l.nativeName ? `${l.nativeName} (${l.fullName})` : `${l.fullName} (${l.id})`, })), ]; } @@ -93,6 +84,6 @@ export function getLanguageSettingsOptions(): Array<{ /** * Gets a string containing all supported language IDs (e.g., "en-US|zh-CN"). */ -export function getSupportedLanguageIds(separator = '|'): string { +export function getSupportedLanguageIds(separator = "|"): string { return SUPPORTED_LANGUAGES.map((l) => l.id).join(separator); } diff --git a/apps/airiscode-cli/src/i18n/locales/de.js b/apps/airiscode-cli/src/i18n/locales/de.js index 6d45ba4ea..97404bf17 100644 --- a/apps/airiscode-cli/src/i18n/locales/de.js +++ b/apps/airiscode-cli/src/i18n/locales/de.js @@ -12,1186 +12,1096 @@ export default { // Help / UI Components // ============================================================================ // Attachment hints - '↑ to manage attachments': '↑ Anhänge verwalten', - '← → select, Delete to remove, ↓ to exit': - '← → auswählen, Entf zum Löschen, ↓ beenden', - 'Attachments: ': 'Anhänge: ', + "↑ to manage attachments": "↑ Anhänge verwalten", + "← → select, Delete to remove, ↓ to exit": "← → auswählen, Entf zum Löschen, ↓ beenden", + "Attachments: ": "Anhänge: ", - 'Basics:': 'Grundlagen:', - 'Add context': 'Kontext hinzufügen', - 'Use {{symbol}} to specify files for context (e.g., {{example}}) to target specific files or folders.': - 'Verwenden Sie {{symbol}}, um Dateien als Kontext anzugeben (z.B. {{example}}), um bestimmte Dateien oder Ordner auszuwählen.', - '@': '@', - '@src/myFile.ts': '@src/myFile.ts', - 'Shell mode': 'Shell-Modus', - 'YOLO mode': 'YOLO-Modus', - 'plan mode': 'Planungsmodus', - 'auto-accept edits': 'Änderungen automatisch akzeptieren', - 'Accepting edits': 'Änderungen werden akzeptiert', - '(shift + tab to cycle)': '(Umschalt + Tab zum Wechseln)', - '(tab to cycle)': '(Tab zum Wechseln)', - 'Execute shell commands via {{symbol}} (e.g., {{example1}}) or use natural language (e.g., {{example2}}).': - 'Shell-Befehle über {{symbol}} ausführen (z.B. {{example1}}) oder natürliche Sprache verwenden (z.B. {{example2}}).', - '!': '!', - '!npm run start': '!npm run start', - 'start server': 'Server starten', - 'Commands:': 'Befehle:', - 'shell command': 'Shell-Befehl', - 'Model Context Protocol command (from external servers)': - 'Model Context Protocol Befehl (von externen Servern)', - 'Keyboard Shortcuts:': 'Tastenkürzel:', - 'Jump through words in the input': 'Wörter in der Eingabe überspringen', - 'Close dialogs, cancel requests, or quit application': - 'Dialoge schließen, Anfragen abbrechen oder Anwendung beenden', - 'New line': 'Neue Zeile', - 'New line (Alt+Enter works for certain linux distros)': - 'Neue Zeile (Alt+Enter funktioniert bei bestimmten Linux-Distributionen)', - 'Clear the screen': 'Bildschirm löschen', - 'Open input in external editor': 'Eingabe in externem Editor öffnen', - 'Send message': 'Nachricht senden', - 'Initializing...': 'Initialisierung...', - 'Connecting to MCP servers... ({{connected}}/{{total}})': - 'Verbindung zu MCP-Servern wird hergestellt... ({{connected}}/{{total}})', - 'Type your message or @path/to/file': - 'Nachricht eingeben oder @Pfad/zur/Datei', + "Basics:": "Grundlagen:", + "Add context": "Kontext hinzufügen", + "Use {{symbol}} to specify files for context (e.g., {{example}}) to target specific files or folders.": + "Verwenden Sie {{symbol}}, um Dateien als Kontext anzugeben (z.B. {{example}}), um bestimmte Dateien oder Ordner auszuwählen.", + "@": "@", + "@src/myFile.ts": "@src/myFile.ts", + "Shell mode": "Shell-Modus", + "YOLO mode": "YOLO-Modus", + "plan mode": "Planungsmodus", + "auto-accept edits": "Änderungen automatisch akzeptieren", + "Accepting edits": "Änderungen werden akzeptiert", + "(shift + tab to cycle)": "(Umschalt + Tab zum Wechseln)", + "(tab to cycle)": "(Tab zum Wechseln)", + "Execute shell commands via {{symbol}} (e.g., {{example1}}) or use natural language (e.g., {{example2}}).": + "Shell-Befehle über {{symbol}} ausführen (z.B. {{example1}}) oder natürliche Sprache verwenden (z.B. {{example2}}).", + "!": "!", + "!npm run start": "!npm run start", + "start server": "Server starten", + "Commands:": "Befehle:", + "shell command": "Shell-Befehl", + "Model Context Protocol command (from external servers)": + "Model Context Protocol Befehl (von externen Servern)", + "Keyboard Shortcuts:": "Tastenkürzel:", + "Jump through words in the input": "Wörter in der Eingabe überspringen", + "Close dialogs, cancel requests, or quit application": + "Dialoge schließen, Anfragen abbrechen oder Anwendung beenden", + "New line": "Neue Zeile", + "New line (Alt+Enter works for certain linux distros)": + "Neue Zeile (Alt+Enter funktioniert bei bestimmten Linux-Distributionen)", + "Clear the screen": "Bildschirm löschen", + "Open input in external editor": "Eingabe in externem Editor öffnen", + "Send message": "Nachricht senden", + "Initializing...": "Initialisierung...", + "Connecting to MCP servers... ({{connected}}/{{total}})": + "Verbindung zu MCP-Servern wird hergestellt... ({{connected}}/{{total}})", + "Type your message or @path/to/file": "Nachricht eingeben oder @Pfad/zur/Datei", "Press 'i' for INSERT mode and 'Esc' for NORMAL mode.": "Drücken Sie 'i' für den EINFÜGE-Modus und 'Esc' für den NORMAL-Modus.", - 'Cancel operation / Clear input (double press)': - 'Vorgang abbrechen / Eingabe löschen (doppelt drücken)', - 'Cycle approval modes': 'Genehmigungsmodi durchschalten', - 'Cycle through your prompt history': 'Eingabeverlauf durchblättern', - 'For a full list of shortcuts, see {{docPath}}': - 'Eine vollständige Liste der Tastenkürzel finden Sie unter {{docPath}}', - 'docs/keyboard-shortcuts.md': 'docs/keyboard-shortcuts.md', - 'for help on Qwen Code': 'für Hilfe zu Qwen Code', - 'show version info': 'Versionsinformationen anzeigen', - 'submit a bug report': 'Fehlerbericht einreichen', - 'About Qwen Code': 'Über Qwen Code', - Status: 'Status', + "Cancel operation / Clear input (double press)": + "Vorgang abbrechen / Eingabe löschen (doppelt drücken)", + "Cycle approval modes": "Genehmigungsmodi durchschalten", + "Cycle through your prompt history": "Eingabeverlauf durchblättern", + "For a full list of shortcuts, see {{docPath}}": + "Eine vollständige Liste der Tastenkürzel finden Sie unter {{docPath}}", + "docs/keyboard-shortcuts.md": "docs/keyboard-shortcuts.md", + "for help on Qwen Code": "für Hilfe zu Qwen Code", + "show version info": "Versionsinformationen anzeigen", + "submit a bug report": "Fehlerbericht einreichen", + "About Qwen Code": "Über Qwen Code", + Status: "Status", // ============================================================================ // System Information Fields // ============================================================================ - 'Qwen Code': 'Qwen Code', - Runtime: 'Laufzeit', - OS: 'Betriebssystem', - Auth: 'Authentifizierung', - 'CLI Version': 'CLI-Version', - 'Git Commit': 'Git-Commit', - Model: 'Modell', - 'Fast Model': 'Schnelles Modell', - Sandbox: 'Sandbox', - 'OS Platform': 'Betriebssystem', - 'OS Arch': 'OS-Architektur', - 'OS Release': 'OS-Version', - 'Node.js Version': 'Node.js-Version', - 'NPM Version': 'NPM-Version', - 'Session ID': 'Sitzungs-ID', - 'Auth Method': 'Authentifizierungsmethode', - 'Base URL': 'Basis-URL', - Proxy: 'Proxy', - 'Memory Usage': 'Speichernutzung', - 'IDE Client': 'IDE-Client', + "Qwen Code": "Qwen Code", + Runtime: "Laufzeit", + OS: "Betriebssystem", + Auth: "Authentifizierung", + "CLI Version": "CLI-Version", + "Git Commit": "Git-Commit", + Model: "Modell", + "Fast Model": "Schnelles Modell", + Sandbox: "Sandbox", + "OS Platform": "Betriebssystem", + "OS Arch": "OS-Architektur", + "OS Release": "OS-Version", + "Node.js Version": "Node.js-Version", + "NPM Version": "NPM-Version", + "Session ID": "Sitzungs-ID", + "Auth Method": "Authentifizierungsmethode", + "Base URL": "Basis-URL", + Proxy: "Proxy", + "Memory Usage": "Speichernutzung", + "IDE Client": "IDE-Client", // ============================================================================ // Commands - General // ============================================================================ - 'Analyzes the project and creates a tailored QWEN.md file.': - 'Analysiert das Projekt und erstellt eine maßgeschneiderte QWEN.md-Datei.', - 'List available Qwen Code tools. Usage: /tools [desc]': - 'Verfügbare Qwen Code Werkzeuge auflisten. Verwendung: /tools [desc]', - 'List available skills.': 'Verfügbare Skills auflisten.', - 'Available Qwen Code CLI tools:': 'Verfügbare Qwen Code CLI-Werkzeuge:', - 'No tools available': 'Keine Werkzeuge verfügbar', - 'View or change the approval mode for tool usage': - 'Genehmigungsmodus für Werkzeugnutzung anzeigen oder ändern', - 'View or change the language setting': - 'Spracheinstellung anzeigen oder ändern', - 'change the theme': 'Design ändern', - 'Select Theme': 'Design auswählen', - Preview: 'Vorschau', - '(Use Enter to select, Tab to configure scope)': - '(Enter zum Auswählen, Tab zum Konfigurieren des Bereichs)', - '(Use Enter to apply scope, Tab to go back)': - '(Enter zum Anwenden des Bereichs, Tab zum Zurückgehen)', - 'Theme configuration unavailable due to NO_COLOR env variable.': - 'Design-Konfiguration aufgrund der NO_COLOR-Umgebungsvariable nicht verfügbar.', + "Analyzes the project and creates a tailored QWEN.md file.": + "Analysiert das Projekt und erstellt eine maßgeschneiderte QWEN.md-Datei.", + "List available Qwen Code tools. Usage: /tools [desc]": + "Verfügbare Qwen Code Werkzeuge auflisten. Verwendung: /tools [desc]", + "List available skills.": "Verfügbare Skills auflisten.", + "Available Qwen Code CLI tools:": "Verfügbare Qwen Code CLI-Werkzeuge:", + "No tools available": "Keine Werkzeuge verfügbar", + "View or change the approval mode for tool usage": + "Genehmigungsmodus für Werkzeugnutzung anzeigen oder ändern", + "View or change the language setting": "Spracheinstellung anzeigen oder ändern", + "change the theme": "Design ändern", + "Select Theme": "Design auswählen", + Preview: "Vorschau", + "(Use Enter to select, Tab to configure scope)": + "(Enter zum Auswählen, Tab zum Konfigurieren des Bereichs)", + "(Use Enter to apply scope, Tab to go back)": + "(Enter zum Anwenden des Bereichs, Tab zum Zurückgehen)", + "Theme configuration unavailable due to NO_COLOR env variable.": + "Design-Konfiguration aufgrund der NO_COLOR-Umgebungsvariable nicht verfügbar.", 'Theme "{{themeName}}" not found.': 'Design "{{themeName}}" nicht gefunden.', 'Theme "{{themeName}}" not found in selected scope.': 'Design "{{themeName}}" im ausgewählten Bereich nicht gefunden.', - 'Clear conversation history and free up context': - 'Gesprächsverlauf löschen und Kontext freigeben', - 'Compresses the context by replacing it with a summary.': - 'Komprimiert den Kontext durch Ersetzen mit einer Zusammenfassung.', - 'open full Qwen Code documentation in your browser': - 'Vollständige Qwen Code Dokumentation im Browser öffnen', - 'Configuration not available.': 'Konfiguration nicht verfügbar.', - 'change the auth method': 'Authentifizierungsmethode ändern', - 'Configure authentication information for login': - 'Authentifizierungsinformationen für die Anmeldung konfigurieren', - 'Copy the last result or code snippet to clipboard': - 'Letztes Ergebnis oder Codeausschnitt in die Zwischenablage kopieren', + "Clear conversation history and free up context": + "Gesprächsverlauf löschen und Kontext freigeben", + "Compresses the context by replacing it with a summary.": + "Komprimiert den Kontext durch Ersetzen mit einer Zusammenfassung.", + "open full Qwen Code documentation in your browser": + "Vollständige Qwen Code Dokumentation im Browser öffnen", + "Configuration not available.": "Konfiguration nicht verfügbar.", + "change the auth method": "Authentifizierungsmethode ändern", + "Configure authentication information for login": + "Authentifizierungsinformationen für die Anmeldung konfigurieren", + "Copy the last result or code snippet to clipboard": + "Letztes Ergebnis oder Codeausschnitt in die Zwischenablage kopieren", // ============================================================================ // Commands - Agents // ============================================================================ - 'Manage subagents for specialized task delegation.': - 'Unteragenten für spezialisierte Aufgabendelegation verwalten.', - 'Manage existing subagents (view, edit, delete).': - 'Bestehende Unteragenten verwalten (anzeigen, bearbeiten, löschen).', - 'Create a new subagent with guided setup.': - 'Neuen Unteragenten mit geführter Einrichtung erstellen.', + "Manage subagents for specialized task delegation.": + "Unteragenten für spezialisierte Aufgabendelegation verwalten.", + "Manage existing subagents (view, edit, delete).": + "Bestehende Unteragenten verwalten (anzeigen, bearbeiten, löschen).", + "Create a new subagent with guided setup.": + "Neuen Unteragenten mit geführter Einrichtung erstellen.", // ============================================================================ // Agents - Management Dialog // ============================================================================ - Agents: 'Agenten', - 'Choose Action': 'Aktion wählen', - 'Edit {{name}}': '{{name}} bearbeiten', - 'Edit Tools: {{name}}': 'Werkzeuge bearbeiten: {{name}}', - 'Edit Color: {{name}}': 'Farbe bearbeiten: {{name}}', - 'Delete {{name}}': '{{name}} löschen', - 'Unknown Step': 'Unbekannter Schritt', - 'Esc to close': 'Esc zum Schließen', - 'Enter to select, ↑↓ to navigate, Esc to close': - 'Enter zum Auswählen, ↑↓ zum Navigieren, Esc zum Schließen', - 'Esc to go back': 'Esc zum Zurückgehen', - 'Enter to confirm, Esc to cancel': 'Enter zum Bestätigen, Esc zum Abbrechen', - 'Enter to select, ↑↓ to navigate, Esc to go back': - 'Enter zum Auswählen, ↑↓ zum Navigieren, Esc zum Zurückgehen', - 'Enter to submit, Esc to go back': 'Enter zum Absenden, Esc zum Zurückgehen', - 'Invalid step: {{step}}': 'Ungültiger Schritt: {{step}}', - 'No subagents found.': 'Keine Unteragenten gefunden.', + Agents: "Agenten", + "Choose Action": "Aktion wählen", + "Edit {{name}}": "{{name}} bearbeiten", + "Edit Tools: {{name}}": "Werkzeuge bearbeiten: {{name}}", + "Edit Color: {{name}}": "Farbe bearbeiten: {{name}}", + "Delete {{name}}": "{{name}} löschen", + "Unknown Step": "Unbekannter Schritt", + "Esc to close": "Esc zum Schließen", + "Enter to select, ↑↓ to navigate, Esc to close": + "Enter zum Auswählen, ↑↓ zum Navigieren, Esc zum Schließen", + "Esc to go back": "Esc zum Zurückgehen", + "Enter to confirm, Esc to cancel": "Enter zum Bestätigen, Esc zum Abbrechen", + "Enter to select, ↑↓ to navigate, Esc to go back": + "Enter zum Auswählen, ↑↓ zum Navigieren, Esc zum Zurückgehen", + "Enter to submit, Esc to go back": "Enter zum Absenden, Esc zum Zurückgehen", + "Invalid step: {{step}}": "Ungültiger Schritt: {{step}}", + "No subagents found.": "Keine Unteragenten gefunden.", "Use '/agents create' to create your first subagent.": "Verwenden Sie '/agents create', um Ihren ersten Unteragenten zu erstellen.", - '(built-in)': '(integriert)', - '(overridden by project level agent)': '(überschrieben durch Projektagent)', - 'Project Level ({{path}})': 'Projektebene ({{path}})', - 'User Level ({{path}})': 'Benutzerebene ({{path}})', - 'Built-in Agents': 'Integrierte Agenten', - 'Extension Agents': 'Erweiterungs-Agenten', - 'Using: {{count}} agents': 'Verwendet: {{count}} Agenten', - 'View Agent': 'Agent anzeigen', - 'Edit Agent': 'Agent bearbeiten', - 'Delete Agent': 'Agent löschen', - Back: 'Zurück', - 'No agent selected': 'Kein Agent ausgewählt', - 'File Path: ': 'Dateipfad: ', - 'Tools: ': 'Werkzeuge: ', - 'Color: ': 'Farbe: ', - 'Description:': 'Beschreibung:', - 'System Prompt:': 'System-Prompt:', - 'Open in editor': 'Im Editor öffnen', - 'Edit tools': 'Werkzeuge bearbeiten', - 'Edit color': 'Farbe bearbeiten', - '❌ Error:': '❌ Fehler:', + "(built-in)": "(integriert)", + "(overridden by project level agent)": "(überschrieben durch Projektagent)", + "Project Level ({{path}})": "Projektebene ({{path}})", + "User Level ({{path}})": "Benutzerebene ({{path}})", + "Built-in Agents": "Integrierte Agenten", + "Extension Agents": "Erweiterungs-Agenten", + "Using: {{count}} agents": "Verwendet: {{count}} Agenten", + "View Agent": "Agent anzeigen", + "Edit Agent": "Agent bearbeiten", + "Delete Agent": "Agent löschen", + Back: "Zurück", + "No agent selected": "Kein Agent ausgewählt", + "File Path: ": "Dateipfad: ", + "Tools: ": "Werkzeuge: ", + "Color: ": "Farbe: ", + "Description:": "Beschreibung:", + "System Prompt:": "System-Prompt:", + "Open in editor": "Im Editor öffnen", + "Edit tools": "Werkzeuge bearbeiten", + "Edit color": "Farbe bearbeiten", + "❌ Error:": "❌ Fehler:", 'Are you sure you want to delete agent "{{name}}"?': 'Sind Sie sicher, dass Sie den Agenten "{{name}}" löschen möchten?', // ============================================================================ // Agents - Creation Wizard // ============================================================================ - 'Project Level (.qwen/agents/)': 'Projektebene (.qwen/agents/)', - 'User Level (~/.qwen/agents/)': 'Benutzerebene (~/.qwen/agents/)', - '✅ Subagent Created Successfully!': '✅ Unteragent erfolgreich erstellt!', + "Project Level (.qwen/agents/)": "Projektebene (.qwen/agents/)", + "User Level (~/.qwen/agents/)": "Benutzerebene (~/.qwen/agents/)", + "✅ Subagent Created Successfully!": "✅ Unteragent erfolgreich erstellt!", 'Subagent "{{name}}" has been saved to {{level}} level.': 'Unteragent "{{name}}" wurde auf {{level}}-Ebene gespeichert.', - 'Name: ': 'Name: ', - 'Location: ': 'Speicherort: ', - '❌ Error saving subagent:': '❌ Fehler beim Speichern des Unteragenten:', - 'Warnings:': 'Warnungen:', + "Name: ": "Name: ", + "Location: ": "Speicherort: ", + "❌ Error saving subagent:": "❌ Fehler beim Speichern des Unteragenten:", + "Warnings:": "Warnungen:", 'Name "{{name}}" already exists at {{level}} level - will overwrite existing subagent': 'Name "{{name}}" existiert bereits auf {{level}}-Ebene - bestehender Unteragent wird überschrieben', 'Name "{{name}}" exists at user level - project level will take precedence': 'Name "{{name}}" existiert auf Benutzerebene - Projektebene hat Vorrang', 'Name "{{name}}" exists at project level - existing subagent will take precedence': 'Name "{{name}}" existiert auf Projektebene - bestehender Unteragent hat Vorrang', - 'Description is over {{length}} characters': - 'Beschreibung ist über {{length}} Zeichen', - 'System prompt is over {{length}} characters': - 'System-Prompt ist über {{length}} Zeichen', + "Description is over {{length}} characters": "Beschreibung ist über {{length}} Zeichen", + "System prompt is over {{length}} characters": "System-Prompt ist über {{length}} Zeichen", // Agents - Creation Wizard Steps - 'Step {{n}}: Choose Location': 'Schritt {{n}}: Speicherort wählen', - 'Step {{n}}: Choose Generation Method': - 'Schritt {{n}}: Generierungsmethode wählen', - 'Generate with Qwen Code (Recommended)': - 'Mit Qwen Code generieren (Empfohlen)', - 'Manual Creation': 'Manuelle Erstellung', - 'Describe what this subagent should do and when it should be used. (Be comprehensive for best results)': - 'Beschreiben Sie, was dieser Unteragent tun soll und wann er verwendet werden soll. (Ausführliche Beschreibung für beste Ergebnisse)', - 'e.g., Expert code reviewer that reviews code based on best practices...': - 'z.B. Experte für Code-Reviews, der Code nach Best Practices überprüft...', - 'Generating subagent configuration...': - 'Unteragent-Konfiguration wird generiert...', - 'Failed to generate subagent: {{error}}': - 'Fehler beim Generieren des Unteragenten: {{error}}', - 'Step {{n}}: Describe Your Subagent': 'Schritt {{n}}: Unteragent beschreiben', - 'Step {{n}}: Enter Subagent Name': 'Schritt {{n}}: Unteragent-Name eingeben', - 'Step {{n}}: Enter System Prompt': 'Schritt {{n}}: System-Prompt eingeben', - 'Step {{n}}: Enter Description': 'Schritt {{n}}: Beschreibung eingeben', + "Step {{n}}: Choose Location": "Schritt {{n}}: Speicherort wählen", + "Step {{n}}: Choose Generation Method": "Schritt {{n}}: Generierungsmethode wählen", + "Generate with Qwen Code (Recommended)": "Mit Qwen Code generieren (Empfohlen)", + "Manual Creation": "Manuelle Erstellung", + "Describe what this subagent should do and when it should be used. (Be comprehensive for best results)": + "Beschreiben Sie, was dieser Unteragent tun soll und wann er verwendet werden soll. (Ausführliche Beschreibung für beste Ergebnisse)", + "e.g., Expert code reviewer that reviews code based on best practices...": + "z.B. Experte für Code-Reviews, der Code nach Best Practices überprüft...", + "Generating subagent configuration...": "Unteragent-Konfiguration wird generiert...", + "Failed to generate subagent: {{error}}": "Fehler beim Generieren des Unteragenten: {{error}}", + "Step {{n}}: Describe Your Subagent": "Schritt {{n}}: Unteragent beschreiben", + "Step {{n}}: Enter Subagent Name": "Schritt {{n}}: Unteragent-Name eingeben", + "Step {{n}}: Enter System Prompt": "Schritt {{n}}: System-Prompt eingeben", + "Step {{n}}: Enter Description": "Schritt {{n}}: Beschreibung eingeben", // Agents - Tool Selection - 'Step {{n}}: Select Tools': 'Schritt {{n}}: Werkzeuge auswählen', - 'All Tools (Default)': 'Alle Werkzeuge (Standard)', - 'All Tools': 'Alle Werkzeuge', - 'Read-only Tools': 'Nur-Lese-Werkzeuge', - 'Read & Edit Tools': 'Lese- und Bearbeitungswerkzeuge', - 'Read & Edit & Execution Tools': - 'Lese-, Bearbeitungs- und Ausführungswerkzeuge', - 'All tools selected, including MCP tools': - 'Alle Werkzeuge ausgewählt, einschließlich MCP-Werkzeuge', - 'Selected tools:': 'Ausgewählte Werkzeuge:', - 'Read-only tools:': 'Nur-Lese-Werkzeuge:', - 'Edit tools:': 'Bearbeitungswerkzeuge:', - 'Execution tools:': 'Ausführungswerkzeuge:', - 'Step {{n}}: Choose Background Color': - 'Schritt {{n}}: Hintergrundfarbe wählen', - 'Step {{n}}: Confirm and Save': 'Schritt {{n}}: Bestätigen und Speichern', + "Step {{n}}: Select Tools": "Schritt {{n}}: Werkzeuge auswählen", + "All Tools (Default)": "Alle Werkzeuge (Standard)", + "All Tools": "Alle Werkzeuge", + "Read-only Tools": "Nur-Lese-Werkzeuge", + "Read & Edit Tools": "Lese- und Bearbeitungswerkzeuge", + "Read & Edit & Execution Tools": "Lese-, Bearbeitungs- und Ausführungswerkzeuge", + "All tools selected, including MCP tools": + "Alle Werkzeuge ausgewählt, einschließlich MCP-Werkzeuge", + "Selected tools:": "Ausgewählte Werkzeuge:", + "Read-only tools:": "Nur-Lese-Werkzeuge:", + "Edit tools:": "Bearbeitungswerkzeuge:", + "Execution tools:": "Ausführungswerkzeuge:", + "Step {{n}}: Choose Background Color": "Schritt {{n}}: Hintergrundfarbe wählen", + "Step {{n}}: Confirm and Save": "Schritt {{n}}: Bestätigen und Speichern", // Agents - Navigation & Instructions - 'Esc to cancel': 'Esc zum Abbrechen', - 'Press Enter to save, e to save and edit, Esc to go back': - 'Enter zum Speichern, e zum Speichern und Bearbeiten, Esc zum Zurückgehen', - 'Press Enter to continue, {{navigation}}Esc to {{action}}': - 'Enter zum Fortfahren, {{navigation}}Esc zum {{action}}', - cancel: 'Abbrechen', - 'go back': 'Zurückgehen', - '↑↓ to navigate, ': '↑↓ zum Navigieren, ', - 'Enter a clear, unique name for this subagent.': - 'Geben Sie einen eindeutigen Namen für diesen Unteragenten ein.', - 'e.g., Code Reviewer': 'z.B. Code-Reviewer', - 'Name cannot be empty.': 'Name darf nicht leer sein.', + "Esc to cancel": "Esc zum Abbrechen", + "Press Enter to save, e to save and edit, Esc to go back": + "Enter zum Speichern, e zum Speichern und Bearbeiten, Esc zum Zurückgehen", + "Press Enter to continue, {{navigation}}Esc to {{action}}": + "Enter zum Fortfahren, {{navigation}}Esc zum {{action}}", + cancel: "Abbrechen", + "go back": "Zurückgehen", + "↑↓ to navigate, ": "↑↓ zum Navigieren, ", + "Enter a clear, unique name for this subagent.": + "Geben Sie einen eindeutigen Namen für diesen Unteragenten ein.", + "e.g., Code Reviewer": "z.B. Code-Reviewer", + "Name cannot be empty.": "Name darf nicht leer sein.", "Write the system prompt that defines this subagent's behavior. Be comprehensive for best results.": - 'Schreiben Sie den System-Prompt, der das Verhalten dieses Unteragenten definiert. Ausführlich für beste Ergebnisse.', - 'e.g., You are an expert code reviewer...': - 'z.B. Sie sind ein Experte für Code-Reviews...', - 'System prompt cannot be empty.': 'System-Prompt darf nicht leer sein.', - 'Describe when and how this subagent should be used.': - 'Beschreiben Sie, wann und wie dieser Unteragent verwendet werden soll.', - 'e.g., Reviews code for best practices and potential bugs.': - 'z.B. Überprüft Code auf Best Practices und mögliche Fehler.', - 'Description cannot be empty.': 'Beschreibung darf nicht leer sein.', - 'Failed to launch editor: {{error}}': - 'Fehler beim Starten des Editors: {{error}}', - 'Failed to save and edit subagent: {{error}}': - 'Fehler beim Speichern und Bearbeiten des Unteragenten: {{error}}', + "Schreiben Sie den System-Prompt, der das Verhalten dieses Unteragenten definiert. Ausführlich für beste Ergebnisse.", + "e.g., You are an expert code reviewer...": "z.B. Sie sind ein Experte für Code-Reviews...", + "System prompt cannot be empty.": "System-Prompt darf nicht leer sein.", + "Describe when and how this subagent should be used.": + "Beschreiben Sie, wann und wie dieser Unteragent verwendet werden soll.", + "e.g., Reviews code for best practices and potential bugs.": + "z.B. Überprüft Code auf Best Practices und mögliche Fehler.", + "Description cannot be empty.": "Beschreibung darf nicht leer sein.", + "Failed to launch editor: {{error}}": "Fehler beim Starten des Editors: {{error}}", + "Failed to save and edit subagent: {{error}}": + "Fehler beim Speichern und Bearbeiten des Unteragenten: {{error}}", // ============================================================================ // Commands - General (continued) // ============================================================================ - 'View and edit Qwen Code settings': - 'Qwen Code Einstellungen anzeigen und bearbeiten', - Settings: 'Einstellungen', - 'To see changes, Qwen Code must be restarted. Press r to exit and apply changes now.': - 'Um Änderungen zu sehen, muss Qwen Code neu gestartet werden. Drücken Sie r, um jetzt zu beenden und Änderungen anzuwenden.', + "View and edit Qwen Code settings": "Qwen Code Einstellungen anzeigen und bearbeiten", + Settings: "Einstellungen", + "To see changes, Qwen Code must be restarted. Press r to exit and apply changes now.": + "Um Änderungen zu sehen, muss Qwen Code neu gestartet werden. Drücken Sie r, um jetzt zu beenden und Änderungen anzuwenden.", 'The command "/{{command}}" is not supported in non-interactive mode.': 'Der Befehl "/{{command}}" wird im nicht-interaktiven Modus nicht unterstützt.', // ============================================================================ // Settings Labels // ============================================================================ - 'Vim Mode': 'Vim-Modus', - 'Disable Auto Update': 'Automatische Updates deaktivieren', - 'Attribution: commit': 'Attribution: Commit', - 'Terminal Bell Notification': 'Terminal-Signalton', - 'Enable Usage Statistics': 'Nutzungsstatistiken aktivieren', - Theme: 'Farbschema', - 'Preferred Editor': 'Bevorzugter Editor', - 'Auto-connect to IDE': 'Automatische Verbindung zur IDE', - 'Enable Prompt Completion': 'Eingabevervollständigung aktivieren', - 'Debug Keystroke Logging': 'Debug-Protokollierung von Tastatureingaben', - 'Language: UI': 'Sprache: Benutzeroberfläche', - 'Language: Model': 'Sprache: Modell', - 'Output Format': 'Ausgabeformat', - 'Hide Window Title': 'Fenstertitel ausblenden', - 'Show Status in Title': 'Status im Titel anzeigen', - 'Hide Tips': 'Tipps ausblenden', - 'Show Line Numbers in Code': 'Zeilennummern im Code anzeigen', - 'Show Citations': 'Quellenangaben anzeigen', - 'Custom Witty Phrases': 'Benutzerdefinierte Witzige Sprüche', - 'Show Welcome Back Dialog': 'Willkommen-zurück-Dialog anzeigen', - 'Enable User Feedback': 'Benutzerfeedback aktivieren', - 'How is Qwen doing this session? (optional)': - 'Wie macht sich Qwen in dieser Sitzung? (optional)', - Bad: 'Schlecht', - Fine: 'In Ordnung', - Good: 'Gut', - Dismiss: 'Ignorieren', - 'Not Sure Yet': 'Noch nicht sicher', - 'Any other key': 'Beliebige andere Taste', - 'Disable Loading Phrases': 'Ladesprüche deaktivieren', - 'Screen Reader Mode': 'Bildschirmleser-Modus', - 'IDE Mode': 'IDE-Modus', - 'Max Session Turns': 'Maximale Sitzungsrunden', - 'Skip Next Speaker Check': 'Nächste-Sprecher-Prüfung überspringen', - 'Skip Loop Detection': 'Schleifenerkennung überspringen', - 'Skip Startup Context': 'Startkontext überspringen', - 'Enable OpenAI Logging': 'OpenAI-Protokollierung aktivieren', - 'OpenAI Logging Directory': 'OpenAI-Protokollierungsverzeichnis', - Timeout: 'Zeitlimit', - 'Max Retries': 'Maximale Wiederholungen', - 'Disable Cache Control': 'Cache-Steuerung deaktivieren', - 'Memory Discovery Max Dirs': 'Maximale Verzeichnisse für Speichererkennung', - 'Load Memory From Include Directories': - 'Speicher aus Include-Verzeichnissen laden', - 'Respect .gitignore': '.gitignore beachten', - 'Respect .qwenignore': '.qwenignore beachten', - 'Enable Recursive File Search': 'Rekursive Dateisuche aktivieren', - 'Disable Fuzzy Search': 'Unscharfe Suche deaktivieren', - 'Interactive Shell (PTY)': 'Interaktive Shell (PTY)', - 'Show Color': 'Farbe anzeigen', - 'Auto Accept': 'Automatisch akzeptieren', - 'Use Ripgrep': 'Ripgrep verwenden', - 'Use Builtin Ripgrep': 'Integriertes Ripgrep verwenden', - 'Enable Tool Output Truncation': 'Werkzeugausgabe-Kürzung aktivieren', - 'Tool Output Truncation Threshold': - 'Schwellenwert für Werkzeugausgabe-Kürzung', - 'Tool Output Truncation Lines': 'Zeilen für Werkzeugausgabe-Kürzung', - 'Folder Trust': 'Ordnervertrauen', - 'Vision Model Preview': 'Vision-Modell-Vorschau', - 'Tool Schema Compliance': 'Werkzeug-Schema-Konformität', + "Vim Mode": "Vim-Modus", + "Disable Auto Update": "Automatische Updates deaktivieren", + "Attribution: commit": "Attribution: Commit", + "Terminal Bell Notification": "Terminal-Signalton", + "Enable Usage Statistics": "Nutzungsstatistiken aktivieren", + Theme: "Farbschema", + "Preferred Editor": "Bevorzugter Editor", + "Auto-connect to IDE": "Automatische Verbindung zur IDE", + "Enable Prompt Completion": "Eingabevervollständigung aktivieren", + "Debug Keystroke Logging": "Debug-Protokollierung von Tastatureingaben", + "Language: UI": "Sprache: Benutzeroberfläche", + "Language: Model": "Sprache: Modell", + "Output Format": "Ausgabeformat", + "Hide Window Title": "Fenstertitel ausblenden", + "Show Status in Title": "Status im Titel anzeigen", + "Hide Tips": "Tipps ausblenden", + "Show Line Numbers in Code": "Zeilennummern im Code anzeigen", + "Show Citations": "Quellenangaben anzeigen", + "Custom Witty Phrases": "Benutzerdefinierte Witzige Sprüche", + "Show Welcome Back Dialog": "Willkommen-zurück-Dialog anzeigen", + "Enable User Feedback": "Benutzerfeedback aktivieren", + "How is Qwen doing this session? (optional)": "Wie macht sich Qwen in dieser Sitzung? (optional)", + Bad: "Schlecht", + Fine: "In Ordnung", + Good: "Gut", + Dismiss: "Ignorieren", + "Not Sure Yet": "Noch nicht sicher", + "Any other key": "Beliebige andere Taste", + "Disable Loading Phrases": "Ladesprüche deaktivieren", + "Screen Reader Mode": "Bildschirmleser-Modus", + "IDE Mode": "IDE-Modus", + "Max Session Turns": "Maximale Sitzungsrunden", + "Skip Next Speaker Check": "Nächste-Sprecher-Prüfung überspringen", + "Skip Loop Detection": "Schleifenerkennung überspringen", + "Skip Startup Context": "Startkontext überspringen", + "Enable OpenAI Logging": "OpenAI-Protokollierung aktivieren", + "OpenAI Logging Directory": "OpenAI-Protokollierungsverzeichnis", + Timeout: "Zeitlimit", + "Max Retries": "Maximale Wiederholungen", + "Disable Cache Control": "Cache-Steuerung deaktivieren", + "Memory Discovery Max Dirs": "Maximale Verzeichnisse für Speichererkennung", + "Load Memory From Include Directories": "Speicher aus Include-Verzeichnissen laden", + "Respect .gitignore": ".gitignore beachten", + "Respect .qwenignore": ".qwenignore beachten", + "Enable Recursive File Search": "Rekursive Dateisuche aktivieren", + "Disable Fuzzy Search": "Unscharfe Suche deaktivieren", + "Interactive Shell (PTY)": "Interaktive Shell (PTY)", + "Show Color": "Farbe anzeigen", + "Auto Accept": "Automatisch akzeptieren", + "Use Ripgrep": "Ripgrep verwenden", + "Use Builtin Ripgrep": "Integriertes Ripgrep verwenden", + "Enable Tool Output Truncation": "Werkzeugausgabe-Kürzung aktivieren", + "Tool Output Truncation Threshold": "Schwellenwert für Werkzeugausgabe-Kürzung", + "Tool Output Truncation Lines": "Zeilen für Werkzeugausgabe-Kürzung", + "Folder Trust": "Ordnervertrauen", + "Vision Model Preview": "Vision-Modell-Vorschau", + "Tool Schema Compliance": "Werkzeug-Schema-Konformität", // Settings enum options - 'Auto (detect from system)': 'Automatisch (vom System erkennen)', - Text: 'Text', - JSON: 'JSON', - Plan: 'Plan', - Default: 'Standard', - 'Auto Edit': 'Automatisch bearbeiten', - YOLO: 'YOLO', - 'toggle vim mode on/off': 'Vim-Modus ein-/ausschalten', - 'check session stats. Usage: /stats [model|tools]': - 'Sitzungsstatistiken prüfen. Verwendung: /stats [model|tools]', - 'Show model-specific usage statistics.': - 'Modellspezifische Nutzungsstatistiken anzeigen.', - 'Show tool-specific usage statistics.': - 'Werkzeugspezifische Nutzungsstatistiken anzeigen.', - 'exit the cli': 'CLI beenden', - 'Open MCP management dialog, or authenticate with OAuth-enabled servers': - 'MCP-Verwaltungsdialog öffnen oder mit OAuth-fähigem Server authentifizieren', - 'List configured MCP servers and tools, or authenticate with OAuth-enabled servers': - 'Konfigurierte MCP-Server und Werkzeuge auflisten oder mit OAuth-fähigen Servern authentifizieren', - 'Manage workspace directories': 'Arbeitsbereichsverzeichnisse verwalten', - 'Add directories to the workspace. Use comma to separate multiple paths': - 'Verzeichnisse zum Arbeitsbereich hinzufügen. Komma zum Trennen mehrerer Pfade verwenden', - 'Show all directories in the workspace': - 'Alle Verzeichnisse im Arbeitsbereich anzeigen', - 'set external editor preference': 'Externen Editor festlegen', - 'Select Editor': 'Editor auswählen', - 'Editor Preference': 'Editor-Einstellung', - 'These editors are currently supported. Please note that some editors cannot be used in sandbox mode.': - 'Diese Editoren werden derzeit unterstützt. Bitte beachten Sie, dass einige Editoren nicht im Sandbox-Modus verwendet werden können.', - 'Your preferred editor is:': 'Ihr bevorzugter Editor ist:', - 'Manage extensions': 'Erweiterungen verwalten', - 'Manage installed extensions': 'Installierte Erweiterungen verwalten', - 'List active extensions': 'Aktive Erweiterungen auflisten', - 'Update extensions. Usage: update |--all': - 'Erweiterungen aktualisieren. Verwendung: update |--all', - 'Disable an extension': 'Erweiterung deaktivieren', - 'Enable an extension': 'Erweiterung aktivieren', - 'Install an extension from a git repo or local path': - 'Erweiterung aus Git-Repository oder lokalem Pfad installieren', - 'Uninstall an extension': 'Erweiterung deinstallieren', - 'No extensions installed.': 'Keine Erweiterungen installiert.', - 'Usage: /extensions update |--all': - 'Verwendung: /extensions update |--all', + "Auto (detect from system)": "Automatisch (vom System erkennen)", + Text: "Text", + JSON: "JSON", + Plan: "Plan", + Default: "Standard", + "Auto Edit": "Automatisch bearbeiten", + YOLO: "YOLO", + "toggle vim mode on/off": "Vim-Modus ein-/ausschalten", + "check session stats. Usage: /stats [model|tools]": + "Sitzungsstatistiken prüfen. Verwendung: /stats [model|tools]", + "Show model-specific usage statistics.": "Modellspezifische Nutzungsstatistiken anzeigen.", + "Show tool-specific usage statistics.": "Werkzeugspezifische Nutzungsstatistiken anzeigen.", + "exit the cli": "CLI beenden", + "Open MCP management dialog, or authenticate with OAuth-enabled servers": + "MCP-Verwaltungsdialog öffnen oder mit OAuth-fähigem Server authentifizieren", + "List configured MCP servers and tools, or authenticate with OAuth-enabled servers": + "Konfigurierte MCP-Server und Werkzeuge auflisten oder mit OAuth-fähigen Servern authentifizieren", + "Manage workspace directories": "Arbeitsbereichsverzeichnisse verwalten", + "Add directories to the workspace. Use comma to separate multiple paths": + "Verzeichnisse zum Arbeitsbereich hinzufügen. Komma zum Trennen mehrerer Pfade verwenden", + "Show all directories in the workspace": "Alle Verzeichnisse im Arbeitsbereich anzeigen", + "set external editor preference": "Externen Editor festlegen", + "Select Editor": "Editor auswählen", + "Editor Preference": "Editor-Einstellung", + "These editors are currently supported. Please note that some editors cannot be used in sandbox mode.": + "Diese Editoren werden derzeit unterstützt. Bitte beachten Sie, dass einige Editoren nicht im Sandbox-Modus verwendet werden können.", + "Your preferred editor is:": "Ihr bevorzugter Editor ist:", + "Manage extensions": "Erweiterungen verwalten", + "Manage installed extensions": "Installierte Erweiterungen verwalten", + "List active extensions": "Aktive Erweiterungen auflisten", + "Update extensions. Usage: update |--all": + "Erweiterungen aktualisieren. Verwendung: update |--all", + "Disable an extension": "Erweiterung deaktivieren", + "Enable an extension": "Erweiterung aktivieren", + "Install an extension from a git repo or local path": + "Erweiterung aus Git-Repository oder lokalem Pfad installieren", + "Uninstall an extension": "Erweiterung deinstallieren", + "No extensions installed.": "Keine Erweiterungen installiert.", + "Usage: /extensions update |--all": + "Verwendung: /extensions update |--all", 'Extension "{{name}}" not found.': 'Erweiterung "{{name}}" nicht gefunden.', - 'No extensions to update.': 'Keine Erweiterungen zum Aktualisieren.', - 'Usage: /extensions install ': - 'Verwendung: /extensions install ', - 'Installing extension from "{{source}}"...': - 'Installiere Erweiterung von "{{source}}"...', - 'Extension "{{name}}" installed successfully.': - 'Erweiterung "{{name}}" erfolgreich installiert.', + "No extensions to update.": "Keine Erweiterungen zum Aktualisieren.", + "Usage: /extensions install ": "Verwendung: /extensions install ", + 'Installing extension from "{{source}}"...': 'Installiere Erweiterung von "{{source}}"...', + 'Extension "{{name}}" installed successfully.': 'Erweiterung "{{name}}" erfolgreich installiert.', 'Failed to install extension from "{{source}}": {{error}}': 'Fehler beim Installieren der Erweiterung von "{{source}}": {{error}}', - 'Usage: /extensions uninstall ': - 'Verwendung: /extensions uninstall ', - 'Uninstalling extension "{{name}}"...': - 'Deinstalliere Erweiterung "{{name}}"...', + "Usage: /extensions uninstall ": + "Verwendung: /extensions uninstall ", + 'Uninstalling extension "{{name}}"...': 'Deinstalliere Erweiterung "{{name}}"...', 'Extension "{{name}}" uninstalled successfully.': 'Erweiterung "{{name}}" erfolgreich deinstalliert.', 'Failed to uninstall extension "{{name}}": {{error}}': 'Fehler beim Deinstallieren der Erweiterung "{{name}}": {{error}}', - 'Usage: /extensions {{command}} [--scope=]': - 'Verwendung: /extensions {{command}} [--scope=]', + "Usage: /extensions {{command}} [--scope=]": + "Verwendung: /extensions {{command}} [--scope=]", 'Unsupported scope "{{scope}}", should be one of "user" or "workspace"': 'Nicht unterstützter Bereich "{{scope}}", sollte "user" oder "workspace" sein', 'Extension "{{name}}" disabled for scope "{{scope}}"': 'Erweiterung "{{name}}" für Bereich "{{scope}}" deaktiviert', 'Extension "{{name}}" enabled for scope "{{scope}}"': 'Erweiterung "{{name}}" für Bereich "{{scope}}" aktiviert', - 'Do you want to continue? [Y/n]: ': 'Möchten Sie fortfahren? [Y/n]: ', - 'Do you want to continue?': 'Möchten Sie fortfahren?', - 'Installing extension "{{name}}".': - 'Erweiterung "{{name}}" wird installiert.', - '**Extensions may introduce unexpected behavior. Ensure you have investigated the extension source and trust the author.**': - '**Erweiterungen können unerwartetes Verhalten verursachen. Stellen Sie sicher, dass Sie die Erweiterungsquelle untersucht haben und dem Autor vertrauen.**', - 'This extension will run the following MCP servers:': - 'Diese Erweiterung wird folgende MCP-Server ausführen:', - local: 'lokal', - remote: 'remote', - 'This extension will add the following commands: {{commands}}.': - 'Diese Erweiterung wird folgende Befehle hinzufügen: {{commands}}.', - 'This extension will append info to your QWEN.md context using {{fileName}}': - 'Diese Erweiterung wird Informationen zu Ihrem QWEN.md-Kontext mit {{fileName}} hinzufügen', - 'This extension will exclude the following core tools: {{tools}}': - 'Diese Erweiterung wird folgende Kernwerkzeuge ausschließen: {{tools}}', - 'This extension will install the following skills:': - 'Diese Erweiterung wird folgende Fähigkeiten installieren:', - 'This extension will install the following subagents:': - 'Diese Erweiterung wird folgende Unteragenten installieren:', - 'Installation cancelled for "{{name}}".': - 'Installation von "{{name}}" abgebrochen.', - 'You are installing an extension from {{originSource}}. Some features may not work perfectly with Qwen Code.': - 'Sie installieren eine Erweiterung von {{originSource}}. Einige Funktionen funktionieren möglicherweise nicht perfekt mit Qwen Code.', - '--ref and --auto-update are not applicable for marketplace extensions.': - '--ref und --auto-update sind nicht anwendbar für Marketplace-Erweiterungen.', + "Do you want to continue? [Y/n]: ": "Möchten Sie fortfahren? [Y/n]: ", + "Do you want to continue?": "Möchten Sie fortfahren?", + 'Installing extension "{{name}}".': 'Erweiterung "{{name}}" wird installiert.', + "**Extensions may introduce unexpected behavior. Ensure you have investigated the extension source and trust the author.**": + "**Erweiterungen können unerwartetes Verhalten verursachen. Stellen Sie sicher, dass Sie die Erweiterungsquelle untersucht haben und dem Autor vertrauen.**", + "This extension will run the following MCP servers:": + "Diese Erweiterung wird folgende MCP-Server ausführen:", + local: "lokal", + remote: "remote", + "This extension will add the following commands: {{commands}}.": + "Diese Erweiterung wird folgende Befehle hinzufügen: {{commands}}.", + "This extension will append info to your QWEN.md context using {{fileName}}": + "Diese Erweiterung wird Informationen zu Ihrem QWEN.md-Kontext mit {{fileName}} hinzufügen", + "This extension will exclude the following core tools: {{tools}}": + "Diese Erweiterung wird folgende Kernwerkzeuge ausschließen: {{tools}}", + "This extension will install the following skills:": + "Diese Erweiterung wird folgende Fähigkeiten installieren:", + "This extension will install the following subagents:": + "Diese Erweiterung wird folgende Unteragenten installieren:", + 'Installation cancelled for "{{name}}".': 'Installation von "{{name}}" abgebrochen.', + "You are installing an extension from {{originSource}}. Some features may not work perfectly with Qwen Code.": + "Sie installieren eine Erweiterung von {{originSource}}. Einige Funktionen funktionieren möglicherweise nicht perfekt mit Qwen Code.", + "--ref and --auto-update are not applicable for marketplace extensions.": + "--ref und --auto-update sind nicht anwendbar für Marketplace-Erweiterungen.", 'Extension "{{name}}" installed successfully and enabled.': 'Erweiterung "{{name}}" erfolgreich installiert und aktiviert.', - 'Installs an extension from a git repository URL, local path, or claude marketplace (marketplace-url:plugin-name).': - 'Installiert eine Erweiterung von einer Git-Repository-URL, einem lokalen Pfad oder dem Claude-Marketplace (marketplace-url:plugin-name).', - 'The github URL, local path, or marketplace source (marketplace-url:plugin-name) of the extension to install.': - 'Die GitHub-URL, der lokale Pfad oder die Marketplace-Quelle (marketplace-url:plugin-name) der zu installierenden Erweiterung.', - 'The git ref to install from.': 'Die Git-Referenz für die Installation.', - 'Enable auto-update for this extension.': - 'Automatisches Update für diese Erweiterung aktivieren.', - 'Enable pre-release versions for this extension.': - 'Pre-Release-Versionen für diese Erweiterung aktivieren.', - 'Acknowledge the security risks of installing an extension and skip the confirmation prompt.': - 'Sicherheitsrisiken der Erweiterungsinstallation bestätigen und Bestätigungsaufforderung überspringen.', - 'The source argument must be provided.': - 'Das Quellargument muss angegeben werden.', + "Installs an extension from a git repository URL, local path, or claude marketplace (marketplace-url:plugin-name).": + "Installiert eine Erweiterung von einer Git-Repository-URL, einem lokalen Pfad oder dem Claude-Marketplace (marketplace-url:plugin-name).", + "The github URL, local path, or marketplace source (marketplace-url:plugin-name) of the extension to install.": + "Die GitHub-URL, der lokale Pfad oder die Marketplace-Quelle (marketplace-url:plugin-name) der zu installierenden Erweiterung.", + "The git ref to install from.": "Die Git-Referenz für die Installation.", + "Enable auto-update for this extension.": + "Automatisches Update für diese Erweiterung aktivieren.", + "Enable pre-release versions for this extension.": + "Pre-Release-Versionen für diese Erweiterung aktivieren.", + "Acknowledge the security risks of installing an extension and skip the confirmation prompt.": + "Sicherheitsrisiken der Erweiterungsinstallation bestätigen und Bestätigungsaufforderung überspringen.", + "The source argument must be provided.": "Das Quellargument muss angegeben werden.", 'Extension "{{name}}" successfully uninstalled.': 'Erweiterung "{{name}}" erfolgreich deinstalliert.', - 'Uninstalls an extension.': 'Deinstalliert eine Erweiterung.', - 'The name or source path of the extension to uninstall.': - 'Der Name oder Quellpfad der zu deinstallierenden Erweiterung.', - 'Please include the name of the extension to uninstall as a positional argument.': - 'Bitte geben Sie den Namen der zu deinstallierenden Erweiterung als Positionsargument an.', - 'Enables an extension.': 'Aktiviert eine Erweiterung.', - 'The name of the extension to enable.': - 'Der Name der zu aktivierenden Erweiterung.', - 'The scope to enable the extenison in. If not set, will be enabled in all scopes.': - 'Der Bereich, in dem die Erweiterung aktiviert werden soll. Wenn nicht gesetzt, wird sie in allen Bereichen aktiviert.', + "Uninstalls an extension.": "Deinstalliert eine Erweiterung.", + "The name or source path of the extension to uninstall.": + "Der Name oder Quellpfad der zu deinstallierenden Erweiterung.", + "Please include the name of the extension to uninstall as a positional argument.": + "Bitte geben Sie den Namen der zu deinstallierenden Erweiterung als Positionsargument an.", + "Enables an extension.": "Aktiviert eine Erweiterung.", + "The name of the extension to enable.": "Der Name der zu aktivierenden Erweiterung.", + "The scope to enable the extenison in. If not set, will be enabled in all scopes.": + "Der Bereich, in dem die Erweiterung aktiviert werden soll. Wenn nicht gesetzt, wird sie in allen Bereichen aktiviert.", 'Extension "{{name}}" successfully enabled for scope "{{scope}}".': 'Erweiterung "{{name}}" erfolgreich für Bereich "{{scope}}" aktiviert.', 'Extension "{{name}}" successfully enabled in all scopes.': 'Erweiterung "{{name}}" erfolgreich in allen Bereichen aktiviert.', - 'Invalid scope: {{scope}}. Please use one of {{scopes}}.': - 'Ungültiger Bereich: {{scope}}. Bitte verwenden Sie einen von {{scopes}}.', - 'Disables an extension.': 'Deaktiviert eine Erweiterung.', - 'The name of the extension to disable.': - 'Der Name der zu deaktivierenden Erweiterung.', - 'The scope to disable the extenison in.': - 'Der Bereich, in dem die Erweiterung deaktiviert werden soll.', + "Invalid scope: {{scope}}. Please use one of {{scopes}}.": + "Ungültiger Bereich: {{scope}}. Bitte verwenden Sie einen von {{scopes}}.", + "Disables an extension.": "Deaktiviert eine Erweiterung.", + "The name of the extension to disable.": "Der Name der zu deaktivierenden Erweiterung.", + "The scope to disable the extenison in.": + "Der Bereich, in dem die Erweiterung deaktiviert werden soll.", 'Extension "{{name}}" successfully disabled for scope "{{scope}}".': 'Erweiterung "{{name}}" erfolgreich für Bereich "{{scope}}" deaktiviert.', 'Extension "{{name}}" successfully updated: {{oldVersion}} → {{newVersion}}.': 'Erweiterung "{{name}}" erfolgreich aktualisiert: {{oldVersion}} → {{newVersion}}.', 'Unable to install extension "{{name}}" due to missing install metadata': 'Erweiterung "{{name}}" kann aufgrund fehlender Installationsmetadaten nicht installiert werden', - 'Extension "{{name}}" is already up to date.': - 'Erweiterung "{{name}}" ist bereits aktuell.', - 'Updates all extensions or a named extension to the latest version.': - 'Aktualisiert alle Erweiterungen oder eine benannte Erweiterung auf die neueste Version.', - 'The name of the extension to update.': - 'Der Name der zu aktualisierenden Erweiterung.', - 'Update all extensions.': 'Alle Erweiterungen aktualisieren.', - 'Either an extension name or --all must be provided': - 'Entweder ein Erweiterungsname oder --all muss angegeben werden', - 'Lists installed extensions.': 'Listet installierte Erweiterungen auf.', - 'Path:': 'Pfad:', - 'Source:': 'Quelle:', - 'Type:': 'Typ:', - 'Ref:': 'Ref:', - 'Release tag:': 'Release-Tag:', - 'Enabled (User):': 'Aktiviert (Benutzer):', - 'Enabled (Workspace):': 'Aktiviert (Arbeitsbereich):', - 'Context files:': 'Kontextdateien:', - 'Skills:': 'Skills:', - 'Agents:': 'Agents:', - 'MCP servers:': 'MCP-Server:', - 'Link extension failed to install.': - 'Verknüpfte Erweiterung konnte nicht installiert werden.', + 'Extension "{{name}}" is already up to date.': 'Erweiterung "{{name}}" ist bereits aktuell.', + "Updates all extensions or a named extension to the latest version.": + "Aktualisiert alle Erweiterungen oder eine benannte Erweiterung auf die neueste Version.", + "The name of the extension to update.": "Der Name der zu aktualisierenden Erweiterung.", + "Update all extensions.": "Alle Erweiterungen aktualisieren.", + "Either an extension name or --all must be provided": + "Entweder ein Erweiterungsname oder --all muss angegeben werden", + "Lists installed extensions.": "Listet installierte Erweiterungen auf.", + "Path:": "Pfad:", + "Source:": "Quelle:", + "Type:": "Typ:", + "Ref:": "Ref:", + "Release tag:": "Release-Tag:", + "Enabled (User):": "Aktiviert (Benutzer):", + "Enabled (Workspace):": "Aktiviert (Arbeitsbereich):", + "Context files:": "Kontextdateien:", + "Skills:": "Skills:", + "Agents:": "Agents:", + "MCP servers:": "MCP-Server:", + "Link extension failed to install.": "Verknüpfte Erweiterung konnte nicht installiert werden.", 'Extension "{{name}}" linked successfully and enabled.': 'Erweiterung "{{name}}" erfolgreich verknüpft und aktiviert.', - 'Links an extension from a local path. Updates made to the local path will always be reflected.': - 'Verknüpft eine Erweiterung von einem lokalen Pfad. Änderungen am lokalen Pfad werden immer widergespiegelt.', - 'The name of the extension to link.': - 'Der Name der zu verknüpfenden Erweiterung.', - 'Set a specific setting for an extension.': - 'Legt eine bestimmte Einstellung für eine Erweiterung fest.', - 'Name of the extension to configure.': - 'Name der zu konfigurierenden Erweiterung.', - 'The setting to configure (name or env var).': - 'Die zu konfigurierende Einstellung (Name oder Umgebungsvariable).', - 'The scope to set the setting in.': - 'Der Bereich, in dem die Einstellung gesetzt werden soll.', - 'List all settings for an extension.': - 'Listet alle Einstellungen einer Erweiterung auf.', - 'Name of the extension.': 'Name der Erweiterung.', + "Links an extension from a local path. Updates made to the local path will always be reflected.": + "Verknüpft eine Erweiterung von einem lokalen Pfad. Änderungen am lokalen Pfad werden immer widergespiegelt.", + "The name of the extension to link.": "Der Name der zu verknüpfenden Erweiterung.", + "Set a specific setting for an extension.": + "Legt eine bestimmte Einstellung für eine Erweiterung fest.", + "Name of the extension to configure.": "Name der zu konfigurierenden Erweiterung.", + "The setting to configure (name or env var).": + "Die zu konfigurierende Einstellung (Name oder Umgebungsvariable).", + "The scope to set the setting in.": "Der Bereich, in dem die Einstellung gesetzt werden soll.", + "List all settings for an extension.": "Listet alle Einstellungen einer Erweiterung auf.", + "Name of the extension.": "Name der Erweiterung.", 'Extension "{{name}}" has no settings to configure.': 'Erweiterung "{{name}}" hat keine zu konfigurierenden Einstellungen.', 'Settings for "{{name}}":': 'Einstellungen für "{{name}}":', - '(workspace)': '(Arbeitsbereich)', - '(user)': '(Benutzer)', - '[not set]': '[nicht gesetzt]', - '[value stored in keychain]': '[Wert in Schlüsselbund gespeichert]', - 'Manage extension settings.': 'Erweiterungseinstellungen verwalten.', - 'You need to specify a command (set or list).': - 'Sie müssen einen Befehl angeben (set oder list).', + "(workspace)": "(Arbeitsbereich)", + "(user)": "(Benutzer)", + "[not set]": "[nicht gesetzt]", + "[value stored in keychain]": "[Wert in Schlüsselbund gespeichert]", + "Manage extension settings.": "Erweiterungseinstellungen verwalten.", + "You need to specify a command (set or list).": + "Sie müssen einen Befehl angeben (set oder list).", // ============================================================================ // Plugin Choice / Marketplace // ============================================================================ - 'No plugins available in this marketplace.': - 'In diesem Marktplatz sind keine Plugins verfügbar.', + "No plugins available in this marketplace.": "In diesem Marktplatz sind keine Plugins verfügbar.", 'Select a plugin to install from marketplace "{{name}}":': 'Wählen Sie ein Plugin zur Installation aus Marktplatz "{{name}}":', - 'Plugin selection cancelled.': 'Plugin-Auswahl abgebrochen.', + "Plugin selection cancelled.": "Plugin-Auswahl abgebrochen.", 'Select a plugin from "{{name}}"': 'Plugin aus "{{name}}" auswählen', - 'Use ↑↓ or j/k to navigate, Enter to select, Escape to cancel': - 'Verwenden Sie ↑↓ oder j/k zum Navigieren, Enter zum Auswählen, Escape zum Abbrechen', - '{{count}} more above': '{{count}} weitere oben', - '{{count}} more below': '{{count}} weitere unten', - 'manage IDE integration': 'IDE-Integration verwalten', - 'check status of IDE integration': 'Status der IDE-Integration prüfen', - 'install required IDE companion for {{ideName}}': - 'Erforderlichen IDE-Begleiter für {{ideName}} installieren', - 'enable IDE integration': 'IDE-Integration aktivieren', - 'disable IDE integration': 'IDE-Integration deaktivieren', - '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.': - 'IDE-Integration wird in Ihrer aktuellen Umgebung nicht unterstützt. Um diese Funktion zu nutzen, führen Sie Qwen Code in einer dieser unterstützten IDEs aus: VS Code oder VS Code-Forks.', - 'Set up GitHub Actions': 'GitHub Actions einrichten', - 'Configure terminal keybindings for multiline input (VS Code, Cursor, Windsurf, Trae)': - 'Terminal-Tastenbelegungen für mehrzeilige Eingabe konfigurieren (VS Code, Cursor, Windsurf, Trae)', - 'Please restart your terminal for the changes to take effect.': - 'Bitte starten Sie Ihr Terminal neu, damit die Änderungen wirksam werden.', - 'Failed to configure terminal: {{error}}': - 'Fehler beim Konfigurieren des Terminals: {{error}}', - 'Could not determine {{terminalName}} config path on Windows: APPDATA environment variable is not set.': - 'Konnte {{terminalName}}-Konfigurationspfad unter Windows nicht ermitteln: APPDATA-Umgebungsvariable ist nicht gesetzt.', - '{{terminalName}} keybindings.json exists but is not a valid JSON array. Please fix the file manually or delete it to allow automatic configuration.': - '{{terminalName}} keybindings.json existiert, ist aber kein gültiges JSON-Array. Bitte korrigieren Sie die Datei manuell oder löschen Sie sie, um automatische Konfiguration zu ermöglichen.', - 'File: {{file}}': 'Datei: {{file}}', - 'Failed to parse {{terminalName}} keybindings.json. The file contains invalid JSON. Please fix the file manually or delete it to allow automatic configuration.': - 'Fehler beim Parsen von {{terminalName}} keybindings.json. Die Datei enthält ungültiges JSON. Bitte korrigieren Sie die Datei manuell oder löschen Sie sie, um automatische Konfiguration zu ermöglichen.', - 'Error: {{error}}': 'Fehler: {{error}}', - 'Shift+Enter binding already exists': - 'Umschalt+Enter-Belegung existiert bereits', - 'Ctrl+Enter binding already exists': 'Strg+Enter-Belegung existiert bereits', - 'Existing keybindings detected. Will not modify to avoid conflicts.': - 'Bestehende Tastenbelegungen erkannt. Keine Änderungen, um Konflikte zu vermeiden.', - 'Please check and modify manually if needed: {{file}}': - 'Bitte prüfen und bei Bedarf manuell ändern: {{file}}', - 'Added Shift+Enter and Ctrl+Enter keybindings to {{terminalName}}.': - 'Umschalt+Enter und Strg+Enter Tastenbelegungen zu {{terminalName}} hinzugefügt.', - 'Modified: {{file}}': 'Geändert: {{file}}', - '{{terminalName}} keybindings already configured.': - '{{terminalName}}-Tastenbelegungen bereits konfiguriert.', - 'Failed to configure {{terminalName}}.': - 'Fehler beim Konfigurieren von {{terminalName}}.', - 'Your terminal is already configured for an optimal experience with multiline input (Shift+Enter and Ctrl+Enter).': - 'Ihr Terminal ist bereits für optimale Erfahrung mit mehrzeiliger Eingabe konfiguriert (Umschalt+Enter und Strg+Enter).', + "Use ↑↓ or j/k to navigate, Enter to select, Escape to cancel": + "Verwenden Sie ↑↓ oder j/k zum Navigieren, Enter zum Auswählen, Escape zum Abbrechen", + "{{count}} more above": "{{count}} weitere oben", + "{{count}} more below": "{{count}} weitere unten", + "manage IDE integration": "IDE-Integration verwalten", + "check status of IDE integration": "Status der IDE-Integration prüfen", + "install required IDE companion for {{ideName}}": + "Erforderlichen IDE-Begleiter für {{ideName}} installieren", + "enable IDE integration": "IDE-Integration aktivieren", + "disable IDE integration": "IDE-Integration deaktivieren", + "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.": + "IDE-Integration wird in Ihrer aktuellen Umgebung nicht unterstützt. Um diese Funktion zu nutzen, führen Sie Qwen Code in einer dieser unterstützten IDEs aus: VS Code oder VS Code-Forks.", + "Set up GitHub Actions": "GitHub Actions einrichten", + "Configure terminal keybindings for multiline input (VS Code, Cursor, Windsurf, Trae)": + "Terminal-Tastenbelegungen für mehrzeilige Eingabe konfigurieren (VS Code, Cursor, Windsurf, Trae)", + "Please restart your terminal for the changes to take effect.": + "Bitte starten Sie Ihr Terminal neu, damit die Änderungen wirksam werden.", + "Failed to configure terminal: {{error}}": "Fehler beim Konfigurieren des Terminals: {{error}}", + "Could not determine {{terminalName}} config path on Windows: APPDATA environment variable is not set.": + "Konnte {{terminalName}}-Konfigurationspfad unter Windows nicht ermitteln: APPDATA-Umgebungsvariable ist nicht gesetzt.", + "{{terminalName}} keybindings.json exists but is not a valid JSON array. Please fix the file manually or delete it to allow automatic configuration.": + "{{terminalName}} keybindings.json existiert, ist aber kein gültiges JSON-Array. Bitte korrigieren Sie die Datei manuell oder löschen Sie sie, um automatische Konfiguration zu ermöglichen.", + "File: {{file}}": "Datei: {{file}}", + "Failed to parse {{terminalName}} keybindings.json. The file contains invalid JSON. Please fix the file manually or delete it to allow automatic configuration.": + "Fehler beim Parsen von {{terminalName}} keybindings.json. Die Datei enthält ungültiges JSON. Bitte korrigieren Sie die Datei manuell oder löschen Sie sie, um automatische Konfiguration zu ermöglichen.", + "Error: {{error}}": "Fehler: {{error}}", + "Shift+Enter binding already exists": "Umschalt+Enter-Belegung existiert bereits", + "Ctrl+Enter binding already exists": "Strg+Enter-Belegung existiert bereits", + "Existing keybindings detected. Will not modify to avoid conflicts.": + "Bestehende Tastenbelegungen erkannt. Keine Änderungen, um Konflikte zu vermeiden.", + "Please check and modify manually if needed: {{file}}": + "Bitte prüfen und bei Bedarf manuell ändern: {{file}}", + "Added Shift+Enter and Ctrl+Enter keybindings to {{terminalName}}.": + "Umschalt+Enter und Strg+Enter Tastenbelegungen zu {{terminalName}} hinzugefügt.", + "Modified: {{file}}": "Geändert: {{file}}", + "{{terminalName}} keybindings already configured.": + "{{terminalName}}-Tastenbelegungen bereits konfiguriert.", + "Failed to configure {{terminalName}}.": "Fehler beim Konfigurieren von {{terminalName}}.", + "Your terminal is already configured for an optimal experience with multiline input (Shift+Enter and Ctrl+Enter).": + "Ihr Terminal ist bereits für optimale Erfahrung mit mehrzeiliger Eingabe konfiguriert (Umschalt+Enter und Strg+Enter).", // ============================================================================ // Commands - Hooks // ============================================================================ - 'Manage Qwen Code hooks': 'Qwen Code-Hooks verwalten', - 'List all configured hooks': 'Alle konfigurierten Hooks auflisten', - 'Enable a disabled hook': 'Einen deaktivierten Hook aktivieren', - 'Disable an active hook': 'Einen aktiven Hook deaktivieren', + "Manage Qwen Code hooks": "Qwen Code-Hooks verwalten", + "List all configured hooks": "Alle konfigurierten Hooks auflisten", + "Enable a disabled hook": "Einen deaktivierten Hook aktivieren", + "Disable an active hook": "Einen aktiven Hook deaktivieren", // Hooks - Dialog - Hooks: 'Hooks', - 'Loading hooks...': 'Hooks werden geladen...', - 'Error loading hooks:': 'Fehler beim Laden der Hooks:', - 'Press Escape to close': 'Escape zum Schließen drücken', - 'Press Escape, Ctrl+C, or Ctrl+D to cancel': - 'Escape, Ctrl+C oder Ctrl+D zum Abbrechen', - 'Press Space, Enter, or Escape to dismiss': - 'Leertaste, Enter oder Escape zum Schließen', - 'No hook selected': 'Kein Hook ausgewählt', + Hooks: "Hooks", + "Loading hooks...": "Hooks werden geladen...", + "Error loading hooks:": "Fehler beim Laden der Hooks:", + "Press Escape to close": "Escape zum Schließen drücken", + "Press Escape, Ctrl+C, or Ctrl+D to cancel": "Escape, Ctrl+C oder Ctrl+D zum Abbrechen", + "Press Space, Enter, or Escape to dismiss": "Leertaste, Enter oder Escape zum Schließen", + "No hook selected": "Kein Hook ausgewählt", // Hooks - List Step - 'No hook events found.': 'Keine Hook-Ereignisse gefunden.', - '{{count}} hook configured': '{{count}} Hook konfiguriert', - '{{count}} hooks configured': '{{count}} Hooks konfiguriert', - 'This menu is read-only. To add or modify hooks, edit settings.json directly or ask Qwen Code.': - 'Dieses Menü ist schreibgeschützt. Um Hooks hinzuzufügen oder zu ändern, bearbeiten Sie settings.json direkt oder fragen Sie Qwen Code.', - 'Enter to select · Esc to cancel': 'Enter zum Auswählen · Esc zum Abbrechen', + "No hook events found.": "Keine Hook-Ereignisse gefunden.", + "{{count}} hook configured": "{{count}} Hook konfiguriert", + "{{count}} hooks configured": "{{count}} Hooks konfiguriert", + "This menu is read-only. To add or modify hooks, edit settings.json directly or ask Qwen Code.": + "Dieses Menü ist schreibgeschützt. Um Hooks hinzuzufügen oder zu ändern, bearbeiten Sie settings.json direkt oder fragen Sie Qwen Code.", + "Enter to select · Esc to cancel": "Enter zum Auswählen · Esc zum Abbrechen", // Hooks - Detail Step - 'Exit codes:': 'Exit-Codes:', - 'Configured hooks:': 'Konfigurierte Hooks:', - 'No hooks configured for this event.': - 'Für dieses Ereignis sind keine Hooks konfiguriert.', - 'To add hooks, edit settings.json directly or ask Qwen.': - 'Um Hooks hinzuzufügen, bearbeiten Sie settings.json direkt oder fragen Sie Qwen.', - 'Enter to select · Esc to go back': 'Enter zum Auswählen · Esc zum Zurück', + "Exit codes:": "Exit-Codes:", + "Configured hooks:": "Konfigurierte Hooks:", + "No hooks configured for this event.": "Für dieses Ereignis sind keine Hooks konfiguriert.", + "To add hooks, edit settings.json directly or ask Qwen.": + "Um Hooks hinzuzufügen, bearbeiten Sie settings.json direkt oder fragen Sie Qwen.", + "Enter to select · Esc to go back": "Enter zum Auswählen · Esc zum Zurück", // Hooks - Config Detail Step - 'Hook details': 'Hook-Details', - 'Event:': 'Ereignis:', - 'Extension:': 'Erweiterung:', - 'Desc:': 'Beschreibung:', - 'No hook config selected': 'Keine Hook-Konfiguration ausgewählt', - 'To modify or remove this hook, edit settings.json directly or ask Qwen to help.': - 'Um diesen Hook zu ändern oder zu entfernen, bearbeiten Sie settings.json direkt oder fragen Sie Qwen.', + "Hook details": "Hook-Details", + "Event:": "Ereignis:", + "Extension:": "Erweiterung:", + "Desc:": "Beschreibung:", + "No hook config selected": "Keine Hook-Konfiguration ausgewählt", + "To modify or remove this hook, edit settings.json directly or ask Qwen to help.": + "Um diesen Hook zu ändern oder zu entfernen, bearbeiten Sie settings.json direkt oder fragen Sie Qwen.", // Hooks - Disabled Step - 'Hook Configuration - Disabled': 'Hook-Konfiguration - Deaktiviert', - 'All hooks are currently disabled. You have {{count}} that are not running.': - 'Alle Hooks sind derzeit deaktiviert. Sie haben {{count}} die nicht ausgeführt werden.', - '{{count}} configured hook': '{{count}} konfigurierter Hook', - '{{count}} configured hooks': '{{count}} konfigurierte Hooks', - 'When hooks are disabled:': 'Wenn Hooks deaktiviert sind:', - 'No hook commands will execute': 'Keine Hook-Befehle werden ausgeführt', - 'StatusLine will not be displayed': 'StatusLine wird nicht angezeigt', - 'Tool operations will proceed without hook validation': - 'Tool-Operationen werden ohne Hook-Validierung fortgesetzt', + "Hook Configuration - Disabled": "Hook-Konfiguration - Deaktiviert", + "All hooks are currently disabled. You have {{count}} that are not running.": + "Alle Hooks sind derzeit deaktiviert. Sie haben {{count}} die nicht ausgeführt werden.", + "{{count}} configured hook": "{{count}} konfigurierter Hook", + "{{count}} configured hooks": "{{count}} konfigurierte Hooks", + "When hooks are disabled:": "Wenn Hooks deaktiviert sind:", + "No hook commands will execute": "Keine Hook-Befehle werden ausgeführt", + "StatusLine will not be displayed": "StatusLine wird nicht angezeigt", + "Tool operations will proceed without hook validation": + "Tool-Operationen werden ohne Hook-Validierung fortgesetzt", 'To re-enable hooks, remove "disableAllHooks" from settings.json or ask Qwen Code.': 'Um Hooks wieder zu aktivieren, entfernen Sie "disableAllHooks" aus settings.json oder fragen Sie Qwen Code.', // Hooks - Source - Project: 'Projekt', - User: 'Benutzer', - System: 'System', - Extension: 'Erweiterung', - 'Local Settings': 'Lokale Einstellungen', - 'User Settings': 'Benutzereinstellungen', - 'System Settings': 'Systemeinstellungen', - Extensions: 'Erweiterungen', + Project: "Projekt", + User: "Benutzer", + System: "System", + Extension: "Erweiterung", + "Local Settings": "Lokale Einstellungen", + "User Settings": "Benutzereinstellungen", + "System Settings": "Systemeinstellungen", + Extensions: "Erweiterungen", // Hooks - Status - '✓ Enabled': '✓ Aktiviert', - '✗ Disabled': '✗ Deaktiviert', + "✓ Enabled": "✓ Aktiviert", + "✗ Disabled": "✗ Deaktiviert", // Hooks - Event Descriptions (short) - 'Before tool execution': 'Vor der Tool-Ausführung', - 'After tool execution': 'Nach der Tool-Ausführung', - 'After tool execution fails': 'Wenn die Tool-Ausführung fehlschlägt', - 'When notifications are sent': 'Wenn Benachrichtigungen gesendet werden', - 'When the user submits a prompt': 'Wenn der Benutzer einen Prompt absendet', - 'When a new session is started': 'Wenn eine neue Sitzung gestartet wird', - 'Right before Qwen Code concludes its response': - 'Direkt bevor Qwen Code seine Antwort abschließt', - 'When a subagent (Agent tool call) is started': - 'Wenn ein Subagent (Agent-Tool-Aufruf) gestartet wird', - 'Right before a subagent concludes its response': - 'Direkt bevor ein Subagent seine Antwort abschließt', - 'Before conversation compaction': 'Vor der Gesprächskomprimierung', - 'When a session is ending': 'Wenn eine Sitzung endet', - 'When a permission dialog is displayed': - 'Wenn ein Berechtigungsdialog angezeigt wird', + "Before tool execution": "Vor der Tool-Ausführung", + "After tool execution": "Nach der Tool-Ausführung", + "After tool execution fails": "Wenn die Tool-Ausführung fehlschlägt", + "When notifications are sent": "Wenn Benachrichtigungen gesendet werden", + "When the user submits a prompt": "Wenn der Benutzer einen Prompt absendet", + "When a new session is started": "Wenn eine neue Sitzung gestartet wird", + "Right before Qwen Code concludes its response": + "Direkt bevor Qwen Code seine Antwort abschließt", + "When a subagent (Agent tool call) is started": + "Wenn ein Subagent (Agent-Tool-Aufruf) gestartet wird", + "Right before a subagent concludes its response": + "Direkt bevor ein Subagent seine Antwort abschließt", + "Before conversation compaction": "Vor der Gesprächskomprimierung", + "When a session is ending": "Wenn eine Sitzung endet", + "When a permission dialog is displayed": "Wenn ein Berechtigungsdialog angezeigt wird", // Hooks - Event Descriptions (detailed) - 'Input to command is JSON of tool call arguments.': - 'Die Eingabe an den Befehl ist JSON der Tool-Aufruf-Argumente.', + "Input to command is JSON of tool call arguments.": + "Die Eingabe an den Befehl ist JSON der Tool-Aufruf-Argumente.", 'Input to command is JSON with fields "inputs" (tool call arguments) and "response" (tool call response).': 'Die Eingabe an den Befehl ist JSON mit den Feldern "inputs" (Tool-Aufruf-Argumente) und "response" (Tool-Aufruf-Antwort).', - 'Input to command is JSON with tool_name, tool_input, tool_use_id, error, error_type, is_interrupt, and is_timeout.': - 'Die Eingabe an den Befehl ist JSON mit tool_name, tool_input, tool_use_id, error, error_type, is_interrupt und is_timeout.', - 'Input to command is JSON with notification message and type.': - 'Die Eingabe an den Befehl ist JSON mit Benachrichtigungsnachricht und -typ.', - 'Input to command is JSON with original user prompt text.': - 'Die Eingabe an den Befehl ist JSON mit dem ursprünglichen Benutzer-Prompt-Text.', - 'Input to command is JSON with session start source.': - 'Die Eingabe an den Befehl ist JSON mit der Sitzungsstart-Quelle.', - 'Input to command is JSON with session end reason.': - 'Die Eingabe an den Befehl ist JSON mit dem Sitzungsende-Grund.', - 'Input to command is JSON with agent_id and agent_type.': - 'Die Eingabe an den Befehl ist JSON mit agent_id und agent_type.', - 'Input to command is JSON with agent_id, agent_type, and agent_transcript_path.': - 'Die Eingabe an den Befehl ist JSON mit agent_id, agent_type und agent_transcript_path.', - 'Input to command is JSON with compaction details.': - 'Die Eingabe an den Befehl ist JSON mit Komprimierungsdetails.', - 'Input to command is JSON with tool_name, tool_input, and tool_use_id. Output JSON with hookSpecificOutput containing decision to allow or deny.': - 'Die Eingabe an den Befehl ist JSON mit tool_name, tool_input und tool_use_id. Ausgabe ist JSON mit hookSpecificOutput, das die Entscheidung zum Zulassen oder Ablehnen enthält.', + "Input to command is JSON with tool_name, tool_input, tool_use_id, error, error_type, is_interrupt, and is_timeout.": + "Die Eingabe an den Befehl ist JSON mit tool_name, tool_input, tool_use_id, error, error_type, is_interrupt und is_timeout.", + "Input to command is JSON with notification message and type.": + "Die Eingabe an den Befehl ist JSON mit Benachrichtigungsnachricht und -typ.", + "Input to command is JSON with original user prompt text.": + "Die Eingabe an den Befehl ist JSON mit dem ursprünglichen Benutzer-Prompt-Text.", + "Input to command is JSON with session start source.": + "Die Eingabe an den Befehl ist JSON mit der Sitzungsstart-Quelle.", + "Input to command is JSON with session end reason.": + "Die Eingabe an den Befehl ist JSON mit dem Sitzungsende-Grund.", + "Input to command is JSON with agent_id and agent_type.": + "Die Eingabe an den Befehl ist JSON mit agent_id und agent_type.", + "Input to command is JSON with agent_id, agent_type, and agent_transcript_path.": + "Die Eingabe an den Befehl ist JSON mit agent_id, agent_type und agent_transcript_path.", + "Input to command is JSON with compaction details.": + "Die Eingabe an den Befehl ist JSON mit Komprimierungsdetails.", + "Input to command is JSON with tool_name, tool_input, and tool_use_id. Output JSON with hookSpecificOutput containing decision to allow or deny.": + "Die Eingabe an den Befehl ist JSON mit tool_name, tool_input und tool_use_id. Ausgabe ist JSON mit hookSpecificOutput, das die Entscheidung zum Zulassen oder Ablehnen enthält.", // Hooks - Exit Code Descriptions - 'stdout/stderr not shown': 'stdout/stderr nicht angezeigt', - 'show stderr to model and continue conversation': - 'stderr dem Modell anzeigen und Konversation fortsetzen', - 'show stderr to user only': 'stderr nur dem Benutzer anzeigen', - 'stdout shown in transcript mode (ctrl+o)': - 'stdout im Transkriptmodus angezeigt (ctrl+o)', - 'show stderr to model immediately': 'stderr sofort dem Modell anzeigen', - 'show stderr to user only but continue with tool call': - 'stderr nur dem Benutzer anzeigen, aber mit Tool-Aufruf fortfahren', - 'block processing, erase original prompt, and show stderr to user only': - 'Verarbeitung blockieren, ursprünglichen Prompt löschen und stderr nur dem Benutzer anzeigen', - 'stdout shown to Qwen': 'stdout dem Qwen anzeigen', - 'show stderr to user only (blocking errors ignored)': - 'stderr nur dem Benutzer anzeigen (Blockierungsfehler ignoriert)', - 'command completes successfully': 'Befehl erfolgreich abgeschlossen', - 'stdout shown to subagent': 'stdout dem Subagenten anzeigen', - 'show stderr to subagent and continue having it run': - 'stderr dem Subagenten anzeigen und ihn weiterlaufen lassen', - 'stdout appended as custom compact instructions': - 'stdout als benutzerdefinierte Komprimierungsanweisungen angehängt', - 'block compaction': 'Komprimierung blockieren', - 'show stderr to user only but continue with compaction': - 'stderr nur dem Benutzer anzeigen, aber mit Komprimierung fortfahren', - 'use hook decision if provided': - 'Hook-Entscheidung verwenden, falls bereitgestellt', + "stdout/stderr not shown": "stdout/stderr nicht angezeigt", + "show stderr to model and continue conversation": + "stderr dem Modell anzeigen und Konversation fortsetzen", + "show stderr to user only": "stderr nur dem Benutzer anzeigen", + "stdout shown in transcript mode (ctrl+o)": "stdout im Transkriptmodus angezeigt (ctrl+o)", + "show stderr to model immediately": "stderr sofort dem Modell anzeigen", + "show stderr to user only but continue with tool call": + "stderr nur dem Benutzer anzeigen, aber mit Tool-Aufruf fortfahren", + "block processing, erase original prompt, and show stderr to user only": + "Verarbeitung blockieren, ursprünglichen Prompt löschen und stderr nur dem Benutzer anzeigen", + "stdout shown to Qwen": "stdout dem Qwen anzeigen", + "show stderr to user only (blocking errors ignored)": + "stderr nur dem Benutzer anzeigen (Blockierungsfehler ignoriert)", + "command completes successfully": "Befehl erfolgreich abgeschlossen", + "stdout shown to subagent": "stdout dem Subagenten anzeigen", + "show stderr to subagent and continue having it run": + "stderr dem Subagenten anzeigen und ihn weiterlaufen lassen", + "stdout appended as custom compact instructions": + "stdout als benutzerdefinierte Komprimierungsanweisungen angehängt", + "block compaction": "Komprimierung blockieren", + "show stderr to user only but continue with compaction": + "stderr nur dem Benutzer anzeigen, aber mit Komprimierung fortfahren", + "use hook decision if provided": "Hook-Entscheidung verwenden, falls bereitgestellt", // Hooks - Messages - 'Config not loaded.': 'Konfiguration nicht geladen.', - 'Hooks are not enabled. Enable hooks in settings to use this feature.': - 'Hooks sind nicht aktiviert. Aktivieren Sie Hooks in den Einstellungen, um diese Funktion zu nutzen.', - 'No hooks configured. Add hooks in your settings.json file.': - 'Keine Hooks konfiguriert. Fügen Sie Hooks in Ihrer settings.json-Datei hinzu.', - 'Configured Hooks ({{count}} total)': - 'Konfigurierte Hooks ({{count}} insgesamt)', + "Config not loaded.": "Konfiguration nicht geladen.", + "Hooks are not enabled. Enable hooks in settings to use this feature.": + "Hooks sind nicht aktiviert. Aktivieren Sie Hooks in den Einstellungen, um diese Funktion zu nutzen.", + "No hooks configured. Add hooks in your settings.json file.": + "Keine Hooks konfiguriert. Fügen Sie Hooks in Ihrer settings.json-Datei hinzu.", + "Configured Hooks ({{count}} total)": "Konfigurierte Hooks ({{count}} insgesamt)", // ============================================================================ // Commands - Session Export // ============================================================================ - 'Export current session message history to a file': - 'Den Nachrichtenverlauf der aktuellen Sitzung in eine Datei exportieren', - 'Export session to HTML format': 'Sitzung in das HTML-Format exportieren', - 'Export session to JSON format': 'Sitzung in das JSON-Format exportieren', - 'Export session to JSONL format (one message per line)': - 'Sitzung in das JSONL-Format exportieren (eine Nachricht pro Zeile)', - 'Export session to markdown format': - 'Sitzung in das Markdown-Format exportieren', + "Export current session message history to a file": + "Den Nachrichtenverlauf der aktuellen Sitzung in eine Datei exportieren", + "Export session to HTML format": "Sitzung in das HTML-Format exportieren", + "Export session to JSON format": "Sitzung in das JSON-Format exportieren", + "Export session to JSONL format (one message per line)": + "Sitzung in das JSONL-Format exportieren (eine Nachricht pro Zeile)", + "Export session to markdown format": "Sitzung in das Markdown-Format exportieren", // ============================================================================ // Commands - Insights // ============================================================================ - 'generate personalized programming insights from your chat history': - 'Personalisierte Programmier-Einblicke aus Ihrem Chatverlauf generieren', + "generate personalized programming insights from your chat history": + "Personalisierte Programmier-Einblicke aus Ihrem Chatverlauf generieren", // ============================================================================ // Commands - Session History // ============================================================================ - 'Resume a previous session': 'Eine vorherige Sitzung fortsetzen', - 'Restore a tool call. This will reset the conversation and file history to the state it was in when the tool call was suggested': - 'Einen Tool-Aufruf wiederherstellen. Dadurch werden Konversations- und Dateiverlauf auf den Zustand zurückgesetzt, in dem der Tool-Aufruf vorgeschlagen wurde', - 'Could not detect terminal type. Supported terminals: VS Code, Cursor, Windsurf, and Trae.': - 'Terminal-Typ konnte nicht erkannt werden. Unterstützte Terminals: VS Code, Cursor, Windsurf und Trae.', + "Resume a previous session": "Eine vorherige Sitzung fortsetzen", + "Restore a tool call. This will reset the conversation and file history to the state it was in when the tool call was suggested": + "Einen Tool-Aufruf wiederherstellen. Dadurch werden Konversations- und Dateiverlauf auf den Zustand zurückgesetzt, in dem der Tool-Aufruf vorgeschlagen wurde", + "Could not detect terminal type. Supported terminals: VS Code, Cursor, Windsurf, and Trae.": + "Terminal-Typ konnte nicht erkannt werden. Unterstützte Terminals: VS Code, Cursor, Windsurf und Trae.", 'Terminal "{{terminal}}" is not supported yet.': 'Terminal "{{terminal}}" wird noch nicht unterstützt.', // ============================================================================ // Commands - Language // ============================================================================ - 'Invalid language. Available: {{options}}': - 'Ungültige Sprache. Verfügbar: {{options}}', - 'Language subcommands do not accept additional arguments.': - 'Sprach-Unterbefehle akzeptieren keine zusätzlichen Argumente.', - 'Current UI language: {{lang}}': 'Aktuelle UI-Sprache: {{lang}}', - 'Current LLM output language: {{lang}}': - 'Aktuelle LLM-Ausgabesprache: {{lang}}', - 'LLM output language not set': 'LLM-Ausgabesprache nicht festgelegt', - 'Set UI language': 'UI-Sprache festlegen', - 'Set LLM output language': 'LLM-Ausgabesprache festlegen', - 'Usage: /language ui [{{options}}]': 'Verwendung: /language ui [{{options}}]', - 'Usage: /language output ': - 'Verwendung: /language output ', - 'Example: /language output 中文': 'Beispiel: /language output Deutsch', - 'Example: /language output English': 'Beispiel: /language output Englisch', - 'Example: /language output 日本語': 'Beispiel: /language output Japanisch', - 'Example: /language output Português': - 'Beispiel: /language output Portugiesisch', - 'UI language changed to {{lang}}': 'UI-Sprache geändert zu {{lang}}', - 'LLM output language set to {{lang}}': - 'LLM-Ausgabesprache auf {{lang}} gesetzt', - 'LLM output language rule file generated at {{path}}': - 'LLM-Ausgabesprach-Regeldatei generiert unter {{path}}', - 'Please restart the application for the changes to take effect.': - 'Bitte starten Sie die Anwendung neu, damit die Änderungen wirksam werden.', - 'Failed to generate LLM output language rule file: {{error}}': - 'Fehler beim Generieren der LLM-Ausgabesprach-Regeldatei: {{error}}', - 'Invalid command. Available subcommands:': - 'Ungültiger Befehl. Verfügbare Unterbefehle:', - 'Available subcommands:': 'Verfügbare Unterbefehle:', - 'To request additional UI language packs, please open an issue on GitHub.': - 'Um zusätzliche UI-Sprachpakete anzufordern, öffnen Sie bitte ein Issue auf GitHub.', - 'Available options:': 'Verfügbare Optionen:', - 'Set UI language to {{name}}': 'UI-Sprache auf {{name}} setzen', + "Invalid language. Available: {{options}}": "Ungültige Sprache. Verfügbar: {{options}}", + "Language subcommands do not accept additional arguments.": + "Sprach-Unterbefehle akzeptieren keine zusätzlichen Argumente.", + "Current UI language: {{lang}}": "Aktuelle UI-Sprache: {{lang}}", + "Current LLM output language: {{lang}}": "Aktuelle LLM-Ausgabesprache: {{lang}}", + "LLM output language not set": "LLM-Ausgabesprache nicht festgelegt", + "Set UI language": "UI-Sprache festlegen", + "Set LLM output language": "LLM-Ausgabesprache festlegen", + "Usage: /language ui [{{options}}]": "Verwendung: /language ui [{{options}}]", + "Usage: /language output ": "Verwendung: /language output ", + "Example: /language output 中文": "Beispiel: /language output Deutsch", + "Example: /language output English": "Beispiel: /language output Englisch", + "Example: /language output 日本語": "Beispiel: /language output Japanisch", + "Example: /language output Português": "Beispiel: /language output Portugiesisch", + "UI language changed to {{lang}}": "UI-Sprache geändert zu {{lang}}", + "LLM output language set to {{lang}}": "LLM-Ausgabesprache auf {{lang}} gesetzt", + "LLM output language rule file generated at {{path}}": + "LLM-Ausgabesprach-Regeldatei generiert unter {{path}}", + "Please restart the application for the changes to take effect.": + "Bitte starten Sie die Anwendung neu, damit die Änderungen wirksam werden.", + "Failed to generate LLM output language rule file: {{error}}": + "Fehler beim Generieren der LLM-Ausgabesprach-Regeldatei: {{error}}", + "Invalid command. Available subcommands:": "Ungültiger Befehl. Verfügbare Unterbefehle:", + "Available subcommands:": "Verfügbare Unterbefehle:", + "To request additional UI language packs, please open an issue on GitHub.": + "Um zusätzliche UI-Sprachpakete anzufordern, öffnen Sie bitte ein Issue auf GitHub.", + "Available options:": "Verfügbare Optionen:", + "Set UI language to {{name}}": "UI-Sprache auf {{name}} setzen", // ============================================================================ // Commands - Approval Mode // ============================================================================ - 'Tool Approval Mode': 'Werkzeug-Genehmigungsmodus', - 'Current approval mode: {{mode}}': 'Aktueller Genehmigungsmodus: {{mode}}', - 'Available approval modes:': 'Verfügbare Genehmigungsmodi:', - 'Approval mode changed to: {{mode}}': - 'Genehmigungsmodus geändert zu: {{mode}}', - 'Approval mode changed to: {{mode}} (saved to {{scope}} settings{{location}})': - 'Genehmigungsmodus geändert zu: {{mode}} (gespeichert in {{scope}} Einstellungen{{location}})', - 'Usage: /approval-mode [--session|--user|--project]': - 'Verwendung: /approval-mode [--session|--user|--project]', + "Tool Approval Mode": "Werkzeug-Genehmigungsmodus", + "Current approval mode: {{mode}}": "Aktueller Genehmigungsmodus: {{mode}}", + "Available approval modes:": "Verfügbare Genehmigungsmodi:", + "Approval mode changed to: {{mode}}": "Genehmigungsmodus geändert zu: {{mode}}", + "Approval mode changed to: {{mode}} (saved to {{scope}} settings{{location}})": + "Genehmigungsmodus geändert zu: {{mode}} (gespeichert in {{scope}} Einstellungen{{location}})", + "Usage: /approval-mode [--session|--user|--project]": + "Verwendung: /approval-mode [--session|--user|--project]", - 'Scope subcommands do not accept additional arguments.': - 'Bereichs-Unterbefehle akzeptieren keine zusätzlichen Argumente.', - 'Plan mode - Analyze only, do not modify files or execute commands': - 'Planungsmodus - Nur analysieren, keine Dateien ändern oder Befehle ausführen', - 'Default mode - Require approval for file edits or shell commands': - 'Standardmodus - Genehmigung für Dateibearbeitungen oder Shell-Befehle erforderlich', - 'Auto-edit mode - Automatically approve file edits': - 'Automatischer Bearbeitungsmodus - Dateibearbeitungen automatisch genehmigen', - 'YOLO mode - Automatically approve all tools': - 'YOLO-Modus - Alle Werkzeuge automatisch genehmigen', - '{{mode}} mode': '{{mode}}-Modus', - 'Settings service is not available; unable to persist the approval mode.': - 'Einstellungsdienst nicht verfügbar; Genehmigungsmodus kann nicht gespeichert werden.', - 'Failed to save approval mode: {{error}}': - 'Fehler beim Speichern des Genehmigungsmodus: {{error}}', - 'Failed to change approval mode: {{error}}': - 'Fehler beim Ändern des Genehmigungsmodus: {{error}}', - 'Apply to current session only (temporary)': - 'Nur auf aktuelle Sitzung anwenden (temporär)', - 'Persist for this project/workspace': - 'Für dieses Projekt/Arbeitsbereich speichern', - 'Persist for this user on this machine': - 'Für diesen Benutzer auf diesem Computer speichern', - 'Analyze only, do not modify files or execute commands': - 'Nur analysieren, keine Dateien ändern oder Befehle ausführen', - 'Require approval for file edits or shell commands': - 'Genehmigung für Dateibearbeitungen oder Shell-Befehle erforderlich', - 'Automatically approve file edits': - 'Dateibearbeitungen automatisch genehmigen', - 'Automatically approve all tools': 'Alle Werkzeuge automatisch genehmigen', - 'Workspace approval mode exists and takes priority. User-level change will have no effect.': - 'Arbeitsbereich-Genehmigungsmodus existiert und hat Vorrang. Benutzerebene-Änderung hat keine Wirkung.', - 'Apply To': 'Anwenden auf', - 'Workspace Settings': 'Arbeitsbereich-Einstellungen', + "Scope subcommands do not accept additional arguments.": + "Bereichs-Unterbefehle akzeptieren keine zusätzlichen Argumente.", + "Plan mode - Analyze only, do not modify files or execute commands": + "Planungsmodus - Nur analysieren, keine Dateien ändern oder Befehle ausführen", + "Default mode - Require approval for file edits or shell commands": + "Standardmodus - Genehmigung für Dateibearbeitungen oder Shell-Befehle erforderlich", + "Auto-edit mode - Automatically approve file edits": + "Automatischer Bearbeitungsmodus - Dateibearbeitungen automatisch genehmigen", + "YOLO mode - Automatically approve all tools": + "YOLO-Modus - Alle Werkzeuge automatisch genehmigen", + "{{mode}} mode": "{{mode}}-Modus", + "Settings service is not available; unable to persist the approval mode.": + "Einstellungsdienst nicht verfügbar; Genehmigungsmodus kann nicht gespeichert werden.", + "Failed to save approval mode: {{error}}": + "Fehler beim Speichern des Genehmigungsmodus: {{error}}", + "Failed to change approval mode: {{error}}": + "Fehler beim Ändern des Genehmigungsmodus: {{error}}", + "Apply to current session only (temporary)": "Nur auf aktuelle Sitzung anwenden (temporär)", + "Persist for this project/workspace": "Für dieses Projekt/Arbeitsbereich speichern", + "Persist for this user on this machine": "Für diesen Benutzer auf diesem Computer speichern", + "Analyze only, do not modify files or execute commands": + "Nur analysieren, keine Dateien ändern oder Befehle ausführen", + "Require approval for file edits or shell commands": + "Genehmigung für Dateibearbeitungen oder Shell-Befehle erforderlich", + "Automatically approve file edits": "Dateibearbeitungen automatisch genehmigen", + "Automatically approve all tools": "Alle Werkzeuge automatisch genehmigen", + "Workspace approval mode exists and takes priority. User-level change will have no effect.": + "Arbeitsbereich-Genehmigungsmodus existiert und hat Vorrang. Benutzerebene-Änderung hat keine Wirkung.", + "Apply To": "Anwenden auf", + "Workspace Settings": "Arbeitsbereich-Einstellungen", // ============================================================================ // Commands - Memory // ============================================================================ - 'Commands for interacting with memory.': - 'Befehle für die Interaktion mit dem Speicher.', - 'Show the current memory contents.': 'Aktuellen Speicherinhalt anzeigen.', - 'Show project-level memory contents.': - 'Projektebene-Speicherinhalt anzeigen.', - 'Show global memory contents.': 'Globalen Speicherinhalt anzeigen.', - 'Add content to project-level memory.': - 'Inhalt zum Projektebene-Speicher hinzufügen.', - 'Add content to global memory.': 'Inhalt zum globalen Speicher hinzufügen.', - 'Refresh the memory from the source.': - 'Speicher aus der Quelle aktualisieren.', - 'Usage: /memory add --project ': - 'Verwendung: /memory add --project ', - 'Usage: /memory add --global ': - 'Verwendung: /memory add --global ', + "Commands for interacting with memory.": "Befehle für die Interaktion mit dem Speicher.", + "Show the current memory contents.": "Aktuellen Speicherinhalt anzeigen.", + "Show project-level memory contents.": "Projektebene-Speicherinhalt anzeigen.", + "Show global memory contents.": "Globalen Speicherinhalt anzeigen.", + "Add content to project-level memory.": "Inhalt zum Projektebene-Speicher hinzufügen.", + "Add content to global memory.": "Inhalt zum globalen Speicher hinzufügen.", + "Refresh the memory from the source.": "Speicher aus der Quelle aktualisieren.", + "Usage: /memory add --project ": + "Verwendung: /memory add --project ", + "Usage: /memory add --global ": + "Verwendung: /memory add --global ", 'Attempting to save to project memory: "{{text}}"': 'Versuche im Projektspeicher zu speichern: "{{text}}"', 'Attempting to save to global memory: "{{text}}"': 'Versuche im globalen Speicher zu speichern: "{{text}}"', - 'Current memory content from {{count}} file(s):': - 'Aktueller Speicherinhalt aus {{count}} Datei(en):', - 'Memory is currently empty.': 'Speicher ist derzeit leer.', - 'Project memory file not found or is currently empty.': - 'Projektspeicherdatei nicht gefunden oder derzeit leer.', - 'Global memory file not found or is currently empty.': - 'Globale Speicherdatei nicht gefunden oder derzeit leer.', - 'Global memory is currently empty.': 'Globaler Speicher ist derzeit leer.', - 'Global memory content:\n\n---\n{{content}}\n---': - 'Globaler Speicherinhalt:\n\n---\n{{content}}\n---', - 'Project memory content from {{path}}:\n\n---\n{{content}}\n---': - 'Projektspeicherinhalt von {{path}}:\n\n---\n{{content}}\n---', - 'Project memory is currently empty.': 'Projektspeicher ist derzeit leer.', - 'Refreshing memory from source files...': - 'Speicher wird aus Quelldateien aktualisiert...', - 'Add content to the memory. Use --global for global memory or --project for project memory.': - 'Inhalt zum Speicher hinzufügen. --global für globalen Speicher oder --project für Projektspeicher verwenden.', - 'Usage: /memory add [--global|--project] ': - 'Verwendung: /memory add [--global|--project] ', + "Current memory content from {{count}} file(s):": + "Aktueller Speicherinhalt aus {{count}} Datei(en):", + "Memory is currently empty.": "Speicher ist derzeit leer.", + "Project memory file not found or is currently empty.": + "Projektspeicherdatei nicht gefunden oder derzeit leer.", + "Global memory file not found or is currently empty.": + "Globale Speicherdatei nicht gefunden oder derzeit leer.", + "Global memory is currently empty.": "Globaler Speicher ist derzeit leer.", + "Global memory content:\n\n---\n{{content}}\n---": + "Globaler Speicherinhalt:\n\n---\n{{content}}\n---", + "Project memory content from {{path}}:\n\n---\n{{content}}\n---": + "Projektspeicherinhalt von {{path}}:\n\n---\n{{content}}\n---", + "Project memory is currently empty.": "Projektspeicher ist derzeit leer.", + "Refreshing memory from source files...": "Speicher wird aus Quelldateien aktualisiert...", + "Add content to the memory. Use --global for global memory or --project for project memory.": + "Inhalt zum Speicher hinzufügen. --global für globalen Speicher oder --project für Projektspeicher verwenden.", + "Usage: /memory add [--global|--project] ": + "Verwendung: /memory add [--global|--project] ", 'Attempting to save to memory {{scope}}: "{{fact}}"': 'Versuche im Speicher {{scope}} zu speichern: "{{fact}}"', // ============================================================================ // Commands - MCP // ============================================================================ - 'Authenticate with an OAuth-enabled MCP server': - 'Mit einem OAuth-fähigen MCP-Server authentifizieren', - 'List configured MCP servers and tools': - 'Konfigurierte MCP-Server und Werkzeuge auflisten', - 'Restarts MCP servers.': 'MCP-Server neu starten.', - 'Could not retrieve tool registry.': - 'Werkzeugregister konnte nicht abgerufen werden.', - 'No MCP servers configured with OAuth authentication.': - 'Keine MCP-Server mit OAuth-Authentifizierung konfiguriert.', - 'MCP servers with OAuth authentication:': - 'MCP-Server mit OAuth-Authentifizierung:', - 'Use /mcp auth to authenticate.': - 'Verwenden Sie /mcp auth zur Authentifizierung.', + "Authenticate with an OAuth-enabled MCP server": + "Mit einem OAuth-fähigen MCP-Server authentifizieren", + "List configured MCP servers and tools": "Konfigurierte MCP-Server und Werkzeuge auflisten", + "Restarts MCP servers.": "MCP-Server neu starten.", + "Could not retrieve tool registry.": "Werkzeugregister konnte nicht abgerufen werden.", + "No MCP servers configured with OAuth authentication.": + "Keine MCP-Server mit OAuth-Authentifizierung konfiguriert.", + "MCP servers with OAuth authentication:": "MCP-Server mit OAuth-Authentifizierung:", + "Use /mcp auth to authenticate.": + "Verwenden Sie /mcp auth zur Authentifizierung.", "MCP server '{{name}}' not found.": "MCP-Server '{{name}}' nicht gefunden.", "Successfully authenticated and refreshed tools for '{{name}}'.": "Erfolgreich authentifiziert und Werkzeuge für '{{name}}' aktualisiert.", "Failed to authenticate with MCP server '{{name}}': {{error}}": "Authentifizierung mit MCP-Server '{{name}}' fehlgeschlagen: {{error}}", - "Re-discovering tools from '{{name}}'...": - "Werkzeuge von '{{name}}' werden neu erkannt...", - "Discovered {{count}} tool(s) from '{{name}}'.": - "{{count}} Werkzeug(e) von '{{name}}' entdeckt.", - 'Authentication complete. Returning to server details...': - 'Authentifizierung abgeschlossen. Zurück zu den Serverdetails...', - 'Authentication successful.': 'Authentifizierung erfolgreich.', - 'If the browser does not open, copy and paste this URL into your browser:': - 'Falls der Browser sich nicht öffnet, kopieren Sie diese URL und fügen Sie sie in Ihren Browser ein:', - 'Make sure to copy the COMPLETE URL - it may wrap across multiple lines.': - '⚠️ Stellen Sie sicher, dass Sie die VOLLSTÄNDIGE URL kopieren – sie kann über mehrere Zeilen gehen.', + "Re-discovering tools from '{{name}}'...": "Werkzeuge von '{{name}}' werden neu erkannt...", + "Discovered {{count}} tool(s) from '{{name}}'.": "{{count}} Werkzeug(e) von '{{name}}' entdeckt.", + "Authentication complete. Returning to server details...": + "Authentifizierung abgeschlossen. Zurück zu den Serverdetails...", + "Authentication successful.": "Authentifizierung erfolgreich.", + "If the browser does not open, copy and paste this URL into your browser:": + "Falls der Browser sich nicht öffnet, kopieren Sie diese URL und fügen Sie sie in Ihren Browser ein:", + "Make sure to copy the COMPLETE URL - it may wrap across multiple lines.": + "⚠️ Stellen Sie sicher, dass Sie die VOLLSTÄNDIGE URL kopieren – sie kann über mehrere Zeilen gehen.", // ============================================================================ // Commands - Chat // ============================================================================ - 'Manage conversation history.': 'Gesprächsverlauf verwalten.', - 'List saved conversation checkpoints': - 'Gespeicherte Gesprächsprüfpunkte auflisten', - 'No saved conversation checkpoints found.': - 'Keine gespeicherten Gesprächsprüfpunkte gefunden.', - 'List of saved conversations:': 'Liste gespeicherter Gespräche:', - 'Note: Newest last, oldest first': 'Hinweis: Neueste zuletzt, älteste zuerst', - 'Save the current conversation as a checkpoint. Usage: /chat save ': - 'Aktuelles Gespräch als Prüfpunkt speichern. Verwendung: /chat save ', - 'Missing tag. Usage: /chat save ': - 'Tag fehlt. Verwendung: /chat save ', - 'Delete a conversation checkpoint. Usage: /chat delete ': - 'Gesprächsprüfpunkt löschen. Verwendung: /chat delete ', - 'Missing tag. Usage: /chat delete ': - 'Tag fehlt. Verwendung: /chat delete ', + "Manage conversation history.": "Gesprächsverlauf verwalten.", + "List saved conversation checkpoints": "Gespeicherte Gesprächsprüfpunkte auflisten", + "No saved conversation checkpoints found.": "Keine gespeicherten Gesprächsprüfpunkte gefunden.", + "List of saved conversations:": "Liste gespeicherter Gespräche:", + "Note: Newest last, oldest first": "Hinweis: Neueste zuletzt, älteste zuerst", + "Save the current conversation as a checkpoint. Usage: /chat save ": + "Aktuelles Gespräch als Prüfpunkt speichern. Verwendung: /chat save ", + "Missing tag. Usage: /chat save ": "Tag fehlt. Verwendung: /chat save ", + "Delete a conversation checkpoint. Usage: /chat delete ": + "Gesprächsprüfpunkt löschen. Verwendung: /chat delete ", + "Missing tag. Usage: /chat delete ": "Tag fehlt. Verwendung: /chat delete ", "Conversation checkpoint '{{tag}}' has been deleted.": "Gesprächsprüfpunkt '{{tag}}' wurde gelöscht.", "Error: No checkpoint found with tag '{{tag}}'.": "Fehler: Kein Prüfpunkt mit Tag '{{tag}}' gefunden.", - 'Resume a conversation from a checkpoint. Usage: /chat resume ': - 'Gespräch von einem Prüfpunkt fortsetzen. Verwendung: /chat resume ', - 'Missing tag. Usage: /chat resume ': - 'Tag fehlt. Verwendung: /chat resume ', - 'No saved checkpoint found with tag: {{tag}}.': - 'Kein gespeicherter Prüfpunkt mit Tag gefunden: {{tag}}.', - 'A checkpoint with the tag {{tag}} already exists. Do you want to overwrite it?': - 'Ein Prüfpunkt mit dem Tag {{tag}} existiert bereits. Möchten Sie ihn überschreiben?', - 'No chat client available to save conversation.': - 'Kein Chat-Client verfügbar, um Gespräch zu speichern.', - 'Conversation checkpoint saved with tag: {{tag}}.': - 'Gesprächsprüfpunkt gespeichert mit Tag: {{tag}}.', - 'No conversation found to save.': 'Kein Gespräch zum Speichern gefunden.', - 'No chat client available to share conversation.': - 'Kein Chat-Client verfügbar, um Gespräch zu teilen.', - 'Invalid file format. Only .md and .json are supported.': - 'Ungültiges Dateiformat. Nur .md und .json werden unterstützt.', - 'Error sharing conversation: {{error}}': - 'Fehler beim Teilen des Gesprächs: {{error}}', - 'Conversation shared to {{filePath}}': 'Gespräch geteilt nach {{filePath}}', - 'No conversation found to share.': 'Kein Gespräch zum Teilen gefunden.', - 'Share the current conversation to a markdown or json file. Usage: /chat share ': - 'Aktuelles Gespräch in eine Markdown- oder JSON-Datei teilen. Verwendung: /chat share ', + "Resume a conversation from a checkpoint. Usage: /chat resume ": + "Gespräch von einem Prüfpunkt fortsetzen. Verwendung: /chat resume ", + "Missing tag. Usage: /chat resume ": "Tag fehlt. Verwendung: /chat resume ", + "No saved checkpoint found with tag: {{tag}}.": + "Kein gespeicherter Prüfpunkt mit Tag gefunden: {{tag}}.", + "A checkpoint with the tag {{tag}} already exists. Do you want to overwrite it?": + "Ein Prüfpunkt mit dem Tag {{tag}} existiert bereits. Möchten Sie ihn überschreiben?", + "No chat client available to save conversation.": + "Kein Chat-Client verfügbar, um Gespräch zu speichern.", + "Conversation checkpoint saved with tag: {{tag}}.": + "Gesprächsprüfpunkt gespeichert mit Tag: {{tag}}.", + "No conversation found to save.": "Kein Gespräch zum Speichern gefunden.", + "No chat client available to share conversation.": + "Kein Chat-Client verfügbar, um Gespräch zu teilen.", + "Invalid file format. Only .md and .json are supported.": + "Ungültiges Dateiformat. Nur .md und .json werden unterstützt.", + "Error sharing conversation: {{error}}": "Fehler beim Teilen des Gesprächs: {{error}}", + "Conversation shared to {{filePath}}": "Gespräch geteilt nach {{filePath}}", + "No conversation found to share.": "Kein Gespräch zum Teilen gefunden.", + "Share the current conversation to a markdown or json file. Usage: /chat share ": + "Aktuelles Gespräch in eine Markdown- oder JSON-Datei teilen. Verwendung: /chat share ", // ============================================================================ // Commands - Summary // ============================================================================ - 'Generate a project summary and save it to .qwen/PROJECT_SUMMARY.md': - 'Projektzusammenfassung generieren und in .qwen/PROJECT_SUMMARY.md speichern', - 'No chat client available to generate summary.': - 'Kein Chat-Client verfügbar, um Zusammenfassung zu generieren.', - 'Already generating summary, wait for previous request to complete': - 'Zusammenfassung wird bereits generiert, warten Sie auf Abschluss der vorherigen Anfrage', - 'No conversation found to summarize.': - 'Kein Gespräch zum Zusammenfassen gefunden.', - 'Failed to generate project context summary: {{error}}': - 'Fehler beim Generieren der Projektkontextzusammenfassung: {{error}}', - 'Saved project summary to {{filePathForDisplay}}.': - 'Projektzusammenfassung gespeichert unter {{filePathForDisplay}}.', - 'Saving project summary...': 'Projektzusammenfassung wird gespeichert...', - 'Generating project summary...': 'Projektzusammenfassung wird generiert...', - 'Failed to generate summary - no text content received from LLM response': - 'Fehler beim Generieren der Zusammenfassung - kein Textinhalt von LLM-Antwort erhalten', + "Generate a project summary and save it to .qwen/PROJECT_SUMMARY.md": + "Projektzusammenfassung generieren und in .qwen/PROJECT_SUMMARY.md speichern", + "No chat client available to generate summary.": + "Kein Chat-Client verfügbar, um Zusammenfassung zu generieren.", + "Already generating summary, wait for previous request to complete": + "Zusammenfassung wird bereits generiert, warten Sie auf Abschluss der vorherigen Anfrage", + "No conversation found to summarize.": "Kein Gespräch zum Zusammenfassen gefunden.", + "Failed to generate project context summary: {{error}}": + "Fehler beim Generieren der Projektkontextzusammenfassung: {{error}}", + "Saved project summary to {{filePathForDisplay}}.": + "Projektzusammenfassung gespeichert unter {{filePathForDisplay}}.", + "Saving project summary...": "Projektzusammenfassung wird gespeichert...", + "Generating project summary...": "Projektzusammenfassung wird generiert...", + "Failed to generate summary - no text content received from LLM response": + "Fehler beim Generieren der Zusammenfassung - kein Textinhalt von LLM-Antwort erhalten", // ============================================================================ // Commands - Model // ============================================================================ - 'Switch the model for this session': 'Modell für diese Sitzung wechseln', - 'Set fast model for background tasks': - 'Schnelles Modell für Hintergrundaufgaben festlegen', - 'Content generator configuration not available.': - 'Inhaltsgenerator-Konfiguration nicht verfügbar.', - 'Authentication type not available.': - 'Authentifizierungstyp nicht verfügbar.', - 'No models available for the current authentication type ({{authType}}).': - 'Keine Modelle für den aktuellen Authentifizierungstyp ({{authType}}) verfügbar.', + "Switch the model for this session": "Modell für diese Sitzung wechseln", + "Set fast model for background tasks": "Schnelles Modell für Hintergrundaufgaben festlegen", + "Content generator configuration not available.": + "Inhaltsgenerator-Konfiguration nicht verfügbar.", + "Authentication type not available.": "Authentifizierungstyp nicht verfügbar.", + "No models available for the current authentication type ({{authType}}).": + "Keine Modelle für den aktuellen Authentifizierungstyp ({{authType}}) verfügbar.", // ============================================================================ // Commands - Clear // ============================================================================ - 'Starting a new session, resetting chat, and clearing terminal.': - 'Neue Sitzung wird gestartet, Chat wird zurückgesetzt und Terminal wird gelöscht.', - 'Starting a new session and clearing.': - 'Neue Sitzung wird gestartet und gelöscht.', + "Starting a new session, resetting chat, and clearing terminal.": + "Neue Sitzung wird gestartet, Chat wird zurückgesetzt und Terminal wird gelöscht.", + "Starting a new session and clearing.": "Neue Sitzung wird gestartet und gelöscht.", // ============================================================================ // Commands - Compress // ============================================================================ - 'Already compressing, wait for previous request to complete': - 'Komprimierung läuft bereits, warten Sie auf Abschluss der vorherigen Anfrage', - 'Failed to compress chat history.': - 'Fehler beim Komprimieren des Chatverlaufs.', - 'Failed to compress chat history: {{error}}': - 'Fehler beim Komprimieren des Chatverlaufs: {{error}}', - 'Compressing chat history': 'Chatverlauf wird komprimiert', - 'Chat history compressed from {{originalTokens}} to {{newTokens}} tokens.': - 'Chatverlauf komprimiert von {{originalTokens}} auf {{newTokens}} Token.', - 'Compression was not beneficial for this history size.': - 'Komprimierung war für diese Verlaufsgröße nicht vorteilhaft.', - 'Chat history compression did not reduce size. This may indicate issues with the compression prompt.': - 'Chatverlauf-Komprimierung hat die Größe nicht reduziert. Dies kann auf Probleme mit dem Komprimierungs-Prompt hindeuten.', - 'Could not compress chat history due to a token counting error.': - 'Chatverlauf konnte aufgrund eines Token-Zählfehlers nicht komprimiert werden.', - 'Chat history is already compressed.': 'Chatverlauf ist bereits komprimiert.', + "Already compressing, wait for previous request to complete": + "Komprimierung läuft bereits, warten Sie auf Abschluss der vorherigen Anfrage", + "Failed to compress chat history.": "Fehler beim Komprimieren des Chatverlaufs.", + "Failed to compress chat history: {{error}}": + "Fehler beim Komprimieren des Chatverlaufs: {{error}}", + "Compressing chat history": "Chatverlauf wird komprimiert", + "Chat history compressed from {{originalTokens}} to {{newTokens}} tokens.": + "Chatverlauf komprimiert von {{originalTokens}} auf {{newTokens}} Token.", + "Compression was not beneficial for this history size.": + "Komprimierung war für diese Verlaufsgröße nicht vorteilhaft.", + "Chat history compression did not reduce size. This may indicate issues with the compression prompt.": + "Chatverlauf-Komprimierung hat die Größe nicht reduziert. Dies kann auf Probleme mit dem Komprimierungs-Prompt hindeuten.", + "Could not compress chat history due to a token counting error.": + "Chatverlauf konnte aufgrund eines Token-Zählfehlers nicht komprimiert werden.", + "Chat history is already compressed.": "Chatverlauf ist bereits komprimiert.", // ============================================================================ // Commands - Directory // ============================================================================ - 'Configuration is not available.': 'Konfiguration ist nicht verfügbar.', - 'Please provide at least one path to add.': - 'Bitte geben Sie mindestens einen Pfad zum Hinzufügen an.', - 'The /directory add command is not supported in restrictive sandbox profiles. Please use --include-directories when starting the session instead.': - 'Der Befehl /directory add wird in restriktiven Sandbox-Profilen nicht unterstützt. Bitte verwenden Sie --include-directories beim Starten der Sitzung.', - "Error adding '{{path}}': {{error}}": - "Fehler beim Hinzufügen von '{{path}}': {{error}}", - 'Successfully added QWEN.md files from the following directories if there are:\n- {{directories}}': - 'QWEN.md-Dateien aus folgenden Verzeichnissen erfolgreich hinzugefügt, falls vorhanden:\n- {{directories}}', - 'Error refreshing memory: {{error}}': - 'Fehler beim Aktualisieren des Speichers: {{error}}', - 'Successfully added directories:\n- {{directories}}': - 'Verzeichnisse erfolgreich hinzugefügt:\n- {{directories}}', - 'Current workspace directories:\n{{directories}}': - 'Aktuelle Arbeitsbereichsverzeichnisse:\n{{directories}}', + "Configuration is not available.": "Konfiguration ist nicht verfügbar.", + "Please provide at least one path to add.": + "Bitte geben Sie mindestens einen Pfad zum Hinzufügen an.", + "The /directory add command is not supported in restrictive sandbox profiles. Please use --include-directories when starting the session instead.": + "Der Befehl /directory add wird in restriktiven Sandbox-Profilen nicht unterstützt. Bitte verwenden Sie --include-directories beim Starten der Sitzung.", + "Error adding '{{path}}': {{error}}": "Fehler beim Hinzufügen von '{{path}}': {{error}}", + "Successfully added QWEN.md files from the following directories if there are:\n- {{directories}}": + "QWEN.md-Dateien aus folgenden Verzeichnissen erfolgreich hinzugefügt, falls vorhanden:\n- {{directories}}", + "Error refreshing memory: {{error}}": "Fehler beim Aktualisieren des Speichers: {{error}}", + "Successfully added directories:\n- {{directories}}": + "Verzeichnisse erfolgreich hinzugefügt:\n- {{directories}}", + "Current workspace directories:\n{{directories}}": + "Aktuelle Arbeitsbereichsverzeichnisse:\n{{directories}}", // ============================================================================ // Commands - Docs // ============================================================================ - 'Please open the following URL in your browser to view the documentation:\n{{url}}': - 'Bitte öffnen Sie folgende URL in Ihrem Browser, um die Dokumentation anzusehen:\n{{url}}', - 'Opening documentation in your browser: {{url}}': - 'Dokumentation wird in Ihrem Browser geöffnet: {{url}}', + "Please open the following URL in your browser to view the documentation:\n{{url}}": + "Bitte öffnen Sie folgende URL in Ihrem Browser, um die Dokumentation anzusehen:\n{{url}}", + "Opening documentation in your browser: {{url}}": + "Dokumentation wird in Ihrem Browser geöffnet: {{url}}", // ============================================================================ // Dialogs - Tool Confirmation // ============================================================================ - 'Do you want to proceed?': 'Möchten Sie fortfahren?', - 'Yes, allow once': 'Ja, einmal erlauben', - 'Allow always': 'Immer erlauben', - Yes: 'Ja', - No: 'Nein', - 'No (esc)': 'Nein (Esc)', - 'Yes, allow always for this session': 'Ja, für diese Sitzung immer erlauben', + "Do you want to proceed?": "Möchten Sie fortfahren?", + "Yes, allow once": "Ja, einmal erlauben", + "Allow always": "Immer erlauben", + Yes: "Ja", + No: "Nein", + "No (esc)": "Nein (Esc)", + "Yes, allow always for this session": "Ja, für diese Sitzung immer erlauben", // MCP Management Dialog (translations for MCP UI components) - 'Manage MCP servers': 'MCP-Server verwalten', - 'Server Detail': 'Serverdetails', - 'Disable Server': 'Server deaktivieren', - Tools: 'Werkzeuge', - 'Tool Detail': 'Werkzeugdetails', - 'MCP Management': 'MCP-Verwaltung', - 'Loading...': 'Lädt...', - 'Unknown step': 'Unbekannter Schritt', - 'Esc to back': 'Esc zurück', - '↑↓ to navigate · Enter to select · Esc to close': - '↑↓ navigieren · Enter auswählen · Esc schließen', - '↑↓ to navigate · Enter to select · Esc to back': - '↑↓ navigieren · Enter auswählen · Esc zurück', - '↑↓ to navigate · Enter to confirm · Esc to back': - '↑↓ navigieren · Enter bestätigen · Esc zurück', - 'User Settings (global)': 'Benutzereinstellungen (global)', - 'Workspace Settings (project-specific)': - 'Arbeitsbereichseinstellungen (projektspezifisch)', - 'Disable server:': 'Server deaktivieren:', - 'Select where to add the server to the exclude list:': - 'Wählen Sie, wo der Server zur Ausschlussliste hinzugefügt werden soll:', - 'Press Enter to confirm, Esc to cancel': - 'Enter zum Bestätigen, Esc zum Abbrechen', - Disable: 'Deaktivieren', - Enable: 'Aktivieren', - Authenticate: 'Authentifizieren', - 'Re-authenticate': 'Erneut authentifizieren', - 'Clear Authentication': 'Authentifizierung löschen', - disabled: 'deaktiviert', - 'Server:': 'Server:', - Reconnect: 'Neu verbinden', - 'View tools': 'Werkzeuge anzeigen', - 'Status:': 'Status:', - 'Command:': 'Befehl:', - 'Working Directory:': 'Arbeitsverzeichnis:', - 'Capabilities:': 'Fähigkeiten:', - 'No server selected': 'Kein Server ausgewählt', - '(disabled)': '(deaktiviert)', - 'Error:': 'Fehler:', - tool: 'Werkzeug', - tools: 'Werkzeuge', - connected: 'verbunden', - connecting: 'verbindet', - disconnected: 'getrennt', - error: 'Fehler', + "Manage MCP servers": "MCP-Server verwalten", + "Server Detail": "Serverdetails", + "Disable Server": "Server deaktivieren", + Tools: "Werkzeuge", + "Tool Detail": "Werkzeugdetails", + "MCP Management": "MCP-Verwaltung", + "Loading...": "Lädt...", + "Unknown step": "Unbekannter Schritt", + "Esc to back": "Esc zurück", + "↑↓ to navigate · Enter to select · Esc to close": + "↑↓ navigieren · Enter auswählen · Esc schließen", + "↑↓ to navigate · Enter to select · Esc to back": "↑↓ navigieren · Enter auswählen · Esc zurück", + "↑↓ to navigate · Enter to confirm · Esc to back": + "↑↓ navigieren · Enter bestätigen · Esc zurück", + "User Settings (global)": "Benutzereinstellungen (global)", + "Workspace Settings (project-specific)": "Arbeitsbereichseinstellungen (projektspezifisch)", + "Disable server:": "Server deaktivieren:", + "Select where to add the server to the exclude list:": + "Wählen Sie, wo der Server zur Ausschlussliste hinzugefügt werden soll:", + "Press Enter to confirm, Esc to cancel": "Enter zum Bestätigen, Esc zum Abbrechen", + Disable: "Deaktivieren", + Enable: "Aktivieren", + Authenticate: "Authentifizieren", + "Re-authenticate": "Erneut authentifizieren", + "Clear Authentication": "Authentifizierung löschen", + disabled: "deaktiviert", + "Server:": "Server:", + Reconnect: "Neu verbinden", + "View tools": "Werkzeuge anzeigen", + "Status:": "Status:", + "Command:": "Befehl:", + "Working Directory:": "Arbeitsverzeichnis:", + "Capabilities:": "Fähigkeiten:", + "No server selected": "Kein Server ausgewählt", + "(disabled)": "(deaktiviert)", + "Error:": "Fehler:", + tool: "Werkzeug", + tools: "Werkzeuge", + connected: "verbunden", + connecting: "verbindet", + disconnected: "getrennt", + error: "Fehler", // MCP Server List - 'User MCPs': 'Benutzer-MCPs', - 'Project MCPs': 'Projekt-MCPs', - 'Extension MCPs': 'Erweiterungs-MCPs', - server: 'Server', - servers: 'Server', - 'Add MCP servers to your settings to get started.': - 'Fügen Sie MCP-Server zu Ihren Einstellungen hinzu, um zu beginnen.', - 'Run qwen --debug to see error logs': - 'Führen Sie qwen --debug aus, um Fehlerprotokolle anzuzeigen', + "User MCPs": "Benutzer-MCPs", + "Project MCPs": "Projekt-MCPs", + "Extension MCPs": "Erweiterungs-MCPs", + server: "Server", + servers: "Server", + "Add MCP servers to your settings to get started.": + "Fügen Sie MCP-Server zu Ihren Einstellungen hinzu, um zu beginnen.", + "Run qwen --debug to see error logs": + "Führen Sie qwen --debug aus, um Fehlerprotokolle anzuzeigen", // MCP OAuth Authentication - 'OAuth Authentication': 'OAuth-Authentifizierung', - 'Press Enter to start authentication, Esc to go back': - 'Drücken Sie Enter, um die Authentifizierung zu starten, Esc zum Zurückgehen', - 'Authenticating... Please complete the login in your browser.': - 'Authentifizierung läuft... Bitte schließen Sie die Anmeldung in Ihrem Browser ab.', - 'Press Enter or Esc to go back': 'Drücken Sie Enter oder Esc zum Zurückgehen', + "OAuth Authentication": "OAuth-Authentifizierung", + "Press Enter to start authentication, Esc to go back": + "Drücken Sie Enter, um die Authentifizierung zu starten, Esc zum Zurückgehen", + "Authenticating... Please complete the login in your browser.": + "Authentifizierung läuft... Bitte schließen Sie die Anmeldung in Ihrem Browser ab.", + "Press Enter or Esc to go back": "Drücken Sie Enter oder Esc zum Zurückgehen", // MCP Tool List - 'No tools available for this server.': - 'Keine Werkzeuge für diesen Server verfügbar.', - destructive: 'destruktiv', - 'read-only': 'schreibgeschützt', - 'open-world': 'offene Welt', - idempotent: 'idempotent', - 'Tools for {{name}}': 'Werkzeuge für {{name}}', - 'Tools for {{serverName}}': 'Werkzeuge für {{serverName}}', - '{{current}}/{{total}}': '{{current}}/{{total}}', + "No tools available for this server.": "Keine Werkzeuge für diesen Server verfügbar.", + destructive: "destruktiv", + "read-only": "schreibgeschützt", + "open-world": "offene Welt", + idempotent: "idempotent", + "Tools for {{name}}": "Werkzeuge für {{name}}", + "Tools for {{serverName}}": "Werkzeuge für {{serverName}}", + "{{current}}/{{total}}": "{{current}}/{{total}}", // MCP Tool Detail - required: 'erforderlich', - Type: 'Typ', - Enum: 'Aufzählung', - Parameters: 'Parameter', - 'No tool selected': 'Kein Werkzeug ausgewählt', - Annotations: 'Anmerkungen', - Title: 'Titel', - 'Read Only': 'Schreibgeschützt', - Destructive: 'Destruktiv', - Idempotent: 'Idempotent', - 'Open World': 'Offene Welt', - Server: 'Server', + required: "erforderlich", + Type: "Typ", + Enum: "Aufzählung", + Parameters: "Parameter", + "No tool selected": "Kein Werkzeug ausgewählt", + Annotations: "Anmerkungen", + Title: "Titel", + "Read Only": "Schreibgeschützt", + Destructive: "Destruktiv", + Idempotent: "Idempotent", + "Open World": "Offene Welt", + Server: "Server", // Invalid tool related translations - '{{count}} invalid tools': '{{count}} ungültige Werkzeuge', - invalid: 'ungültig', - 'invalid: {{reason}}': 'ungültig: {{reason}}', - 'missing name': 'Name fehlt', - 'missing description': 'Beschreibung fehlt', - '(unnamed)': '(unbenannt)', - 'Warning: This tool cannot be called by the LLM': - 'Warnung: Dieses Werkzeug kann nicht vom LLM aufgerufen werden', - Reason: 'Grund', - 'Tools must have both name and description to be used by the LLM.': - 'Werkzeuge müssen sowohl einen Namen als auch eine Beschreibung haben, um vom LLM verwendet zu werden.', - 'Modify in progress:': 'Änderung in Bearbeitung:', - 'Save and close external editor to continue': - 'Speichern und externen Editor schließen, um fortzufahren', - 'Apply this change?': 'Diese Änderung anwenden?', - 'Yes, allow always': 'Ja, immer erlauben', - 'Modify with external editor': 'Mit externem Editor bearbeiten', - 'No, suggest changes (esc)': 'Nein, Änderungen vorschlagen (Esc)', - "Allow execution of: '{{command}}'?": - "Ausführung erlauben von: '{{command}}'?", - 'Yes, allow always ...': 'Ja, immer erlauben ...', - 'Always allow in this project': 'In diesem Projekt immer erlauben', - 'Always allow {{action}} in this project': - '{{action}} in diesem Projekt immer erlauben', - 'Always allow for this user': 'Für diesen Benutzer immer erlauben', - 'Always allow {{action}} for this user': - '{{action}} für diesen Benutzer immer erlauben', - 'Yes, restore previous mode ({{mode}})': - 'Ja, vorherigen Modus wiederherstellen ({{mode}})', - 'Yes, and auto-accept edits': 'Ja, und Änderungen automatisch akzeptieren', - 'Yes, and manually approve edits': 'Ja, und Änderungen manuell genehmigen', - 'No, keep planning (esc)': 'Nein, weiter planen (Esc)', - 'URLs to fetch:': 'Abzurufende URLs:', - 'MCP Server: {{server}}': 'MCP-Server: {{server}}', - 'Tool: {{tool}}': 'Werkzeug: {{tool}}', + "{{count}} invalid tools": "{{count}} ungültige Werkzeuge", + invalid: "ungültig", + "invalid: {{reason}}": "ungültig: {{reason}}", + "missing name": "Name fehlt", + "missing description": "Beschreibung fehlt", + "(unnamed)": "(unbenannt)", + "Warning: This tool cannot be called by the LLM": + "Warnung: Dieses Werkzeug kann nicht vom LLM aufgerufen werden", + Reason: "Grund", + "Tools must have both name and description to be used by the LLM.": + "Werkzeuge müssen sowohl einen Namen als auch eine Beschreibung haben, um vom LLM verwendet zu werden.", + "Modify in progress:": "Änderung in Bearbeitung:", + "Save and close external editor to continue": + "Speichern und externen Editor schließen, um fortzufahren", + "Apply this change?": "Diese Änderung anwenden?", + "Yes, allow always": "Ja, immer erlauben", + "Modify with external editor": "Mit externem Editor bearbeiten", + "No, suggest changes (esc)": "Nein, Änderungen vorschlagen (Esc)", + "Allow execution of: '{{command}}'?": "Ausführung erlauben von: '{{command}}'?", + "Yes, allow always ...": "Ja, immer erlauben ...", + "Always allow in this project": "In diesem Projekt immer erlauben", + "Always allow {{action}} in this project": "{{action}} in diesem Projekt immer erlauben", + "Always allow for this user": "Für diesen Benutzer immer erlauben", + "Always allow {{action}} for this user": "{{action}} für diesen Benutzer immer erlauben", + "Yes, restore previous mode ({{mode}})": "Ja, vorherigen Modus wiederherstellen ({{mode}})", + "Yes, and auto-accept edits": "Ja, und Änderungen automatisch akzeptieren", + "Yes, and manually approve edits": "Ja, und Änderungen manuell genehmigen", + "No, keep planning (esc)": "Nein, weiter planen (Esc)", + "URLs to fetch:": "Abzurufende URLs:", + "MCP Server: {{server}}": "MCP-Server: {{server}}", + "Tool: {{tool}}": "Werkzeug: {{tool}}", 'Allow execution of MCP tool "{{tool}}" from server "{{server}}"?': 'Ausführung des MCP-Werkzeugs "{{tool}}" von Server "{{server}}" erlauben?', 'Yes, always allow tool "{{tool}}" from server "{{server}}"': @@ -1202,752 +1112,714 @@ export default { // ============================================================================ // Dialogs - Shell Confirmation // ============================================================================ - 'Shell Command Execution': 'Shell-Befehlsausführung', - 'A custom command wants to run the following shell commands:': - 'Ein benutzerdefinierter Befehl möchte folgende Shell-Befehle ausführen:', + "Shell Command Execution": "Shell-Befehlsausführung", + "A custom command wants to run the following shell commands:": + "Ein benutzerdefinierter Befehl möchte folgende Shell-Befehle ausführen:", // ============================================================================ // Dialogs - Pro Quota // ============================================================================ - 'Pro quota limit reached for {{model}}.': - 'Pro-Kontingentlimit für {{model}} erreicht.', - 'Change auth (executes the /auth command)': - 'Authentifizierung ändern (führt den /auth-Befehl aus)', - 'Continue with {{model}}': 'Mit {{model}} fortfahren', + "Pro quota limit reached for {{model}}.": "Pro-Kontingentlimit für {{model}} erreicht.", + "Change auth (executes the /auth command)": + "Authentifizierung ändern (führt den /auth-Befehl aus)", + "Continue with {{model}}": "Mit {{model}} fortfahren", // ============================================================================ // Dialogs - Welcome Back // ============================================================================ - 'Current Plan:': 'Aktueller Plan:', - 'Progress: {{done}}/{{total}} tasks completed': - 'Fortschritt: {{done}}/{{total}} Aufgaben abgeschlossen', - ', {{inProgress}} in progress': ', {{inProgress}} in Bearbeitung', - 'Pending Tasks:': 'Ausstehende Aufgaben:', - 'What would you like to do?': 'Was möchten Sie tun?', - 'Choose how to proceed with your session:': - 'Wählen Sie, wie Sie mit Ihrer Sitzung fortfahren möchten:', - 'Start new chat session': 'Neue Chat-Sitzung starten', - 'Continue previous conversation': 'Vorheriges Gespräch fortsetzen', - '👋 Welcome back! (Last updated: {{timeAgo}})': - '👋 Willkommen zurück! (Zuletzt aktualisiert: {{timeAgo}})', - '🎯 Overall Goal:': '🎯 Gesamtziel:', + "Current Plan:": "Aktueller Plan:", + "Progress: {{done}}/{{total}} tasks completed": + "Fortschritt: {{done}}/{{total}} Aufgaben abgeschlossen", + ", {{inProgress}} in progress": ", {{inProgress}} in Bearbeitung", + "Pending Tasks:": "Ausstehende Aufgaben:", + "What would you like to do?": "Was möchten Sie tun?", + "Choose how to proceed with your session:": + "Wählen Sie, wie Sie mit Ihrer Sitzung fortfahren möchten:", + "Start new chat session": "Neue Chat-Sitzung starten", + "Continue previous conversation": "Vorheriges Gespräch fortsetzen", + "👋 Welcome back! (Last updated: {{timeAgo}})": + "👋 Willkommen zurück! (Zuletzt aktualisiert: {{timeAgo}})", + "🎯 Overall Goal:": "🎯 Gesamtziel:", // ============================================================================ // Dialogs - Auth // ============================================================================ - 'Get started': 'Loslegen', - 'Select Authentication Method': 'Authentifizierungsmethode auswählen', - 'OpenAI API key is required to use OpenAI authentication.': - 'OpenAI API-Schlüssel ist für die OpenAI-Authentifizierung erforderlich.', - 'You must select an auth method to proceed. Press Ctrl+C again to exit.': - 'Sie müssen eine Authentifizierungsmethode wählen, um fortzufahren. Drücken Sie erneut Strg+C zum Beenden.', - 'Terms of Services and Privacy Notice': - 'Nutzungsbedingungen und Datenschutzhinweis', - 'Paid \u00B7 Up to 6,000 requests/5 hrs \u00B7 All Alibaba Cloud Coding Plan Models': - 'Kostenpflichtig \u00B7 Bis zu 6.000 Anfragen/5 Std. \u00B7 Alle Alibaba Cloud Coding Plan Modelle', - 'Alibaba Cloud Coding Plan': 'Alibaba Cloud Coding Plan', - 'Bring your own API key': 'Eigenen API-Schlüssel verwenden', - 'API-KEY': 'API-KEY', - 'Use coding plan credentials or your own api-keys/providers.': - 'Verwenden Sie Coding Plan-Anmeldedaten oder Ihre eigenen API-Schlüssel/Anbieter.', - OpenAI: 'OpenAI', - 'Failed to login. Message: {{message}}': - 'Anmeldung fehlgeschlagen. Meldung: {{message}}', - 'Authentication is enforced to be {{enforcedType}}, but you are currently using {{currentType}}.': - 'Authentifizierung ist auf {{enforcedType}} festgelegt, aber Sie verwenden derzeit {{currentType}}.', - 'Please visit this URL to authorize:': - 'Bitte besuchen Sie diese URL zur Autorisierung:', - 'Or scan the QR code below:': 'Oder scannen Sie den QR-Code unten:', - 'Waiting for authorization': 'Warten auf Autorisierung', - 'Time remaining:': 'Verbleibende Zeit:', - '(Press ESC or CTRL+C to cancel)': '(ESC oder STRG+C zum Abbrechen drücken)', - 'OAuth token expired (over {{seconds}} seconds). Please select authentication method again.': - 'OAuth-Token abgelaufen (über {{seconds}} Sekunden). Bitte wählen Sie erneut eine Authentifizierungsmethode.', - 'Press any key to return to authentication type selection.': - 'Drücken Sie eine beliebige Taste, um zur Authentifizierungstypauswahl zurückzukehren.', - 'Authentication timed out. Please try again.': - 'Authentifizierung abgelaufen. Bitte versuchen Sie es erneut.', - 'Waiting for auth... (Press ESC or CTRL+C to cancel)': - 'Warten auf Authentifizierung... (ESC oder STRG+C zum Abbrechen drücken)', - 'Missing API key for OpenAI-compatible auth. Set settings.security.auth.apiKey, or set the {{envKeyHint}} environment variable.': - 'API-Schlüssel für OpenAI-kompatible Authentifizierung fehlt. Setzen Sie settings.security.auth.apiKey oder die Umgebungsvariable {{envKeyHint}}.', - '{{envKeyHint}} environment variable not found.': - 'Umgebungsvariable {{envKeyHint}} wurde nicht gefunden.', - '{{envKeyHint}} environment variable not found. Please set it in your .env file or environment variables.': - 'Umgebungsvariable {{envKeyHint}} wurde nicht gefunden. Bitte legen Sie sie in Ihrer .env-Datei oder den Systemumgebungsvariablen fest.', - '{{envKeyHint}} environment variable not found (or set settings.security.auth.apiKey). Please set it in your .env file or environment variables.': - 'Umgebungsvariable {{envKeyHint}} wurde nicht gefunden (oder setzen Sie settings.security.auth.apiKey). Bitte legen Sie sie in Ihrer .env-Datei oder den Systemumgebungsvariablen fest.', - 'Missing API key for OpenAI-compatible auth. Set the {{envKeyHint}} environment variable.': - 'API-Schlüssel für OpenAI-kompatible Authentifizierung fehlt. Setzen Sie die Umgebungsvariable {{envKeyHint}}.', - 'Anthropic provider missing required baseUrl in modelProviders[].baseUrl.': - 'Anthropic-Anbieter fehlt erforderliche baseUrl in modelProviders[].baseUrl.', - 'ANTHROPIC_BASE_URL environment variable not found.': - 'Umgebungsvariable ANTHROPIC_BASE_URL wurde nicht gefunden.', - 'Invalid auth method selected.': - 'Ungültige Authentifizierungsmethode ausgewählt.', - 'Failed to authenticate. Message: {{message}}': - 'Authentifizierung fehlgeschlagen. Meldung: {{message}}', - 'Authenticated successfully with {{authType}} credentials.': - 'Erfolgreich mit {{authType}}-Anmeldedaten authentifiziert.', + "Get started": "Loslegen", + "Select Authentication Method": "Authentifizierungsmethode auswählen", + "OpenAI API key is required to use OpenAI authentication.": + "OpenAI API-Schlüssel ist für die OpenAI-Authentifizierung erforderlich.", + "You must select an auth method to proceed. Press Ctrl+C again to exit.": + "Sie müssen eine Authentifizierungsmethode wählen, um fortzufahren. Drücken Sie erneut Strg+C zum Beenden.", + "Terms of Services and Privacy Notice": "Nutzungsbedingungen und Datenschutzhinweis", + "Paid \u00B7 Up to 6,000 requests/5 hrs \u00B7 All Alibaba Cloud Coding Plan Models": + "Kostenpflichtig \u00B7 Bis zu 6.000 Anfragen/5 Std. \u00B7 Alle Alibaba Cloud Coding Plan Modelle", + "Alibaba Cloud Coding Plan": "Alibaba Cloud Coding Plan", + "Bring your own API key": "Eigenen API-Schlüssel verwenden", + "API-KEY": "API-KEY", + "Use coding plan credentials or your own api-keys/providers.": + "Verwenden Sie Coding Plan-Anmeldedaten oder Ihre eigenen API-Schlüssel/Anbieter.", + OpenAI: "OpenAI", + "Failed to login. Message: {{message}}": "Anmeldung fehlgeschlagen. Meldung: {{message}}", + "Authentication is enforced to be {{enforcedType}}, but you are currently using {{currentType}}.": + "Authentifizierung ist auf {{enforcedType}} festgelegt, aber Sie verwenden derzeit {{currentType}}.", + "Please visit this URL to authorize:": "Bitte besuchen Sie diese URL zur Autorisierung:", + "Or scan the QR code below:": "Oder scannen Sie den QR-Code unten:", + "Waiting for authorization": "Warten auf Autorisierung", + "Time remaining:": "Verbleibende Zeit:", + "(Press ESC or CTRL+C to cancel)": "(ESC oder STRG+C zum Abbrechen drücken)", + "OAuth token expired (over {{seconds}} seconds). Please select authentication method again.": + "OAuth-Token abgelaufen (über {{seconds}} Sekunden). Bitte wählen Sie erneut eine Authentifizierungsmethode.", + "Press any key to return to authentication type selection.": + "Drücken Sie eine beliebige Taste, um zur Authentifizierungstypauswahl zurückzukehren.", + "Authentication timed out. Please try again.": + "Authentifizierung abgelaufen. Bitte versuchen Sie es erneut.", + "Waiting for auth... (Press ESC or CTRL+C to cancel)": + "Warten auf Authentifizierung... (ESC oder STRG+C zum Abbrechen drücken)", + "Missing API key for OpenAI-compatible auth. Set settings.security.auth.apiKey, or set the {{envKeyHint}} environment variable.": + "API-Schlüssel für OpenAI-kompatible Authentifizierung fehlt. Setzen Sie settings.security.auth.apiKey oder die Umgebungsvariable {{envKeyHint}}.", + "{{envKeyHint}} environment variable not found.": + "Umgebungsvariable {{envKeyHint}} wurde nicht gefunden.", + "{{envKeyHint}} environment variable not found. Please set it in your .env file or environment variables.": + "Umgebungsvariable {{envKeyHint}} wurde nicht gefunden. Bitte legen Sie sie in Ihrer .env-Datei oder den Systemumgebungsvariablen fest.", + "{{envKeyHint}} environment variable not found (or set settings.security.auth.apiKey). Please set it in your .env file or environment variables.": + "Umgebungsvariable {{envKeyHint}} wurde nicht gefunden (oder setzen Sie settings.security.auth.apiKey). Bitte legen Sie sie in Ihrer .env-Datei oder den Systemumgebungsvariablen fest.", + "Missing API key for OpenAI-compatible auth. Set the {{envKeyHint}} environment variable.": + "API-Schlüssel für OpenAI-kompatible Authentifizierung fehlt. Setzen Sie die Umgebungsvariable {{envKeyHint}}.", + "Anthropic provider missing required baseUrl in modelProviders[].baseUrl.": + "Anthropic-Anbieter fehlt erforderliche baseUrl in modelProviders[].baseUrl.", + "ANTHROPIC_BASE_URL environment variable not found.": + "Umgebungsvariable ANTHROPIC_BASE_URL wurde nicht gefunden.", + "Invalid auth method selected.": "Ungültige Authentifizierungsmethode ausgewählt.", + "Failed to authenticate. Message: {{message}}": + "Authentifizierung fehlgeschlagen. Meldung: {{message}}", + "Authenticated successfully with {{authType}} credentials.": + "Erfolgreich mit {{authType}}-Anmeldedaten authentifiziert.", 'Invalid QWEN_DEFAULT_AUTH_TYPE value: "{{value}}". Valid values are: {{validValues}}': 'Ungültiger QWEN_DEFAULT_AUTH_TYPE-Wert: "{{value}}". Gültige Werte sind: {{validValues}}', - 'OpenAI Configuration Required': 'OpenAI-Konfiguration erforderlich', - 'Please enter your OpenAI configuration. You can get an API key from': - 'Bitte geben Sie Ihre OpenAI-Konfiguration ein. Sie können einen API-Schlüssel erhalten von', - 'API Key:': 'API-Schlüssel:', - 'Invalid credentials: {{errorMessage}}': - 'Ungültige Anmeldedaten: {{errorMessage}}', - 'Failed to validate credentials': - 'Anmeldedaten konnten nicht validiert werden', - 'Press Enter to continue, Tab/↑↓ to navigate, Esc to cancel': - 'Enter zum Fortfahren, Tab/↑↓ zum Navigieren, Esc zum Abbrechen', + "OpenAI Configuration Required": "OpenAI-Konfiguration erforderlich", + "Please enter your OpenAI configuration. You can get an API key from": + "Bitte geben Sie Ihre OpenAI-Konfiguration ein. Sie können einen API-Schlüssel erhalten von", + "API Key:": "API-Schlüssel:", + "Invalid credentials: {{errorMessage}}": "Ungültige Anmeldedaten: {{errorMessage}}", + "Failed to validate credentials": "Anmeldedaten konnten nicht validiert werden", + "Press Enter to continue, Tab/↑↓ to navigate, Esc to cancel": + "Enter zum Fortfahren, Tab/↑↓ zum Navigieren, Esc zum Abbrechen", // ============================================================================ // Dialogs - Model // ============================================================================ - 'Select Model': 'Modell auswählen', - '(Press Esc to close)': '(Esc zum Schließen drücken)', - 'Current (effective) configuration': 'Aktuelle (wirksame) Konfiguration', - AuthType: 'Authentifizierungstyp', - 'API Key': 'API-Schlüssel', - unset: 'nicht gesetzt', - '(default)': '(Standard)', - '(set)': '(gesetzt)', - '(not set)': '(nicht gesetzt)', - Modality: 'Modalität', - 'Context Window': 'Kontextfenster', - text: 'Text', - 'text-only': 'nur Text', - image: 'Bild', - pdf: 'PDF', - audio: 'Audio', - video: 'Video', - 'not set': 'nicht gesetzt', - none: 'keine', - unknown: 'unbekannt', + "Select Model": "Modell auswählen", + "(Press Esc to close)": "(Esc zum Schließen drücken)", + "Current (effective) configuration": "Aktuelle (wirksame) Konfiguration", + AuthType: "Authentifizierungstyp", + "API Key": "API-Schlüssel", + unset: "nicht gesetzt", + "(default)": "(Standard)", + "(set)": "(gesetzt)", + "(not set)": "(nicht gesetzt)", + Modality: "Modalität", + "Context Window": "Kontextfenster", + text: "Text", + "text-only": "nur Text", + image: "Bild", + pdf: "PDF", + audio: "Audio", + video: "Video", + "not set": "nicht gesetzt", + none: "keine", + unknown: "unbekannt", "Failed to switch model to '{{modelId}}'.\n\n{{error}}": "Modell konnte nicht auf '{{modelId}}' umgestellt werden.\n\n{{error}}", - 'Qwen 3.6 Plus — efficient hybrid model with leading coding performance': - 'Qwen 3.6 Plus — effizientes Hybridmodell mit führender Programmierleistung', - 'The latest Qwen Vision model from Alibaba Cloud ModelStudio (version: qwen3-vl-plus-2025-09-23)': - 'Das neueste Qwen Vision Modell von Alibaba Cloud ModelStudio (Version: qwen3-vl-plus-2025-09-23)', + "Qwen 3.6 Plus — efficient hybrid model with leading coding performance": + "Qwen 3.6 Plus — effizientes Hybridmodell mit führender Programmierleistung", + "The latest Qwen Vision model from Alibaba Cloud ModelStudio (version: qwen3-vl-plus-2025-09-23)": + "Das neueste Qwen Vision Modell von Alibaba Cloud ModelStudio (Version: qwen3-vl-plus-2025-09-23)", // ============================================================================ // Dialogs - Permissions // ============================================================================ - 'Manage folder trust settings': 'Ordnervertrauenseinstellungen verwalten', - 'Manage permission rules': 'Berechtigungsregeln verwalten', - Allow: 'Erlauben', - Ask: 'Fragen', - Deny: 'Verweigern', - Workspace: 'Arbeitsbereich', + "Manage folder trust settings": "Ordnervertrauenseinstellungen verwalten", + "Manage permission rules": "Berechtigungsregeln verwalten", + Allow: "Erlauben", + Ask: "Fragen", + Deny: "Verweigern", + Workspace: "Arbeitsbereich", "Qwen Code won't ask before using allowed tools.": - 'Qwen Code fragt nicht, bevor erlaubte Tools verwendet werden.', - 'Qwen Code will ask before using these tools.': - 'Qwen Code fragt, bevor diese Tools verwendet werden.', - 'Qwen Code is not allowed to use denied tools.': - 'Qwen Code darf verweigerte Tools nicht verwenden.', - 'Manage trusted directories for this workspace.': - 'Vertrauenswürdige Verzeichnisse für diesen Arbeitsbereich verwalten.', - 'Any use of the {{tool}} tool': 'Jede Verwendung des {{tool}}-Tools', - "{{tool}} commands matching '{{pattern}}'": - "{{tool}}-Befehle, die '{{pattern}}' entsprechen", - 'From user settings': 'Aus Benutzereinstellungen', - 'From project settings': 'Aus Projekteinstellungen', - 'From session': 'Aus Sitzung', - 'Project settings (local)': 'Projekteinstellungen (lokal)', - 'Saved in .qwen/settings.local.json': - 'Gespeichert in .qwen/settings.local.json', - 'Project settings': 'Projekteinstellungen', - 'Checked in at .qwen/settings.json': 'Eingecheckt in .qwen/settings.json', - 'User settings': 'Benutzereinstellungen', - 'Saved in at ~/.qwen/settings.json': 'Gespeichert in ~/.qwen/settings.json', - 'Add a new rule…': 'Neue Regel hinzufügen…', - 'Add {{type}} permission rule': '{{type}}-Berechtigungsregel hinzufügen', - 'Permission rules are a tool name, optionally followed by a specifier in parentheses.': - 'Berechtigungsregeln sind ein Toolname, optional gefolgt von einem Bezeichner in Klammern.', - 'e.g.,': 'z.B.', - or: 'oder', - 'Enter permission rule…': 'Berechtigungsregel eingeben…', - 'Enter to submit · Esc to cancel': 'Enter zum Absenden · Esc zum Abbrechen', - 'Where should this rule be saved?': 'Wo soll diese Regel gespeichert werden?', - 'Enter to confirm · Esc to cancel': - 'Enter zum Bestätigen · Esc zum Abbrechen', - 'Delete {{type}} rule?': '{{type}}-Regel löschen?', - 'Are you sure you want to delete this permission rule?': - 'Sind Sie sicher, dass Sie diese Berechtigungsregel löschen möchten?', - 'Permissions:': 'Berechtigungen:', - '(←/→ or tab to cycle)': '(←/→ oder Tab zum Wechseln)', - 'Press ↑↓ to navigate · Enter to select · Type to search · Esc to cancel': - '↑↓ navigieren · Enter auswählen · Tippen suchen · Esc abbrechen', - 'Search…': 'Suche…', - 'Use /trust to manage folder trust settings for this workspace.': - 'Verwenden Sie /trust, um die Ordnervertrauenseinstellungen für diesen Arbeitsbereich zu verwalten.', + "Qwen Code fragt nicht, bevor erlaubte Tools verwendet werden.", + "Qwen Code will ask before using these tools.": + "Qwen Code fragt, bevor diese Tools verwendet werden.", + "Qwen Code is not allowed to use denied tools.": + "Qwen Code darf verweigerte Tools nicht verwenden.", + "Manage trusted directories for this workspace.": + "Vertrauenswürdige Verzeichnisse für diesen Arbeitsbereich verwalten.", + "Any use of the {{tool}} tool": "Jede Verwendung des {{tool}}-Tools", + "{{tool}} commands matching '{{pattern}}'": "{{tool}}-Befehle, die '{{pattern}}' entsprechen", + "From user settings": "Aus Benutzereinstellungen", + "From project settings": "Aus Projekteinstellungen", + "From session": "Aus Sitzung", + "Project settings (local)": "Projekteinstellungen (lokal)", + "Saved in .qwen/settings.local.json": "Gespeichert in .qwen/settings.local.json", + "Project settings": "Projekteinstellungen", + "Checked in at .qwen/settings.json": "Eingecheckt in .qwen/settings.json", + "User settings": "Benutzereinstellungen", + "Saved in at ~/.qwen/settings.json": "Gespeichert in ~/.qwen/settings.json", + "Add a new rule…": "Neue Regel hinzufügen…", + "Add {{type}} permission rule": "{{type}}-Berechtigungsregel hinzufügen", + "Permission rules are a tool name, optionally followed by a specifier in parentheses.": + "Berechtigungsregeln sind ein Toolname, optional gefolgt von einem Bezeichner in Klammern.", + "e.g.,": "z.B.", + or: "oder", + "Enter permission rule…": "Berechtigungsregel eingeben…", + "Enter to submit · Esc to cancel": "Enter zum Absenden · Esc zum Abbrechen", + "Where should this rule be saved?": "Wo soll diese Regel gespeichert werden?", + "Enter to confirm · Esc to cancel": "Enter zum Bestätigen · Esc zum Abbrechen", + "Delete {{type}} rule?": "{{type}}-Regel löschen?", + "Are you sure you want to delete this permission rule?": + "Sind Sie sicher, dass Sie diese Berechtigungsregel löschen möchten?", + "Permissions:": "Berechtigungen:", + "(←/→ or tab to cycle)": "(←/→ oder Tab zum Wechseln)", + "Press ↑↓ to navigate · Enter to select · Type to search · Esc to cancel": + "↑↓ navigieren · Enter auswählen · Tippen suchen · Esc abbrechen", + "Search…": "Suche…", + "Use /trust to manage folder trust settings for this workspace.": + "Verwenden Sie /trust, um die Ordnervertrauenseinstellungen für diesen Arbeitsbereich zu verwalten.", // Workspace directory management - 'Add directory…': 'Verzeichnis hinzufügen…', - 'Add directory to workspace': 'Verzeichnis zum Arbeitsbereich hinzufügen', - 'Qwen Code can read files in the workspace, and make edits when auto-accept edits is on.': - 'Qwen Code kann Dateien im Arbeitsbereich lesen und Bearbeitungen vornehmen, wenn die automatische Akzeptierung aktiviert ist.', - 'Qwen Code will be able to read files in this directory and make edits when auto-accept edits is on.': - 'Qwen Code kann Dateien in diesem Verzeichnis lesen und Bearbeitungen vornehmen, wenn die automatische Akzeptierung aktiviert ist.', - 'Enter the path to the directory:': 'Pfad zum Verzeichnis eingeben:', - 'Enter directory path…': 'Verzeichnispfad eingeben…', - 'Tab to complete · Enter to add · Esc to cancel': - 'Tab zum Vervollständigen · Enter zum Hinzufügen · Esc zum Abbrechen', - 'Remove directory?': 'Verzeichnis entfernen?', - 'Are you sure you want to remove this directory from the workspace?': - 'Möchten Sie dieses Verzeichnis wirklich aus dem Arbeitsbereich entfernen?', - ' (Original working directory)': ' (Ursprüngliches Arbeitsverzeichnis)', - ' (from settings)': ' (aus Einstellungen)', - 'Directory does not exist.': 'Verzeichnis existiert nicht.', - 'Path is not a directory.': 'Pfad ist kein Verzeichnis.', - 'This directory is already in the workspace.': - 'Dieses Verzeichnis ist bereits im Arbeitsbereich.', - 'Already covered by existing directory: {{dir}}': - 'Bereits durch vorhandenes Verzeichnis abgedeckt: {{dir}}', + "Add directory…": "Verzeichnis hinzufügen…", + "Add directory to workspace": "Verzeichnis zum Arbeitsbereich hinzufügen", + "Qwen Code can read files in the workspace, and make edits when auto-accept edits is on.": + "Qwen Code kann Dateien im Arbeitsbereich lesen und Bearbeitungen vornehmen, wenn die automatische Akzeptierung aktiviert ist.", + "Qwen Code will be able to read files in this directory and make edits when auto-accept edits is on.": + "Qwen Code kann Dateien in diesem Verzeichnis lesen und Bearbeitungen vornehmen, wenn die automatische Akzeptierung aktiviert ist.", + "Enter the path to the directory:": "Pfad zum Verzeichnis eingeben:", + "Enter directory path…": "Verzeichnispfad eingeben…", + "Tab to complete · Enter to add · Esc to cancel": + "Tab zum Vervollständigen · Enter zum Hinzufügen · Esc zum Abbrechen", + "Remove directory?": "Verzeichnis entfernen?", + "Are you sure you want to remove this directory from the workspace?": + "Möchten Sie dieses Verzeichnis wirklich aus dem Arbeitsbereich entfernen?", + " (Original working directory)": " (Ursprüngliches Arbeitsverzeichnis)", + " (from settings)": " (aus Einstellungen)", + "Directory does not exist.": "Verzeichnis existiert nicht.", + "Path is not a directory.": "Pfad ist kein Verzeichnis.", + "This directory is already in the workspace.": + "Dieses Verzeichnis ist bereits im Arbeitsbereich.", + "Already covered by existing directory: {{dir}}": + "Bereits durch vorhandenes Verzeichnis abgedeckt: {{dir}}", // ============================================================================ // Status Bar // ============================================================================ - 'Using:': 'Verwendet:', - '{{count}} open file': '{{count}} geöffnete Datei', - '{{count}} open files': '{{count}} geöffnete Dateien', - '(ctrl+g to view)': '(Strg+G zum Anzeigen)', - '{{count}} {{name}} file': '{{count}} {{name}}-Datei', - '{{count}} {{name}} files': '{{count}} {{name}}-Dateien', - '{{count}} MCP server': '{{count}} MCP-Server', - '{{count}} MCP servers': '{{count}} MCP-Server', - '{{count}} Blocked': '{{count}} blockiert', - '(ctrl+t to view)': '(Strg+T zum Anzeigen)', - '(ctrl+t to toggle)': '(Strg+T zum Umschalten)', - 'Press Ctrl+C again to exit.': 'Drücken Sie erneut Strg+C zum Beenden.', - 'Press Ctrl+D again to exit.': 'Drücken Sie erneut Strg+D zum Beenden.', - 'Press Esc again to clear.': 'Drücken Sie erneut Esc zum Löschen.', + "Using:": "Verwendet:", + "{{count}} open file": "{{count}} geöffnete Datei", + "{{count}} open files": "{{count}} geöffnete Dateien", + "(ctrl+g to view)": "(Strg+G zum Anzeigen)", + "{{count}} {{name}} file": "{{count}} {{name}}-Datei", + "{{count}} {{name}} files": "{{count}} {{name}}-Dateien", + "{{count}} MCP server": "{{count}} MCP-Server", + "{{count}} MCP servers": "{{count}} MCP-Server", + "{{count}} Blocked": "{{count}} blockiert", + "(ctrl+t to view)": "(Strg+T zum Anzeigen)", + "(ctrl+t to toggle)": "(Strg+T zum Umschalten)", + "Press Ctrl+C again to exit.": "Drücken Sie erneut Strg+C zum Beenden.", + "Press Ctrl+D again to exit.": "Drücken Sie erneut Strg+D zum Beenden.", + "Press Esc again to clear.": "Drücken Sie erneut Esc zum Löschen.", // ============================================================================ // MCP Status // ============================================================================ - 'No MCP servers configured.': 'Keine MCP-Server konfiguriert.', - '⏳ MCP servers are starting up ({{count}} initializing)...': - '⏳ MCP-Server werden gestartet ({{count}} werden initialisiert)...', - 'Note: First startup may take longer. Tool availability will update automatically.': - 'Hinweis: Der erste Start kann länger dauern. Werkzeugverfügbarkeit wird automatisch aktualisiert.', - 'Configured MCP servers:': 'Konfigurierte MCP-Server:', - Ready: 'Bereit', - 'Starting... (first startup may take longer)': - 'Wird gestartet... (erster Start kann länger dauern)', - Disconnected: 'Getrennt', - '{{count}} tool': '{{count}} Werkzeug', - '{{count}} tools': '{{count}} Werkzeuge', - '{{count}} prompt': '{{count}} Prompt', - '{{count}} prompts': '{{count}} Prompts', - '(from {{extensionName}})': '(von {{extensionName}})', - OAuth: 'OAuth', - 'OAuth expired': 'OAuth abgelaufen', - 'OAuth not authenticated': 'OAuth nicht authentifiziert', - 'tools and prompts will appear when ready': - 'Werkzeuge und Prompts werden angezeigt, wenn bereit', - '{{count}} tools cached': '{{count}} Werkzeuge zwischengespeichert', - 'Tools:': 'Werkzeuge:', - 'Parameters:': 'Parameter:', - 'Prompts:': 'Prompts:', - Blocked: 'Blockiert', - '💡 Tips:': '💡 Tipps:', - Use: 'Verwenden', - 'to show server and tool descriptions': - 'um Server- und Werkzeugbeschreibungen anzuzeigen', - 'to show tool parameter schemas': 'um Werkzeug-Parameter-Schemas anzuzeigen', - 'to hide descriptions': 'um Beschreibungen auszublenden', - 'to authenticate with OAuth-enabled servers': - 'um sich bei OAuth-fähigen Servern zu authentifizieren', - Press: 'Drücken Sie', - 'to toggle tool descriptions on/off': - 'um Werkzeugbeschreibungen ein-/auszuschalten', + "No MCP servers configured.": "Keine MCP-Server konfiguriert.", + "⏳ MCP servers are starting up ({{count}} initializing)...": + "⏳ MCP-Server werden gestartet ({{count}} werden initialisiert)...", + "Note: First startup may take longer. Tool availability will update automatically.": + "Hinweis: Der erste Start kann länger dauern. Werkzeugverfügbarkeit wird automatisch aktualisiert.", + "Configured MCP servers:": "Konfigurierte MCP-Server:", + Ready: "Bereit", + "Starting... (first startup may take longer)": + "Wird gestartet... (erster Start kann länger dauern)", + Disconnected: "Getrennt", + "{{count}} tool": "{{count}} Werkzeug", + "{{count}} tools": "{{count}} Werkzeuge", + "{{count}} prompt": "{{count}} Prompt", + "{{count}} prompts": "{{count}} Prompts", + "(from {{extensionName}})": "(von {{extensionName}})", + OAuth: "OAuth", + "OAuth expired": "OAuth abgelaufen", + "OAuth not authenticated": "OAuth nicht authentifiziert", + "tools and prompts will appear when ready": "Werkzeuge und Prompts werden angezeigt, wenn bereit", + "{{count}} tools cached": "{{count}} Werkzeuge zwischengespeichert", + "Tools:": "Werkzeuge:", + "Parameters:": "Parameter:", + "Prompts:": "Prompts:", + Blocked: "Blockiert", + "💡 Tips:": "💡 Tipps:", + Use: "Verwenden", + "to show server and tool descriptions": "um Server- und Werkzeugbeschreibungen anzuzeigen", + "to show tool parameter schemas": "um Werkzeug-Parameter-Schemas anzuzeigen", + "to hide descriptions": "um Beschreibungen auszublenden", + "to authenticate with OAuth-enabled servers": + "um sich bei OAuth-fähigen Servern zu authentifizieren", + Press: "Drücken Sie", + "to toggle tool descriptions on/off": "um Werkzeugbeschreibungen ein-/auszuschalten", "Starting OAuth authentication for MCP server '{{name}}'...": "OAuth-Authentifizierung für MCP-Server '{{name}}' wird gestartet...", - 'Restarting MCP servers...': 'MCP-Server werden neu gestartet...', + "Restarting MCP servers...": "MCP-Server werden neu gestartet...", // ============================================================================ // Startup Tips // ============================================================================ - 'Tips for getting started:': 'Tipps zum Einstieg:', - '1. Ask questions, edit files, or run commands.': - '1. Stellen Sie Fragen, bearbeiten Sie Dateien oder führen Sie Befehle aus.', - '2. Be specific for the best results.': - '2. Seien Sie spezifisch für die besten Ergebnisse.', - 'files to customize your interactions with Qwen Code.': - 'Dateien, um Ihre Interaktionen mit Qwen Code anzupassen.', - 'for more information.': 'für weitere Informationen.', + "Tips for getting started:": "Tipps zum Einstieg:", + "1. Ask questions, edit files, or run commands.": + "1. Stellen Sie Fragen, bearbeiten Sie Dateien oder führen Sie Befehle aus.", + "2. Be specific for the best results.": "2. Seien Sie spezifisch für die besten Ergebnisse.", + "files to customize your interactions with Qwen Code.": + "Dateien, um Ihre Interaktionen mit Qwen Code anzupassen.", + "for more information.": "für weitere Informationen.", // ============================================================================ // Exit Screen / Stats // ============================================================================ - 'Agent powering down. Goodbye!': - 'Agent wird heruntergefahren. Auf Wiedersehen!', - 'To continue this session, run': - 'Um diese Sitzung fortzusetzen, führen Sie aus', - 'Interaction Summary': 'Interaktionszusammenfassung', - 'Session ID:': 'Sitzungs-ID:', - 'Tool Calls:': 'Werkzeugaufrufe:', - 'Success Rate:': 'Erfolgsrate:', - 'User Agreement:': 'Benutzerzustimmung:', - reviewed: 'überprüft', - 'Code Changes:': 'Codeänderungen:', - Performance: 'Leistung', - 'Wall Time:': 'Gesamtzeit:', - 'Agent Active:': 'Agent aktiv:', - 'API Time:': 'API-Zeit:', - 'Tool Time:': 'Werkzeugzeit:', - 'Session Stats': 'Sitzungsstatistiken', - 'Model Usage': 'Modellnutzung', - Reqs: 'Anfragen', - 'Input Tokens': 'Eingabe-Token', - 'Output Tokens': 'Ausgabe-Token', - 'Savings Highlight:': 'Einsparungen:', - 'of input tokens were served from the cache, reducing costs.': - 'der Eingabe-Token wurden aus dem Cache bedient, was die Kosten reduziert.', - 'Tip: For a full token breakdown, run `/stats model`.': - 'Tipp: Für eine vollständige Token-Aufschlüsselung führen Sie `/stats model` aus.', - 'Model Stats For Nerds': 'Modellstatistiken für Nerds', - 'Tool Stats For Nerds': 'Werkzeugstatistiken für Nerds', - Metric: 'Metrik', - API: 'API', - Requests: 'Anfragen', - Errors: 'Fehler', - 'Avg Latency': 'Durchschn. Latenz', - Tokens: 'Token', - Total: 'Gesamt', - Prompt: 'Prompt', - Cached: 'Zwischengespeichert', - Thoughts: 'Gedanken', - Tool: 'Werkzeug', - Output: 'Ausgabe', - 'No API calls have been made in this session.': - 'In dieser Sitzung wurden keine API-Aufrufe gemacht.', - 'Tool Name': 'Werkzeugname', - Calls: 'Aufrufe', - 'Success Rate': 'Erfolgsrate', - 'Avg Duration': 'Durchschn. Dauer', - 'User Decision Summary': 'Benutzerentscheidungs-Zusammenfassung', - 'Total Reviewed Suggestions:': 'Insgesamt überprüfter Vorschläge:', - ' » Accepted:': ' » Akzeptiert:', - ' » Rejected:': ' » Abgelehnt:', - ' » Modified:': ' » Geändert:', - ' Overall Agreement Rate:': ' Gesamtzustimmungsrate:', - 'No tool calls have been made in this session.': - 'In dieser Sitzung wurden keine Werkzeugaufrufe gemacht.', - 'Session start time is unavailable, cannot calculate stats.': - 'Sitzungsstartzeit nicht verfügbar, Statistiken können nicht berechnet werden.', + "Agent powering down. Goodbye!": "Agent wird heruntergefahren. Auf Wiedersehen!", + "To continue this session, run": "Um diese Sitzung fortzusetzen, führen Sie aus", + "Interaction Summary": "Interaktionszusammenfassung", + "Session ID:": "Sitzungs-ID:", + "Tool Calls:": "Werkzeugaufrufe:", + "Success Rate:": "Erfolgsrate:", + "User Agreement:": "Benutzerzustimmung:", + reviewed: "überprüft", + "Code Changes:": "Codeänderungen:", + Performance: "Leistung", + "Wall Time:": "Gesamtzeit:", + "Agent Active:": "Agent aktiv:", + "API Time:": "API-Zeit:", + "Tool Time:": "Werkzeugzeit:", + "Session Stats": "Sitzungsstatistiken", + "Model Usage": "Modellnutzung", + Reqs: "Anfragen", + "Input Tokens": "Eingabe-Token", + "Output Tokens": "Ausgabe-Token", + "Savings Highlight:": "Einsparungen:", + "of input tokens were served from the cache, reducing costs.": + "der Eingabe-Token wurden aus dem Cache bedient, was die Kosten reduziert.", + "Tip: For a full token breakdown, run `/stats model`.": + "Tipp: Für eine vollständige Token-Aufschlüsselung führen Sie `/stats model` aus.", + "Model Stats For Nerds": "Modellstatistiken für Nerds", + "Tool Stats For Nerds": "Werkzeugstatistiken für Nerds", + Metric: "Metrik", + API: "API", + Requests: "Anfragen", + Errors: "Fehler", + "Avg Latency": "Durchschn. Latenz", + Tokens: "Token", + Total: "Gesamt", + Prompt: "Prompt", + Cached: "Zwischengespeichert", + Thoughts: "Gedanken", + Tool: "Werkzeug", + Output: "Ausgabe", + "No API calls have been made in this session.": + "In dieser Sitzung wurden keine API-Aufrufe gemacht.", + "Tool Name": "Werkzeugname", + Calls: "Aufrufe", + "Success Rate": "Erfolgsrate", + "Avg Duration": "Durchschn. Dauer", + "User Decision Summary": "Benutzerentscheidungs-Zusammenfassung", + "Total Reviewed Suggestions:": "Insgesamt überprüfter Vorschläge:", + " » Accepted:": " » Akzeptiert:", + " » Rejected:": " » Abgelehnt:", + " » Modified:": " » Geändert:", + " Overall Agreement Rate:": " Gesamtzustimmungsrate:", + "No tool calls have been made in this session.": + "In dieser Sitzung wurden keine Werkzeugaufrufe gemacht.", + "Session start time is unavailable, cannot calculate stats.": + "Sitzungsstartzeit nicht verfügbar, Statistiken können nicht berechnet werden.", // ============================================================================ // Command Format Migration // ============================================================================ - 'Command Format Migration': 'Befehlsformat-Migration', - 'Found {{count}} TOML command file:': '{{count}} TOML-Befehlsdatei gefunden:', - 'Found {{count}} TOML command files:': - '{{count}} TOML-Befehlsdateien gefunden:', - '... and {{count}} more': '... und {{count}} weitere', - 'The TOML format is deprecated. Would you like to migrate them to Markdown format?': - 'Das TOML-Format ist veraltet. Möchten Sie sie ins Markdown-Format migrieren?', - '(Backups will be created and original files will be preserved)': - '(Backups werden erstellt und Originaldateien werden beibehalten)', + "Command Format Migration": "Befehlsformat-Migration", + "Found {{count}} TOML command file:": "{{count}} TOML-Befehlsdatei gefunden:", + "Found {{count}} TOML command files:": "{{count}} TOML-Befehlsdateien gefunden:", + "... and {{count}} more": "... und {{count}} weitere", + "The TOML format is deprecated. Would you like to migrate them to Markdown format?": + "Das TOML-Format ist veraltet. Möchten Sie sie ins Markdown-Format migrieren?", + "(Backups will be created and original files will be preserved)": + "(Backups werden erstellt und Originaldateien werden beibehalten)", // ============================================================================ // Loading Phrases // ============================================================================ - 'Waiting for user confirmation...': 'Warten auf Benutzerbestätigung...', - '(esc to cancel, {{time}})': '(Esc zum Abbrechen, {{time}})', + "Waiting for user confirmation...": "Warten auf Benutzerbestätigung...", + "(esc to cancel, {{time}})": "(Esc zum Abbrechen, {{time}})", // ============================================================================ // Loading Phrases // ============================================================================ WITTY_LOADING_PHRASES: [ - 'Auf gut Glück!', - 'Genialität wird ausgeliefert...', - 'Die Serifen werden aufgemalt...', - 'Durch den Schleimpilz navigieren...', - 'Die digitalen Geister werden befragt...', - 'Splines werden retikuliert...', - 'Die KI-Hamster werden aufgewärmt...', - 'Die Zaubermuschel wird befragt...', - 'Witzige Erwiderung wird generiert...', - 'Die Algorithmen werden poliert...', - 'Perfektion braucht Zeit (mein Code auch)...', - 'Frische Bytes werden gebrüht...', - 'Elektronen werden gezählt...', - 'Kognitive Prozessoren werden aktiviert...', - 'Auf Syntaxfehler im Universum wird geprüft...', - 'Einen Moment, Humor wird optimiert...', - 'Pointen werden gemischt...', - 'Neuronale Netze werden entwirrt...', - 'Brillanz wird kompiliert...', - 'wit.exe wird geladen...', - 'Die Wolke der Weisheit wird beschworen...', - 'Eine witzige Antwort wird vorbereitet...', - 'Einen Moment, ich debugge die Realität...', - 'Die Optionen werden verwirrt...', - 'Kosmische Frequenzen werden eingestellt...', - 'Eine Antwort wird erstellt, die Ihrer Geduld würdig ist...', - 'Die Einsen und Nullen werden kompiliert...', - 'Abhängigkeiten werden aufgelöst... und existenzielle Krisen...', - 'Erinnerungen werden defragmentiert... sowohl RAM als auch persönliche...', - 'Das Humor-Modul wird neu gestartet...', - 'Das Wesentliche wird zwischengespeichert (hauptsächlich Katzen-Memes)...', - 'Für lächerliche Geschwindigkeit wird optimiert', - 'Bits werden getauscht... sagen Sie es nicht den Bytes...', - 'Garbage Collection läuft... bin gleich zurück...', - 'Das Internet wird zusammengebaut...', - 'Kaffee wird in Code umgewandelt...', - 'Die Syntax der Realität wird aktualisiert...', - 'Die Synapsen werden neu verdrahtet...', - 'Ein verlegtes Semikolon wird gesucht...', - 'Die Zahnräder werden geschmiert...', - 'Die Server werden vorgeheizt...', - 'Der Fluxkompensator wird kalibriert...', - 'Der Unwahrscheinlichkeitsantrieb wird aktiviert...', - 'Die Macht wird kanalisiert...', - 'Die Sterne werden für optimale Antwort ausgerichtet...', - 'So sagen wir alle...', - 'Die nächste große Idee wird geladen...', - 'Einen Moment, ich bin in der Zone...', - 'Bereite mich vor, Sie mit Brillanz zu blenden...', - 'Einen Augenblick, ich poliere meinen Witz...', - 'Halten Sie durch, ich erschaffe ein Meisterwerk...', - 'Einen Moment, ich debugge das Universum...', - 'Einen Moment, ich richte die Pixel aus...', - 'Einen Moment, ich optimiere den Humor...', - 'Einen Moment, ich tune die Algorithmen...', - 'Warp-Geschwindigkeit aktiviert...', - 'Mehr Dilithium-Kristalle werden gesucht...', - 'Keine Panik...', - 'Dem weißen Kaninchen wird gefolgt...', - 'Die Wahrheit ist hier drin... irgendwo...', - 'Auf die Kassette wird gepustet...', - 'Ladevorgang... Machen Sie eine Fassrolle!', - 'Auf den Respawn wird gewartet...', - 'Der Kessel-Flug wird in weniger als 12 Parsec beendet...', - 'Der Kuchen ist keine Lüge, er lädt nur noch...', - 'Am Charaktererstellungsbildschirm wird herumgefummelt...', - 'Einen Moment, ich suche das richtige Meme...', + "Auf gut Glück!", + "Genialität wird ausgeliefert...", + "Die Serifen werden aufgemalt...", + "Durch den Schleimpilz navigieren...", + "Die digitalen Geister werden befragt...", + "Splines werden retikuliert...", + "Die KI-Hamster werden aufgewärmt...", + "Die Zaubermuschel wird befragt...", + "Witzige Erwiderung wird generiert...", + "Die Algorithmen werden poliert...", + "Perfektion braucht Zeit (mein Code auch)...", + "Frische Bytes werden gebrüht...", + "Elektronen werden gezählt...", + "Kognitive Prozessoren werden aktiviert...", + "Auf Syntaxfehler im Universum wird geprüft...", + "Einen Moment, Humor wird optimiert...", + "Pointen werden gemischt...", + "Neuronale Netze werden entwirrt...", + "Brillanz wird kompiliert...", + "wit.exe wird geladen...", + "Die Wolke der Weisheit wird beschworen...", + "Eine witzige Antwort wird vorbereitet...", + "Einen Moment, ich debugge die Realität...", + "Die Optionen werden verwirrt...", + "Kosmische Frequenzen werden eingestellt...", + "Eine Antwort wird erstellt, die Ihrer Geduld würdig ist...", + "Die Einsen und Nullen werden kompiliert...", + "Abhängigkeiten werden aufgelöst... und existenzielle Krisen...", + "Erinnerungen werden defragmentiert... sowohl RAM als auch persönliche...", + "Das Humor-Modul wird neu gestartet...", + "Das Wesentliche wird zwischengespeichert (hauptsächlich Katzen-Memes)...", + "Für lächerliche Geschwindigkeit wird optimiert", + "Bits werden getauscht... sagen Sie es nicht den Bytes...", + "Garbage Collection läuft... bin gleich zurück...", + "Das Internet wird zusammengebaut...", + "Kaffee wird in Code umgewandelt...", + "Die Syntax der Realität wird aktualisiert...", + "Die Synapsen werden neu verdrahtet...", + "Ein verlegtes Semikolon wird gesucht...", + "Die Zahnräder werden geschmiert...", + "Die Server werden vorgeheizt...", + "Der Fluxkompensator wird kalibriert...", + "Der Unwahrscheinlichkeitsantrieb wird aktiviert...", + "Die Macht wird kanalisiert...", + "Die Sterne werden für optimale Antwort ausgerichtet...", + "So sagen wir alle...", + "Die nächste große Idee wird geladen...", + "Einen Moment, ich bin in der Zone...", + "Bereite mich vor, Sie mit Brillanz zu blenden...", + "Einen Augenblick, ich poliere meinen Witz...", + "Halten Sie durch, ich erschaffe ein Meisterwerk...", + "Einen Moment, ich debugge das Universum...", + "Einen Moment, ich richte die Pixel aus...", + "Einen Moment, ich optimiere den Humor...", + "Einen Moment, ich tune die Algorithmen...", + "Warp-Geschwindigkeit aktiviert...", + "Mehr Dilithium-Kristalle werden gesucht...", + "Keine Panik...", + "Dem weißen Kaninchen wird gefolgt...", + "Die Wahrheit ist hier drin... irgendwo...", + "Auf die Kassette wird gepustet...", + "Ladevorgang... Machen Sie eine Fassrolle!", + "Auf den Respawn wird gewartet...", + "Der Kessel-Flug wird in weniger als 12 Parsec beendet...", + "Der Kuchen ist keine Lüge, er lädt nur noch...", + "Am Charaktererstellungsbildschirm wird herumgefummelt...", + "Einen Moment, ich suche das richtige Meme...", "'A' wird zum Fortfahren gedrückt...", - 'Digitale Katzen werden gehütet...', - 'Die Pixel werden poliert...', - 'Ein passender Ladebildschirm-Witz wird gesucht...', - 'Ich lenke Sie mit diesem witzigen Spruch ab...', - 'Fast da... wahrscheinlich...', - 'Unsere Hamster arbeiten so schnell sie können...', - 'Cloudy wird am Kopf gestreichelt...', - 'Die Katze wird gestreichelt...', - 'Meinen Chef rickrollen...', - 'Never gonna give you up, never gonna let you down...', - 'Auf den Bass wird geschlagen...', - 'Die Schnozbeeren werden probiert...', + "Digitale Katzen werden gehütet...", + "Die Pixel werden poliert...", + "Ein passender Ladebildschirm-Witz wird gesucht...", + "Ich lenke Sie mit diesem witzigen Spruch ab...", + "Fast da... wahrscheinlich...", + "Unsere Hamster arbeiten so schnell sie können...", + "Cloudy wird am Kopf gestreichelt...", + "Die Katze wird gestreichelt...", + "Meinen Chef rickrollen...", + "Never gonna give you up, never gonna let you down...", + "Auf den Bass wird geschlagen...", + "Die Schnozbeeren werden probiert...", "I'm going the distance, I'm going for speed...", - 'Ist dies das wahre Leben? Ist dies nur Fantasie?...', - 'Ich habe ein gutes Gefühl dabei...', - 'Den Bären wird gestupst...', - 'Recherche zu den neuesten Memes...', - 'Überlege, wie ich das witziger machen kann...', - 'Hmmm... lassen Sie mich nachdenken...', - 'Wie nennt man einen Fisch ohne Augen? Ein Fsh...', - 'Warum ging der Computer zur Therapie? Er hatte zu viele Bytes...', - 'Warum mögen Programmierer keine Natur? Sie hat zu viele Bugs...', - 'Warum bevorzugen Programmierer den Dunkelmodus? Weil Licht Bugs anzieht...', - 'Warum ging der Entwickler pleite? Er hat seinen ganzen Cache aufgebraucht...', - 'Was kann man mit einem kaputten Bleistift machen? Nichts, er ist sinnlos...', - 'Perkussive Wartung wird angewendet...', - 'Die richtige USB-Ausrichtung wird gesucht...', - 'Es wird sichergestellt, dass der magische Rauch in den Kabeln bleibt...', - 'Versuche Vim zu beenden...', - 'Das Hamsterrad wird angeworfen...', - 'Das ist kein Bug, das ist ein undokumentiertes Feature...', - 'Engage.', - 'Ich komme wieder... mit einer Antwort.', - 'Mein anderer Prozess ist eine TARDIS...', - 'Mit dem Maschinengeist wird kommuniziert...', - 'Die Gedanken marinieren lassen...', - 'Gerade erinnert, wo ich meine Schlüssel hingelegt habe...', - 'Über die Kugel wird nachgedacht...', - 'Ich habe Dinge gesehen, die Sie nicht glauben würden... wie einen Benutzer, der Lademeldungen liest.', - 'Nachdenklicher Blick wird initiiert...', - 'Was ist der Lieblingssnack eines Computers? Mikrochips.', - 'Warum tragen Java-Entwickler Brillen? Weil sie nicht C#.', - 'Der Laser wird aufgeladen... pew pew!', - 'Durch Null wird geteilt... nur Spaß!', - 'Suche nach einem erwachsenen Aufseh... ich meine, Verarbeitung.', - 'Es piept und boopt.', - 'Pufferung... weil auch KIs einen Moment brauchen.', - 'Quantenteilchen werden für schnellere Antwort verschränkt...', - 'Das Chrom wird poliert... an den Algorithmen.', - 'Sind Sie nicht unterhalten? (Arbeite daran!)', - 'Die Code-Gremlins werden beschworen... zum Helfen, natürlich.', - 'Warte nur auf das Einwahlton-Ende...', - 'Das Humor-O-Meter wird neu kalibriert.', - 'Mein anderer Ladebildschirm ist noch lustiger.', - 'Ziemlich sicher, dass irgendwo eine Katze über die Tastatur läuft...', - 'Verbessern... Verbessern... Lädt noch.', - 'Das ist kein Bug, das ist ein Feature... dieses Ladebildschirms.', - 'Haben Sie versucht, es aus- und wieder einzuschalten? (Den Ladebildschirm, nicht mich.)', - 'Zusätzliche Pylonen werden gebaut...', + "Ist dies das wahre Leben? Ist dies nur Fantasie?...", + "Ich habe ein gutes Gefühl dabei...", + "Den Bären wird gestupst...", + "Recherche zu den neuesten Memes...", + "Überlege, wie ich das witziger machen kann...", + "Hmmm... lassen Sie mich nachdenken...", + "Wie nennt man einen Fisch ohne Augen? Ein Fsh...", + "Warum ging der Computer zur Therapie? Er hatte zu viele Bytes...", + "Warum mögen Programmierer keine Natur? Sie hat zu viele Bugs...", + "Warum bevorzugen Programmierer den Dunkelmodus? Weil Licht Bugs anzieht...", + "Warum ging der Entwickler pleite? Er hat seinen ganzen Cache aufgebraucht...", + "Was kann man mit einem kaputten Bleistift machen? Nichts, er ist sinnlos...", + "Perkussive Wartung wird angewendet...", + "Die richtige USB-Ausrichtung wird gesucht...", + "Es wird sichergestellt, dass der magische Rauch in den Kabeln bleibt...", + "Versuche Vim zu beenden...", + "Das Hamsterrad wird angeworfen...", + "Das ist kein Bug, das ist ein undokumentiertes Feature...", + "Engage.", + "Ich komme wieder... mit einer Antwort.", + "Mein anderer Prozess ist eine TARDIS...", + "Mit dem Maschinengeist wird kommuniziert...", + "Die Gedanken marinieren lassen...", + "Gerade erinnert, wo ich meine Schlüssel hingelegt habe...", + "Über die Kugel wird nachgedacht...", + "Ich habe Dinge gesehen, die Sie nicht glauben würden... wie einen Benutzer, der Lademeldungen liest.", + "Nachdenklicher Blick wird initiiert...", + "Was ist der Lieblingssnack eines Computers? Mikrochips.", + "Warum tragen Java-Entwickler Brillen? Weil sie nicht C#.", + "Der Laser wird aufgeladen... pew pew!", + "Durch Null wird geteilt... nur Spaß!", + "Suche nach einem erwachsenen Aufseh... ich meine, Verarbeitung.", + "Es piept und boopt.", + "Pufferung... weil auch KIs einen Moment brauchen.", + "Quantenteilchen werden für schnellere Antwort verschränkt...", + "Das Chrom wird poliert... an den Algorithmen.", + "Sind Sie nicht unterhalten? (Arbeite daran!)", + "Die Code-Gremlins werden beschworen... zum Helfen, natürlich.", + "Warte nur auf das Einwahlton-Ende...", + "Das Humor-O-Meter wird neu kalibriert.", + "Mein anderer Ladebildschirm ist noch lustiger.", + "Ziemlich sicher, dass irgendwo eine Katze über die Tastatur läuft...", + "Verbessern... Verbessern... Lädt noch.", + "Das ist kein Bug, das ist ein Feature... dieses Ladebildschirms.", + "Haben Sie versucht, es aus- und wieder einzuschalten? (Den Ladebildschirm, nicht mich.)", + "Zusätzliche Pylonen werden gebaut...", ], // ============================================================================ // Extension Settings Input // ============================================================================ - 'Enter value...': 'Wert eingeben...', - 'Enter sensitive value...': 'Sensiblen Wert eingeben...', - 'Press Enter to submit, Escape to cancel': - 'Enter zum Absenden, Escape zum Abbrechen drücken', + "Enter value...": "Wert eingeben...", + "Enter sensitive value...": "Sensiblen Wert eingeben...", + "Press Enter to submit, Escape to cancel": "Enter zum Absenden, Escape zum Abbrechen drücken", // ============================================================================ // Command Migration Tool // ============================================================================ - 'Markdown file already exists: {{filename}}': - 'Markdown-Datei existiert bereits: {{filename}}', - 'TOML Command Format Deprecation Notice': - 'TOML-Befehlsformat Veraltet-Hinweis', - 'Found {{count}} command file(s) in TOML format:': - '{{count}} Befehlsdatei(en) im TOML-Format gefunden:', - 'The TOML format for commands is being deprecated in favor of Markdown format.': - 'Das TOML-Format für Befehle wird zugunsten des Markdown-Formats eingestellt.', - 'Markdown format is more readable and easier to edit.': - 'Das Markdown-Format ist lesbarer und einfacher zu bearbeiten.', - 'You can migrate these files automatically using:': - 'Sie können diese Dateien automatisch migrieren mit:', - 'Or manually convert each file:': 'Oder jede Datei manuell konvertieren:', - 'TOML: prompt = "..." / description = "..."': - 'TOML: prompt = "..." / description = "..."', - 'Markdown: YAML frontmatter + content': 'Markdown: YAML-Frontmatter + Inhalt', - 'The migration tool will:': 'Das Migrationstool wird:', - 'Convert TOML files to Markdown': 'TOML-Dateien in Markdown konvertieren', - 'Create backups of original files': - 'Sicherungen der Originaldateien erstellen', - 'Preserve all command functionality': 'Alle Befehlsfunktionen beibehalten', - 'TOML format will continue to work for now, but migration is recommended.': - 'Das TOML-Format funktioniert vorerst weiter, aber eine Migration wird empfohlen.', + "Markdown file already exists: {{filename}}": "Markdown-Datei existiert bereits: {{filename}}", + "TOML Command Format Deprecation Notice": "TOML-Befehlsformat Veraltet-Hinweis", + "Found {{count}} command file(s) in TOML format:": + "{{count}} Befehlsdatei(en) im TOML-Format gefunden:", + "The TOML format for commands is being deprecated in favor of Markdown format.": + "Das TOML-Format für Befehle wird zugunsten des Markdown-Formats eingestellt.", + "Markdown format is more readable and easier to edit.": + "Das Markdown-Format ist lesbarer und einfacher zu bearbeiten.", + "You can migrate these files automatically using:": + "Sie können diese Dateien automatisch migrieren mit:", + "Or manually convert each file:": "Oder jede Datei manuell konvertieren:", + 'TOML: prompt = "..." / description = "..."': 'TOML: prompt = "..." / description = "..."', + "Markdown: YAML frontmatter + content": "Markdown: YAML-Frontmatter + Inhalt", + "The migration tool will:": "Das Migrationstool wird:", + "Convert TOML files to Markdown": "TOML-Dateien in Markdown konvertieren", + "Create backups of original files": "Sicherungen der Originaldateien erstellen", + "Preserve all command functionality": "Alle Befehlsfunktionen beibehalten", + "TOML format will continue to work for now, but migration is recommended.": + "Das TOML-Format funktioniert vorerst weiter, aber eine Migration wird empfohlen.", // ============================================================================ // Extensions - Explore Command // ============================================================================ - 'Open extensions page in your browser': 'Erweiterungsseite im Browser öffnen', - 'Unknown extensions source: {{source}}.': - 'Unbekannte Erweiterungsquelle: {{source}}.', - 'Would open extensions page in your browser: {{url}} (skipped in test environment)': - 'Würde Erweiterungsseite im Browser öffnen: {{url}} (übersprungen in Testumgebung)', - 'View available extensions at {{url}}': - 'Verfügbare Erweiterungen ansehen unter {{url}}', - 'Opening extensions page in your browser: {{url}}': - 'Erweiterungsseite wird im Browser geöffnet: {{url}}', - 'Failed to open browser. Check out the extensions gallery at {{url}}': - 'Browser konnte nicht geöffnet werden. Besuchen Sie die Erweiterungsgalerie unter {{url}}', - 'Use /compress when the conversation gets long to summarize history and free up context.': - 'Verwenden Sie /compress, wenn die Unterhaltung lang wird, um den Verlauf zusammenzufassen und Kontext freizugeben.', - 'Start a fresh idea with /clear or /new; the previous session stays available in history.': - 'Starten Sie eine neue Idee mit /clear oder /new; die vorherige Sitzung bleibt im Verlauf verfügbar.', - 'Use /bug to submit issues to the maintainers when something goes off.': - 'Verwenden Sie /bug, um Probleme an die Betreuer zu melden, wenn etwas schiefgeht.', - 'Switch auth type quickly with /auth.': - 'Wechseln Sie den Authentifizierungstyp schnell mit /auth.', - 'You can run any shell commands from Qwen Code using ! (e.g. !ls).': - 'Sie können beliebige Shell-Befehle in Qwen Code mit ! ausführen (z. B. !ls).', - 'Type / to open the command popup; Tab autocompletes slash commands and saved prompts.': - 'Geben Sie / ein, um das Befehlsmenü zu öffnen; Tab vervollständigt Slash-Befehle und gespeicherte Prompts.', - 'You can resume a previous conversation by running qwen --continue or qwen --resume.': - 'Sie können eine frühere Unterhaltung mit qwen --continue oder qwen --resume fortsetzen.', - 'You can switch permission mode quickly with Shift+Tab or /approval-mode.': - 'Sie können den Berechtigungsmodus schnell mit Shift+Tab oder /approval-mode wechseln.', - 'You can switch permission mode quickly with Tab or /approval-mode.': - 'Sie können den Berechtigungsmodus schnell mit Tab oder /approval-mode wechseln.', - 'Try /insight to generate personalized insights from your chat history.': - 'Probieren Sie /insight, um personalisierte Erkenntnisse aus Ihrem Chatverlauf zu erstellen.', + "Open extensions page in your browser": "Erweiterungsseite im Browser öffnen", + "Unknown extensions source: {{source}}.": "Unbekannte Erweiterungsquelle: {{source}}.", + "Would open extensions page in your browser: {{url}} (skipped in test environment)": + "Würde Erweiterungsseite im Browser öffnen: {{url}} (übersprungen in Testumgebung)", + "View available extensions at {{url}}": "Verfügbare Erweiterungen ansehen unter {{url}}", + "Opening extensions page in your browser: {{url}}": + "Erweiterungsseite wird im Browser geöffnet: {{url}}", + "Failed to open browser. Check out the extensions gallery at {{url}}": + "Browser konnte nicht geöffnet werden. Besuchen Sie die Erweiterungsgalerie unter {{url}}", + "Use /compress when the conversation gets long to summarize history and free up context.": + "Verwenden Sie /compress, wenn die Unterhaltung lang wird, um den Verlauf zusammenzufassen und Kontext freizugeben.", + "Start a fresh idea with /clear or /new; the previous session stays available in history.": + "Starten Sie eine neue Idee mit /clear oder /new; die vorherige Sitzung bleibt im Verlauf verfügbar.", + "Use /bug to submit issues to the maintainers when something goes off.": + "Verwenden Sie /bug, um Probleme an die Betreuer zu melden, wenn etwas schiefgeht.", + "Switch auth type quickly with /auth.": + "Wechseln Sie den Authentifizierungstyp schnell mit /auth.", + "You can run any shell commands from Qwen Code using ! (e.g. !ls).": + "Sie können beliebige Shell-Befehle in Qwen Code mit ! ausführen (z. B. !ls).", + "Type / to open the command popup; Tab autocompletes slash commands and saved prompts.": + "Geben Sie / ein, um das Befehlsmenü zu öffnen; Tab vervollständigt Slash-Befehle und gespeicherte Prompts.", + "You can resume a previous conversation by running qwen --continue or qwen --resume.": + "Sie können eine frühere Unterhaltung mit qwen --continue oder qwen --resume fortsetzen.", + "You can switch permission mode quickly with Shift+Tab or /approval-mode.": + "Sie können den Berechtigungsmodus schnell mit Shift+Tab oder /approval-mode wechseln.", + "You can switch permission mode quickly with Tab or /approval-mode.": + "Sie können den Berechtigungsmodus schnell mit Tab oder /approval-mode wechseln.", + "Try /insight to generate personalized insights from your chat history.": + "Probieren Sie /insight, um personalisierte Erkenntnisse aus Ihrem Chatverlauf zu erstellen.", // ============================================================================ // Custom API Key Configuration // ============================================================================ - 'You can configure your API key and models in settings.json': - 'Sie können Ihren API-Schlüssel und Modelle in settings.json konfigurieren', - 'Refer to the documentation for setup instructions': - 'Einrichtungsanweisungen finden Sie in der Dokumentation', + "You can configure your API key and models in settings.json": + "Sie können Ihren API-Schlüssel und Modelle in settings.json konfigurieren", + "Refer to the documentation for setup instructions": + "Einrichtungsanweisungen finden Sie in der Dokumentation", // ============================================================================ // Coding Plan Authentication // ============================================================================ - 'API key cannot be empty.': 'API-Schlüssel darf nicht leer sein.', - 'You can get your Coding Plan API key here': - 'Sie können Ihren Coding-Plan-API-Schlüssel hier erhalten', - 'New model configurations are available for Alibaba Cloud Coding Plan. Update now?': - 'Neue Modellkonfigurationen sind für Alibaba Cloud Coding Plan verfügbar. Jetzt aktualisieren?', - 'Coding Plan configuration updated successfully. New models are now available.': - 'Coding Plan-Konfiguration erfolgreich aktualisiert. Neue Modelle sind jetzt verfügbar.', - 'Coding Plan API key not found. Please re-authenticate with Coding Plan.': - 'Coding Plan API-Schlüssel nicht gefunden. Bitte authentifizieren Sie sich erneut mit Coding Plan.', - 'Failed to update Coding Plan configuration: {{message}}': - 'Fehler beim Aktualisieren der Coding Plan-Konfiguration: {{message}}', + "API key cannot be empty.": "API-Schlüssel darf nicht leer sein.", + "You can get your Coding Plan API key here": + "Sie können Ihren Coding-Plan-API-Schlüssel hier erhalten", + "New model configurations are available for Alibaba Cloud Coding Plan. Update now?": + "Neue Modellkonfigurationen sind für Alibaba Cloud Coding Plan verfügbar. Jetzt aktualisieren?", + "Coding Plan configuration updated successfully. New models are now available.": + "Coding Plan-Konfiguration erfolgreich aktualisiert. Neue Modelle sind jetzt verfügbar.", + "Coding Plan API key not found. Please re-authenticate with Coding Plan.": + "Coding Plan API-Schlüssel nicht gefunden. Bitte authentifizieren Sie sich erneut mit Coding Plan.", + "Failed to update Coding Plan configuration: {{message}}": + "Fehler beim Aktualisieren der Coding Plan-Konfiguration: {{message}}", // ============================================================================ // Auth Dialog - View Titles and Labels // ============================================================================ - 'Coding Plan': 'Coding Plan', + "Coding Plan": "Coding Plan", "Paste your api key of ModelStudio Coding Plan and you're all set!": - 'Fügen Sie Ihren ModelStudio Coding Plan API-Schlüssel ein und Sie sind bereit!', - Custom: 'Benutzerdefiniert', - 'More instructions about configuring `modelProviders` manually.': - 'Weitere Anweisungen zur manuellen Konfiguration von `modelProviders`.', - 'Select API-KEY configuration mode:': - 'API-KEY-Konfigurationsmodus auswählen:', - '(Press Escape to go back)': '(Escape drücken zum Zurückgehen)', - '(Press Enter to submit, Escape to cancel)': - '(Enter zum Absenden, Escape zum Abbrechen)', - 'More instructions please check:': 'Weitere Anweisungen finden Sie unter:', - 'Select Region for Coding Plan': 'Region für Coding Plan auswählen', - 'Choose based on where your account is registered': - 'Wählen Sie basierend auf dem Registrierungsort Ihres Kontos', - 'Enter Coding Plan API Key': 'Coding-Plan-API-Schlüssel eingeben', + "Fügen Sie Ihren ModelStudio Coding Plan API-Schlüssel ein und Sie sind bereit!", + Custom: "Benutzerdefiniert", + "More instructions about configuring `modelProviders` manually.": + "Weitere Anweisungen zur manuellen Konfiguration von `modelProviders`.", + "Select API-KEY configuration mode:": "API-KEY-Konfigurationsmodus auswählen:", + "(Press Escape to go back)": "(Escape drücken zum Zurückgehen)", + "(Press Enter to submit, Escape to cancel)": "(Enter zum Absenden, Escape zum Abbrechen)", + "More instructions please check:": "Weitere Anweisungen finden Sie unter:", + "Select Region for Coding Plan": "Region für Coding Plan auswählen", + "Choose based on where your account is registered": + "Wählen Sie basierend auf dem Registrierungsort Ihres Kontos", + "Enter Coding Plan API Key": "Coding-Plan-API-Schlüssel eingeben", // ============================================================================ // Coding Plan International Updates // ============================================================================ - 'New model configurations are available for {{region}}. Update now?': - 'Neue Modellkonfigurationen sind für {{region}} verfügbar. Jetzt aktualisieren?', + "New model configurations are available for {{region}}. Update now?": + "Neue Modellkonfigurationen sind für {{region}} verfügbar. Jetzt aktualisieren?", '{{region}} configuration updated successfully. Model switched to "{{model}}".': '{{region}}-Konfiguration erfolgreich aktualisiert. Modell auf "{{model}}" umgeschaltet.', - 'Authenticated successfully with {{region}}. API key and model configs saved to settings.json (backed up).': - 'Erfolgreich mit {{region}} authentifiziert. API-Schlüssel und Modellkonfigurationen wurden in settings.json gespeichert (gesichert).', + "Authenticated successfully with {{region}}. API key and model configs saved to settings.json (backed up).": + "Erfolgreich mit {{region}} authentifiziert. API-Schlüssel und Modellkonfigurationen wurden in settings.json gespeichert (gesichert).", // ============================================================================ // Context Usage Component // ============================================================================ - 'Context Usage': 'Kontextnutzung', - 'No API response yet. Send a message to see actual usage.': - 'Noch keine API-Antwort. Senden Sie eine Nachricht, um die tatsächliche Nutzung anzuzeigen.', - 'Estimated pre-conversation overhead': - 'Geschätzte Vorabkosten vor der Unterhaltung', - 'Context window': 'Kontextfenster', - tokens: 'Tokens', - Used: 'Verwendet', - Free: 'Frei', - 'Autocompact buffer': 'Autokomprimierungs-Puffer', - 'Usage by category': 'Verwendung nach Kategorie', - 'System prompt': 'System-Prompt', - 'Built-in tools': 'Integrierte Tools', - 'MCP tools': 'MCP-Tools', - 'Memory files': 'Speicherdateien', - Skills: 'Fähigkeiten', - Messages: 'Nachrichten', - 'Show context window usage breakdown.': - 'Zeigt die Aufschlüsselung der Kontextfenster-Nutzung an.', - 'Run /context detail for per-item breakdown.': - 'Führen Sie /context detail für eine Aufschlüsselung nach Elementen aus.', - active: 'aktiv', - 'body loaded': 'Inhalt geladen', - memory: 'Speicher', - '{{region}} configuration updated successfully.': - '{{region}}-Konfiguration erfolgreich aktualisiert.', - 'Authenticated successfully with {{region}}. API key and model configs saved to settings.json.': - 'Erfolgreich mit {{region}} authentifiziert. API-Schlüssel und Modellkonfigurationen wurden in settings.json gespeichert.', - 'Tip: Use /model to switch between available Coding Plan models.': - 'Tipp: Verwenden Sie /model, um zwischen verfügbaren Coding Plan-Modellen zu wechseln.', + "Context Usage": "Kontextnutzung", + "No API response yet. Send a message to see actual usage.": + "Noch keine API-Antwort. Senden Sie eine Nachricht, um die tatsächliche Nutzung anzuzeigen.", + "Estimated pre-conversation overhead": "Geschätzte Vorabkosten vor der Unterhaltung", + "Context window": "Kontextfenster", + tokens: "Tokens", + Used: "Verwendet", + Free: "Frei", + "Autocompact buffer": "Autokomprimierungs-Puffer", + "Usage by category": "Verwendung nach Kategorie", + "System prompt": "System-Prompt", + "Built-in tools": "Integrierte Tools", + "MCP tools": "MCP-Tools", + "Memory files": "Speicherdateien", + Skills: "Fähigkeiten", + Messages: "Nachrichten", + "Show context window usage breakdown.": + "Zeigt die Aufschlüsselung der Kontextfenster-Nutzung an.", + "Run /context detail for per-item breakdown.": + "Führen Sie /context detail für eine Aufschlüsselung nach Elementen aus.", + active: "aktiv", + "body loaded": "Inhalt geladen", + memory: "Speicher", + "{{region}} configuration updated successfully.": + "{{region}}-Konfiguration erfolgreich aktualisiert.", + "Authenticated successfully with {{region}}. API key and model configs saved to settings.json.": + "Erfolgreich mit {{region}} authentifiziert. API-Schlüssel und Modellkonfigurationen wurden in settings.json gespeichert.", + "Tip: Use /model to switch between available Coding Plan models.": + "Tipp: Verwenden Sie /model, um zwischen verfügbaren Coding Plan-Modellen zu wechseln.", // ============================================================================ // Ask User Question Tool // ============================================================================ - 'Please answer the following question(s):': - 'Bitte beantworten Sie die folgende(n) Frage(n):', - 'Cannot ask user questions in non-interactive mode. Please run in interactive mode to use this tool.': - 'Benutzerfragen können im nicht-interaktiven Modus nicht gestellt werden. Bitte führen Sie das Tool im interaktiven Modus aus.', - 'User declined to answer the questions.': - 'Benutzer hat die Beantwortung der Fragen abgelehnt.', - 'User has provided the following answers:': - 'Benutzer hat die folgenden Antworten bereitgestellt:', - 'Failed to process user answers:': - 'Fehler beim Verarbeiten der Benutzerantworten:', - 'Type something...': 'Etwas eingeben...', - Submit: 'Senden', - 'Submit answers': 'Antworten senden', - Cancel: 'Abbrechen', - 'Your answers:': 'Ihre Antworten:', - '(not answered)': '(nicht beantwortet)', - 'Ready to submit your answers?': 'Bereit, Ihre Antworten zu senden?', - '↑/↓: Navigate | ←/→: Switch tabs | Enter: Select': - '↑/↓: Navigieren | ←/→: Tabs wechseln | Enter: Auswählen', - '↑/↓: Navigate | ←/→: Switch tabs | Space/Enter: Toggle | Esc: Cancel': - '↑/↓: Navigieren | ←/→: Tabs wechseln | Space/Enter: Umschalten | Esc: Abbrechen', - '↑/↓: Navigate | Space/Enter: Toggle | Esc: Cancel': - '↑/↓: Navigieren | Space/Enter: Umschalten | Esc: Abbrechen', - '↑/↓: Navigate | Enter: Select | Esc: Cancel': - '↑/↓: Navigieren | Enter: Auswählen | Esc: Abbrechen', + "Please answer the following question(s):": "Bitte beantworten Sie die folgende(n) Frage(n):", + "Cannot ask user questions in non-interactive mode. Please run in interactive mode to use this tool.": + "Benutzerfragen können im nicht-interaktiven Modus nicht gestellt werden. Bitte führen Sie das Tool im interaktiven Modus aus.", + "User declined to answer the questions.": "Benutzer hat die Beantwortung der Fragen abgelehnt.", + "User has provided the following answers:": + "Benutzer hat die folgenden Antworten bereitgestellt:", + "Failed to process user answers:": "Fehler beim Verarbeiten der Benutzerantworten:", + "Type something...": "Etwas eingeben...", + Submit: "Senden", + "Submit answers": "Antworten senden", + Cancel: "Abbrechen", + "Your answers:": "Ihre Antworten:", + "(not answered)": "(nicht beantwortet)", + "Ready to submit your answers?": "Bereit, Ihre Antworten zu senden?", + "↑/↓: Navigate | ←/→: Switch tabs | Enter: Select": + "↑/↓: Navigieren | ←/→: Tabs wechseln | Enter: Auswählen", + "↑/↓: Navigate | ←/→: Switch tabs | Space/Enter: Toggle | Esc: Cancel": + "↑/↓: Navigieren | ←/→: Tabs wechseln | Space/Enter: Umschalten | Esc: Abbrechen", + "↑/↓: Navigate | Space/Enter: Toggle | Esc: Cancel": + "↑/↓: Navigieren | Space/Enter: Umschalten | Esc: Abbrechen", + "↑/↓: Navigate | Enter: Select | Esc: Cancel": + "↑/↓: Navigieren | Enter: Auswählen | Esc: Abbrechen", // ============================================================================ // Commands - Auth // ============================================================================ - 'Authenticate using Alibaba Cloud Coding Plan': - 'Mit Alibaba Cloud Coding Plan authentifizieren', - 'Region for Coding Plan (china/global)': - 'Region für Coding Plan (china/global)', - 'API key for Coding Plan': 'API-Schlüssel für Coding Plan', - 'Show current authentication status': - 'Aktuellen Authentifizierungsstatus anzeigen', - 'Authentication completed successfully.': - 'Authentifizierung erfolgreich abgeschlossen.', - 'Processing Alibaba Cloud Coding Plan authentication...': - 'Alibaba Cloud Coding Plan-Authentifizierung wird verarbeitet...', - 'Successfully authenticated with Alibaba Cloud Coding Plan.': - 'Erfolgreich mit Alibaba Cloud Coding Plan authentifiziert.', - 'Failed to authenticate with Coding Plan: {{error}}': - 'Authentifizierung mit Coding Plan fehlgeschlagen: {{error}}', - '中国 (China)': '中国 (China)', - '阿里云百炼 (aliyun.com)': '阿里云百炼 (aliyun.com)', - Global: 'Global', - 'Alibaba Cloud (alibabacloud.com)': 'Alibaba Cloud (alibabacloud.com)', - 'Select region for Coding Plan:': 'Region für Coding Plan auswählen:', - 'Enter your Coding Plan API key: ': - 'Geben Sie Ihren Coding Plan API-Schlüssel ein: ', - 'Select authentication method:': 'Authentifizierungsmethode auswählen:', - '\n=== Authentication Status ===\n': '\n=== Authentifizierungsstatus ===\n', - '⚠️ No authentication method configured.\n': - '⚠️ Keine Authentifizierungsmethode konfiguriert.\n', - 'Run one of the following commands to get started:\n': - 'Führen Sie einen der folgenden Befehle aus, um zu beginnen:\n', - ' qwen auth coding-plan - Authenticate with Alibaba Cloud Coding Plan\n': - ' qwen auth coding-plan - Mit Alibaba Cloud Coding Plan authentifizieren\n', - 'Or simply run:': 'Oder einfach ausführen:', - ' qwen auth - Interactive authentication setup\n': - ' qwen auth - Interaktive Authentifizierungseinrichtung\n', - '✓ Authentication Method: Alibaba Cloud Coding Plan': - '✓ Authentifizierungsmethode: Alibaba Cloud Coding Plan', - '中国 (China) - 阿里云百炼': '中国 (China) - 阿里云百炼', - 'Global - Alibaba Cloud': 'Global - Alibaba Cloud', - ' Region: {{region}}': ' Region: {{region}}', - ' Current Model: {{model}}': ' Aktuelles Modell: {{model}}', - ' Config Version: {{version}}': ' Konfigurationsversion: {{version}}', - ' Status: API key configured\n': ' Status: API-Schlüssel konfiguriert\n', - '⚠️ Authentication Method: Alibaba Cloud Coding Plan (Incomplete)': - '⚠️ Authentifizierungsmethode: Alibaba Cloud Coding Plan (Unvollständig)', - ' Issue: API key not found in environment or settings\n': - ' Problem: API-Schlüssel nicht in Umgebung oder Einstellungen gefunden\n', - ' Run `qwen auth coding-plan` to re-configure.\n': - ' Führen Sie `qwen auth coding-plan` aus, um neu zu konfigurieren.\n', - '✓ Authentication Method: {{type}}': '✓ Authentifizierungsmethode: {{type}}', - ' Status: Configured\n': ' Status: Konfiguriert\n', - 'Failed to check authentication status: {{error}}': - 'Authentifizierungsstatus konnte nicht überprüft werden: {{error}}', - 'Select an option:': 'Option auswählen:', - 'Raw mode not available. Please run in an interactive terminal.': - 'Raw-Modus nicht verfügbar. Bitte in einem interaktiven Terminal ausführen.', - '(Use ↑ ↓ arrows to navigate, Enter to select, Ctrl+C to exit)\n': - '(↑ ↓ Pfeiltasten zum Navigieren, Enter zum Auswählen, Strg+C zum Beenden)\n', - verbose: 'ausführlich', - 'Show full tool output and thinking in verbose mode (toggle with Ctrl+O).': - 'Vollständige Tool-Ausgabe und Denkprozess im ausführlichen Modus anzeigen (mit Strg+O umschalten).', - 'Press Ctrl+O to show full tool output': - 'Strg+O für vollständige Tool-Ausgabe drücken', + "Authenticate using Alibaba Cloud Coding Plan": "Mit Alibaba Cloud Coding Plan authentifizieren", + "Region for Coding Plan (china/global)": "Region für Coding Plan (china/global)", + "API key for Coding Plan": "API-Schlüssel für Coding Plan", + "Show current authentication status": "Aktuellen Authentifizierungsstatus anzeigen", + "Authentication completed successfully.": "Authentifizierung erfolgreich abgeschlossen.", + "Processing Alibaba Cloud Coding Plan authentication...": + "Alibaba Cloud Coding Plan-Authentifizierung wird verarbeitet...", + "Successfully authenticated with Alibaba Cloud Coding Plan.": + "Erfolgreich mit Alibaba Cloud Coding Plan authentifiziert.", + "Failed to authenticate with Coding Plan: {{error}}": + "Authentifizierung mit Coding Plan fehlgeschlagen: {{error}}", + "中国 (China)": "中国 (China)", + "阿里云百炼 (aliyun.com)": "阿里云百炼 (aliyun.com)", + Global: "Global", + "Alibaba Cloud (alibabacloud.com)": "Alibaba Cloud (alibabacloud.com)", + "Select region for Coding Plan:": "Region für Coding Plan auswählen:", + "Enter your Coding Plan API key: ": "Geben Sie Ihren Coding Plan API-Schlüssel ein: ", + "Select authentication method:": "Authentifizierungsmethode auswählen:", + "\n=== Authentication Status ===\n": "\n=== Authentifizierungsstatus ===\n", + "⚠️ No authentication method configured.\n": "⚠️ Keine Authentifizierungsmethode konfiguriert.\n", + "Run one of the following commands to get started:\n": + "Führen Sie einen der folgenden Befehle aus, um zu beginnen:\n", + " qwen auth coding-plan - Authenticate with Alibaba Cloud Coding Plan\n": + " qwen auth coding-plan - Mit Alibaba Cloud Coding Plan authentifizieren\n", + "Or simply run:": "Oder einfach ausführen:", + " qwen auth - Interactive authentication setup\n": + " qwen auth - Interaktive Authentifizierungseinrichtung\n", + "✓ Authentication Method: Alibaba Cloud Coding Plan": + "✓ Authentifizierungsmethode: Alibaba Cloud Coding Plan", + "中国 (China) - 阿里云百炼": "中国 (China) - 阿里云百炼", + "Global - Alibaba Cloud": "Global - Alibaba Cloud", + " Region: {{region}}": " Region: {{region}}", + " Current Model: {{model}}": " Aktuelles Modell: {{model}}", + " Config Version: {{version}}": " Konfigurationsversion: {{version}}", + " Status: API key configured\n": " Status: API-Schlüssel konfiguriert\n", + "⚠️ Authentication Method: Alibaba Cloud Coding Plan (Incomplete)": + "⚠️ Authentifizierungsmethode: Alibaba Cloud Coding Plan (Unvollständig)", + " Issue: API key not found in environment or settings\n": + " Problem: API-Schlüssel nicht in Umgebung oder Einstellungen gefunden\n", + " Run `qwen auth coding-plan` to re-configure.\n": + " Führen Sie `qwen auth coding-plan` aus, um neu zu konfigurieren.\n", + "✓ Authentication Method: {{type}}": "✓ Authentifizierungsmethode: {{type}}", + " Status: Configured\n": " Status: Konfiguriert\n", + "Failed to check authentication status: {{error}}": + "Authentifizierungsstatus konnte nicht überprüft werden: {{error}}", + "Select an option:": "Option auswählen:", + "Raw mode not available. Please run in an interactive terminal.": + "Raw-Modus nicht verfügbar. Bitte in einem interaktiven Terminal ausführen.", + "(Use ↑ ↓ arrows to navigate, Enter to select, Ctrl+C to exit)\n": + "(↑ ↓ Pfeiltasten zum Navigieren, Enter zum Auswählen, Strg+C zum Beenden)\n", + verbose: "ausführlich", + "Show full tool output and thinking in verbose mode (toggle with Ctrl+O).": + "Vollständige Tool-Ausgabe und Denkprozess im ausführlichen Modus anzeigen (mit Strg+O umschalten).", + "Press Ctrl+O to show full tool output": "Strg+O für vollständige Tool-Ausgabe drücken", - 'Switch to plan mode or exit plan mode': - 'Switch to plan mode or exit plan mode', - 'Exited plan mode. Previous approval mode restored.': - 'Exited plan mode. Previous approval mode restored.', - 'Enabled plan mode. The agent will analyze and plan without executing tools.': - 'Enabled plan mode. The agent will analyze and plan without executing tools.', + "Switch to plan mode or exit plan mode": "Switch to plan mode or exit plan mode", + "Exited plan mode. Previous approval mode restored.": + "Exited plan mode. Previous approval mode restored.", + "Enabled plan mode. The agent will analyze and plan without executing tools.": + "Enabled plan mode. The agent will analyze and plan without executing tools.", 'Already in plan mode. Use "/plan exit" to exit plan mode.': 'Already in plan mode. Use "/plan exit" to exit plan mode.', 'Not in plan mode. Use "/plan" to enter plan mode first.': diff --git a/apps/airiscode-cli/src/i18n/locales/en.js b/apps/airiscode-cli/src/i18n/locales/en.js index a256c3a65..25af8c825 100644 --- a/apps/airiscode-cli/src/i18n/locales/en.js +++ b/apps/airiscode-cli/src/i18n/locales/en.js @@ -12,1238 +12,1154 @@ export default { // Help / UI Components // ============================================================================ // Attachment hints - '↑ to manage attachments': '↑ to manage attachments', - '← → select, Delete to remove, ↓ to exit': - '← → select, Delete to remove, ↓ to exit', - 'Attachments: ': 'Attachments: ', + "↑ to manage attachments": "↑ to manage attachments", + "← → select, Delete to remove, ↓ to exit": "← → select, Delete to remove, ↓ to exit", + "Attachments: ": "Attachments: ", - 'Basics:': 'Basics:', - 'Add context': 'Add context', - 'Use {{symbol}} to specify files for context (e.g., {{example}}) to target specific files or folders.': - 'Use {{symbol}} to specify files for context (e.g., {{example}}) to target specific files or folders.', - '@': '@', - '@src/myFile.ts': '@src/myFile.ts', - 'Shell mode': 'Shell mode', - 'YOLO mode': 'YOLO mode', - 'plan mode': 'plan mode', - 'auto-accept edits': 'auto-accept edits', - 'Accepting edits': 'Accepting edits', - '(shift + tab to cycle)': '(shift + tab to cycle)', - '(tab to cycle)': '(tab to cycle)', - 'Execute shell commands via {{symbol}} (e.g., {{example1}}) or use natural language (e.g., {{example2}}).': - 'Execute shell commands via {{symbol}} (e.g., {{example1}}) or use natural language (e.g., {{example2}}).', - '!': '!', - '!npm run start': '!npm run start', - 'start server': 'start server', - 'Commands:': 'Commands:', - 'shell command': 'shell command', - 'Model Context Protocol command (from external servers)': - 'Model Context Protocol command (from external servers)', - 'Keyboard Shortcuts:': 'Keyboard Shortcuts:', - 'Toggle this help display': 'Toggle this help display', - 'Toggle shell mode': 'Toggle shell mode', - 'Open command menu': 'Open command menu', - 'Add file context': 'Add file context', - 'Accept suggestion / Autocomplete': 'Accept suggestion / Autocomplete', - 'Reverse search history': 'Reverse search history', - 'Press ? again to close': 'Press ? again to close', + "Basics:": "Basics:", + "Add context": "Add context", + "Use {{symbol}} to specify files for context (e.g., {{example}}) to target specific files or folders.": + "Use {{symbol}} to specify files for context (e.g., {{example}}) to target specific files or folders.", + "@": "@", + "@src/myFile.ts": "@src/myFile.ts", + "Shell mode": "Shell mode", + "YOLO mode": "YOLO mode", + "plan mode": "plan mode", + "auto-accept edits": "auto-accept edits", + "Accepting edits": "Accepting edits", + "(shift + tab to cycle)": "(shift + tab to cycle)", + "(tab to cycle)": "(tab to cycle)", + "Execute shell commands via {{symbol}} (e.g., {{example1}}) or use natural language (e.g., {{example2}}).": + "Execute shell commands via {{symbol}} (e.g., {{example1}}) or use natural language (e.g., {{example2}}).", + "!": "!", + "!npm run start": "!npm run start", + "start server": "start server", + "Commands:": "Commands:", + "shell command": "shell command", + "Model Context Protocol command (from external servers)": + "Model Context Protocol command (from external servers)", + "Keyboard Shortcuts:": "Keyboard Shortcuts:", + "Toggle this help display": "Toggle this help display", + "Toggle shell mode": "Toggle shell mode", + "Open command menu": "Open command menu", + "Add file context": "Add file context", + "Accept suggestion / Autocomplete": "Accept suggestion / Autocomplete", + "Reverse search history": "Reverse search history", + "Press ? again to close": "Press ? again to close", // Keyboard shortcuts panel descriptions - 'for shell mode': 'for shell mode', - 'for commands': 'for commands', - 'for file paths': 'for file paths', - 'to clear input': 'to clear input', - 'to cycle approvals': 'to cycle approvals', - 'to quit': 'to quit', - 'for newline': 'for newline', - 'to clear screen': 'to clear screen', - 'to search history': 'to search history', - 'to paste images': 'to paste images', - 'for external editor': 'for external editor', - 'Jump through words in the input': 'Jump through words in the input', - 'Close dialogs, cancel requests, or quit application': - 'Close dialogs, cancel requests, or quit application', - 'New line': 'New line', - 'New line (Alt+Enter works for certain linux distros)': - 'New line (Alt+Enter works for certain linux distros)', - 'Clear the screen': 'Clear the screen', - 'Open input in external editor': 'Open input in external editor', - 'Send message': 'Send message', - 'Initializing...': 'Initializing...', - 'Connecting to MCP servers... ({{connected}}/{{total}})': - 'Connecting to MCP servers... ({{connected}}/{{total}})', - 'Type your message or @path/to/file': 'Type your message or @path/to/file', - '? for shortcuts': '? for shortcuts', + "for shell mode": "for shell mode", + "for commands": "for commands", + "for file paths": "for file paths", + "to clear input": "to clear input", + "to cycle approvals": "to cycle approvals", + "to quit": "to quit", + "for newline": "for newline", + "to clear screen": "to clear screen", + "to search history": "to search history", + "to paste images": "to paste images", + "for external editor": "for external editor", + "Jump through words in the input": "Jump through words in the input", + "Close dialogs, cancel requests, or quit application": + "Close dialogs, cancel requests, or quit application", + "New line": "New line", + "New line (Alt+Enter works for certain linux distros)": + "New line (Alt+Enter works for certain linux distros)", + "Clear the screen": "Clear the screen", + "Open input in external editor": "Open input in external editor", + "Send message": "Send message", + "Initializing...": "Initializing...", + "Connecting to MCP servers... ({{connected}}/{{total}})": + "Connecting to MCP servers... ({{connected}}/{{total}})", + "Type your message or @path/to/file": "Type your message or @path/to/file", + "? for shortcuts": "? for shortcuts", "Press 'i' for INSERT mode and 'Esc' for NORMAL mode.": "Press 'i' for INSERT mode and 'Esc' for NORMAL mode.", - 'Cancel operation / Clear input (double press)': - 'Cancel operation / Clear input (double press)', - 'Cycle approval modes': 'Cycle approval modes', - 'Cycle through your prompt history': 'Cycle through your prompt history', - 'For a full list of shortcuts, see {{docPath}}': - 'For a full list of shortcuts, see {{docPath}}', - 'docs/keyboard-shortcuts.md': 'docs/keyboard-shortcuts.md', - 'for help on Qwen Code': 'for help on Qwen Code', - 'show version info': 'show version info', - 'submit a bug report': 'submit a bug report', - 'About Qwen Code': 'About Qwen Code', - Status: 'Status', + "Cancel operation / Clear input (double press)": "Cancel operation / Clear input (double press)", + "Cycle approval modes": "Cycle approval modes", + "Cycle through your prompt history": "Cycle through your prompt history", + "For a full list of shortcuts, see {{docPath}}": "For a full list of shortcuts, see {{docPath}}", + "docs/keyboard-shortcuts.md": "docs/keyboard-shortcuts.md", + "for help on Qwen Code": "for help on Qwen Code", + "show version info": "show version info", + "submit a bug report": "submit a bug report", + "About Qwen Code": "About Qwen Code", + Status: "Status", // ============================================================================ // System Information Fields // ============================================================================ - 'Qwen Code': 'Qwen Code', - Runtime: 'Runtime', - OS: 'OS', - Auth: 'Auth', - 'CLI Version': 'CLI Version', - 'Git Commit': 'Git Commit', - Model: 'Model', - 'Fast Model': 'Fast Model', - Sandbox: 'Sandbox', - 'OS Platform': 'OS Platform', - 'OS Arch': 'OS Arch', - 'OS Release': 'OS Release', - 'Node.js Version': 'Node.js Version', - 'NPM Version': 'NPM Version', - 'Session ID': 'Session ID', - 'Auth Method': 'Auth Method', - 'Base URL': 'Base URL', - Proxy: 'Proxy', - 'Memory Usage': 'Memory Usage', - 'IDE Client': 'IDE Client', + "Qwen Code": "Qwen Code", + Runtime: "Runtime", + OS: "OS", + Auth: "Auth", + "CLI Version": "CLI Version", + "Git Commit": "Git Commit", + Model: "Model", + "Fast Model": "Fast Model", + Sandbox: "Sandbox", + "OS Platform": "OS Platform", + "OS Arch": "OS Arch", + "OS Release": "OS Release", + "Node.js Version": "Node.js Version", + "NPM Version": "NPM Version", + "Session ID": "Session ID", + "Auth Method": "Auth Method", + "Base URL": "Base URL", + Proxy: "Proxy", + "Memory Usage": "Memory Usage", + "IDE Client": "IDE Client", // ============================================================================ // Commands - General // ============================================================================ - 'Analyzes the project and creates a tailored QWEN.md file.': - 'Analyzes the project and creates a tailored QWEN.md file.', - 'List available Qwen Code tools. Usage: /tools [desc]': - 'List available Qwen Code tools. Usage: /tools [desc]', - 'List available skills.': 'List available skills.', - 'Available Qwen Code CLI tools:': 'Available Qwen Code CLI tools:', - 'No tools available': 'No tools available', - 'View or change the approval mode for tool usage': - 'View or change the approval mode for tool usage', + "Analyzes the project and creates a tailored QWEN.md file.": + "Analyzes the project and creates a tailored QWEN.md file.", + "List available Qwen Code tools. Usage: /tools [desc]": + "List available Qwen Code tools. Usage: /tools [desc]", + "List available skills.": "List available skills.", + "Available Qwen Code CLI tools:": "Available Qwen Code CLI tools:", + "No tools available": "No tools available", + "View or change the approval mode for tool usage": + "View or change the approval mode for tool usage", 'Invalid approval mode "{{arg}}". Valid modes: {{modes}}': 'Invalid approval mode "{{arg}}". Valid modes: {{modes}}', 'Approval mode set to "{{mode}}"': 'Approval mode set to "{{mode}}"', - 'View or change the language setting': 'View or change the language setting', - 'change the theme': 'change the theme', - 'Select Theme': 'Select Theme', - Preview: 'Preview', - '(Use Enter to select, Tab to configure scope)': - '(Use Enter to select, Tab to configure scope)', - '(Use Enter to apply scope, Tab to go back)': - '(Use Enter to apply scope, Tab to go back)', - 'Theme configuration unavailable due to NO_COLOR env variable.': - 'Theme configuration unavailable due to NO_COLOR env variable.', + "View or change the language setting": "View or change the language setting", + "change the theme": "change the theme", + "Select Theme": "Select Theme", + Preview: "Preview", + "(Use Enter to select, Tab to configure scope)": "(Use Enter to select, Tab to configure scope)", + "(Use Enter to apply scope, Tab to go back)": "(Use Enter to apply scope, Tab to go back)", + "Theme configuration unavailable due to NO_COLOR env variable.": + "Theme configuration unavailable due to NO_COLOR env variable.", 'Theme "{{themeName}}" not found.': 'Theme "{{themeName}}" not found.', 'Theme "{{themeName}}" not found in selected scope.': 'Theme "{{themeName}}" not found in selected scope.', - 'Clear conversation history and free up context': - 'Clear conversation history and free up context', - 'Compresses the context by replacing it with a summary.': - 'Compresses the context by replacing it with a summary.', - 'open full Qwen Code documentation in your browser': - 'open full Qwen Code documentation in your browser', - 'Configuration not available.': 'Configuration not available.', - 'change the auth method': 'change the auth method', - 'Configure authentication information for login': - 'Configure authentication information for login', - 'Copy the last result or code snippet to clipboard': - 'Copy the last result or code snippet to clipboard', + "Clear conversation history and free up context": + "Clear conversation history and free up context", + "Compresses the context by replacing it with a summary.": + "Compresses the context by replacing it with a summary.", + "open full Qwen Code documentation in your browser": + "open full Qwen Code documentation in your browser", + "Configuration not available.": "Configuration not available.", + "change the auth method": "change the auth method", + "Configure authentication information for login": + "Configure authentication information for login", + "Copy the last result or code snippet to clipboard": + "Copy the last result or code snippet to clipboard", // ============================================================================ // Commands - Agents // ============================================================================ - 'Manage subagents for specialized task delegation.': - 'Manage subagents for specialized task delegation.', - 'Manage existing subagents (view, edit, delete).': - 'Manage existing subagents (view, edit, delete).', - 'Create a new subagent with guided setup.': - 'Create a new subagent with guided setup.', + "Manage subagents for specialized task delegation.": + "Manage subagents for specialized task delegation.", + "Manage existing subagents (view, edit, delete).": + "Manage existing subagents (view, edit, delete).", + "Create a new subagent with guided setup.": "Create a new subagent with guided setup.", // ============================================================================ // Agents - Management Dialog // ============================================================================ - Agents: 'Agents', - 'Choose Action': 'Choose Action', - 'Edit {{name}}': 'Edit {{name}}', - 'Edit Tools: {{name}}': 'Edit Tools: {{name}}', - 'Edit Color: {{name}}': 'Edit Color: {{name}}', - 'Delete {{name}}': 'Delete {{name}}', - 'Unknown Step': 'Unknown Step', - 'Esc to close': 'Esc to close', - 'Enter to select, ↑↓ to navigate, Esc to close': - 'Enter to select, ↑↓ to navigate, Esc to close', - 'Esc to go back': 'Esc to go back', - 'Enter to confirm, Esc to cancel': 'Enter to confirm, Esc to cancel', - 'Enter to select, ↑↓ to navigate, Esc to go back': - 'Enter to select, ↑↓ to navigate, Esc to go back', - 'Enter to submit, Esc to go back': 'Enter to submit, Esc to go back', - 'Invalid step: {{step}}': 'Invalid step: {{step}}', - 'No subagents found.': 'No subagents found.', + Agents: "Agents", + "Choose Action": "Choose Action", + "Edit {{name}}": "Edit {{name}}", + "Edit Tools: {{name}}": "Edit Tools: {{name}}", + "Edit Color: {{name}}": "Edit Color: {{name}}", + "Delete {{name}}": "Delete {{name}}", + "Unknown Step": "Unknown Step", + "Esc to close": "Esc to close", + "Enter to select, ↑↓ to navigate, Esc to close": "Enter to select, ↑↓ to navigate, Esc to close", + "Esc to go back": "Esc to go back", + "Enter to confirm, Esc to cancel": "Enter to confirm, Esc to cancel", + "Enter to select, ↑↓ to navigate, Esc to go back": + "Enter to select, ↑↓ to navigate, Esc to go back", + "Enter to submit, Esc to go back": "Enter to submit, Esc to go back", + "Invalid step: {{step}}": "Invalid step: {{step}}", + "No subagents found.": "No subagents found.", "Use '/agents create' to create your first subagent.": "Use '/agents create' to create your first subagent.", - '(built-in)': '(built-in)', - '(overridden by project level agent)': '(overridden by project level agent)', - 'Project Level ({{path}})': 'Project Level ({{path}})', - 'User Level ({{path}})': 'User Level ({{path}})', - 'Built-in Agents': 'Built-in Agents', - 'Extension Agents': 'Extension Agents', - 'Using: {{count}} agents': 'Using: {{count}} agents', - 'View Agent': 'View Agent', - 'Edit Agent': 'Edit Agent', - 'Delete Agent': 'Delete Agent', - Back: 'Back', - 'No agent selected': 'No agent selected', - 'File Path: ': 'File Path: ', - 'Tools: ': 'Tools: ', - 'Color: ': 'Color: ', - 'Description:': 'Description:', - 'System Prompt:': 'System Prompt:', - 'Open in editor': 'Open in editor', - 'Edit tools': 'Edit tools', - 'Edit color': 'Edit color', - '❌ Error:': '❌ Error:', + "(built-in)": "(built-in)", + "(overridden by project level agent)": "(overridden by project level agent)", + "Project Level ({{path}})": "Project Level ({{path}})", + "User Level ({{path}})": "User Level ({{path}})", + "Built-in Agents": "Built-in Agents", + "Extension Agents": "Extension Agents", + "Using: {{count}} agents": "Using: {{count}} agents", + "View Agent": "View Agent", + "Edit Agent": "Edit Agent", + "Delete Agent": "Delete Agent", + Back: "Back", + "No agent selected": "No agent selected", + "File Path: ": "File Path: ", + "Tools: ": "Tools: ", + "Color: ": "Color: ", + "Description:": "Description:", + "System Prompt:": "System Prompt:", + "Open in editor": "Open in editor", + "Edit tools": "Edit tools", + "Edit color": "Edit color", + "❌ Error:": "❌ Error:", 'Are you sure you want to delete agent "{{name}}"?': 'Are you sure you want to delete agent "{{name}}"?', // ============================================================================ // Agents - Creation Wizard // ============================================================================ - 'Project Level (.qwen/agents/)': 'Project Level (.qwen/agents/)', - 'User Level (~/.qwen/agents/)': 'User Level (~/.qwen/agents/)', - '✅ Subagent Created Successfully!': '✅ Subagent Created Successfully!', + "Project Level (.qwen/agents/)": "Project Level (.qwen/agents/)", + "User Level (~/.qwen/agents/)": "User Level (~/.qwen/agents/)", + "✅ Subagent Created Successfully!": "✅ Subagent Created Successfully!", 'Subagent "{{name}}" has been saved to {{level}} level.': 'Subagent "{{name}}" has been saved to {{level}} level.', - 'Name: ': 'Name: ', - 'Location: ': 'Location: ', - '❌ Error saving subagent:': '❌ Error saving subagent:', - 'Warnings:': 'Warnings:', + "Name: ": "Name: ", + "Location: ": "Location: ", + "❌ Error saving subagent:": "❌ Error saving subagent:", + "Warnings:": "Warnings:", 'Name "{{name}}" already exists at {{level}} level - will overwrite existing subagent': 'Name "{{name}}" already exists at {{level}} level - will overwrite existing subagent', 'Name "{{name}}" exists at user level - project level will take precedence': 'Name "{{name}}" exists at user level - project level will take precedence', 'Name "{{name}}" exists at project level - existing subagent will take precedence': 'Name "{{name}}" exists at project level - existing subagent will take precedence', - 'Description is over {{length}} characters': - 'Description is over {{length}} characters', - 'System prompt is over {{length}} characters': - 'System prompt is over {{length}} characters', + "Description is over {{length}} characters": "Description is over {{length}} characters", + "System prompt is over {{length}} characters": "System prompt is over {{length}} characters", // Agents - Creation Wizard Steps - 'Step {{n}}: Choose Location': 'Step {{n}}: Choose Location', - 'Step {{n}}: Choose Generation Method': - 'Step {{n}}: Choose Generation Method', - 'Generate with Qwen Code (Recommended)': - 'Generate with Qwen Code (Recommended)', - 'Manual Creation': 'Manual Creation', - 'Describe what this subagent should do and when it should be used. (Be comprehensive for best results)': - 'Describe what this subagent should do and when it should be used. (Be comprehensive for best results)', - 'e.g., Expert code reviewer that reviews code based on best practices...': - 'e.g., Expert code reviewer that reviews code based on best practices...', - 'Generating subagent configuration...': - 'Generating subagent configuration...', - 'Failed to generate subagent: {{error}}': - 'Failed to generate subagent: {{error}}', - 'Step {{n}}: Describe Your Subagent': 'Step {{n}}: Describe Your Subagent', - 'Step {{n}}: Enter Subagent Name': 'Step {{n}}: Enter Subagent Name', - 'Step {{n}}: Enter System Prompt': 'Step {{n}}: Enter System Prompt', - 'Step {{n}}: Enter Description': 'Step {{n}}: Enter Description', + "Step {{n}}: Choose Location": "Step {{n}}: Choose Location", + "Step {{n}}: Choose Generation Method": "Step {{n}}: Choose Generation Method", + "Generate with Qwen Code (Recommended)": "Generate with Qwen Code (Recommended)", + "Manual Creation": "Manual Creation", + "Describe what this subagent should do and when it should be used. (Be comprehensive for best results)": + "Describe what this subagent should do and when it should be used. (Be comprehensive for best results)", + "e.g., Expert code reviewer that reviews code based on best practices...": + "e.g., Expert code reviewer that reviews code based on best practices...", + "Generating subagent configuration...": "Generating subagent configuration...", + "Failed to generate subagent: {{error}}": "Failed to generate subagent: {{error}}", + "Step {{n}}: Describe Your Subagent": "Step {{n}}: Describe Your Subagent", + "Step {{n}}: Enter Subagent Name": "Step {{n}}: Enter Subagent Name", + "Step {{n}}: Enter System Prompt": "Step {{n}}: Enter System Prompt", + "Step {{n}}: Enter Description": "Step {{n}}: Enter Description", // Agents - Tool Selection - 'Step {{n}}: Select Tools': 'Step {{n}}: Select Tools', - 'All Tools (Default)': 'All Tools (Default)', - 'All Tools': 'All Tools', - 'Read-only Tools': 'Read-only Tools', - 'Read & Edit Tools': 'Read & Edit Tools', - 'Read & Edit & Execution Tools': 'Read & Edit & Execution Tools', - 'All tools selected, including MCP tools': - 'All tools selected, including MCP tools', - 'Selected tools:': 'Selected tools:', - 'Read-only tools:': 'Read-only tools:', - 'Edit tools:': 'Edit tools:', - 'Execution tools:': 'Execution tools:', - 'Step {{n}}: Choose Background Color': 'Step {{n}}: Choose Background Color', - 'Step {{n}}: Confirm and Save': 'Step {{n}}: Confirm and Save', + "Step {{n}}: Select Tools": "Step {{n}}: Select Tools", + "All Tools (Default)": "All Tools (Default)", + "All Tools": "All Tools", + "Read-only Tools": "Read-only Tools", + "Read & Edit Tools": "Read & Edit Tools", + "Read & Edit & Execution Tools": "Read & Edit & Execution Tools", + "All tools selected, including MCP tools": "All tools selected, including MCP tools", + "Selected tools:": "Selected tools:", + "Read-only tools:": "Read-only tools:", + "Edit tools:": "Edit tools:", + "Execution tools:": "Execution tools:", + "Step {{n}}: Choose Background Color": "Step {{n}}: Choose Background Color", + "Step {{n}}: Confirm and Save": "Step {{n}}: Confirm and Save", // Agents - Navigation & Instructions - 'Esc to cancel': 'Esc to cancel', - 'Press Enter to save, e to save and edit, Esc to go back': - 'Press Enter to save, e to save and edit, Esc to go back', - 'Press Enter to continue, {{navigation}}Esc to {{action}}': - 'Press Enter to continue, {{navigation}}Esc to {{action}}', - cancel: 'cancel', - 'go back': 'go back', - '↑↓ to navigate, ': '↑↓ to navigate, ', - 'Enter a clear, unique name for this subagent.': - 'Enter a clear, unique name for this subagent.', - 'e.g., Code Reviewer': 'e.g., Code Reviewer', - 'Name cannot be empty.': 'Name cannot be empty.', + "Esc to cancel": "Esc to cancel", + "Press Enter to save, e to save and edit, Esc to go back": + "Press Enter to save, e to save and edit, Esc to go back", + "Press Enter to continue, {{navigation}}Esc to {{action}}": + "Press Enter to continue, {{navigation}}Esc to {{action}}", + cancel: "cancel", + "go back": "go back", + "↑↓ to navigate, ": "↑↓ to navigate, ", + "Enter a clear, unique name for this subagent.": "Enter a clear, unique name for this subagent.", + "e.g., Code Reviewer": "e.g., Code Reviewer", + "Name cannot be empty.": "Name cannot be empty.", "Write the system prompt that defines this subagent's behavior. Be comprehensive for best results.": "Write the system prompt that defines this subagent's behavior. Be comprehensive for best results.", - 'e.g., You are an expert code reviewer...': - 'e.g., You are an expert code reviewer...', - 'System prompt cannot be empty.': 'System prompt cannot be empty.', - 'Describe when and how this subagent should be used.': - 'Describe when and how this subagent should be used.', - 'e.g., Reviews code for best practices and potential bugs.': - 'e.g., Reviews code for best practices and potential bugs.', - 'Description cannot be empty.': 'Description cannot be empty.', - 'Failed to launch editor: {{error}}': 'Failed to launch editor: {{error}}', - 'Failed to save and edit subagent: {{error}}': - 'Failed to save and edit subagent: {{error}}', + "e.g., You are an expert code reviewer...": "e.g., You are an expert code reviewer...", + "System prompt cannot be empty.": "System prompt cannot be empty.", + "Describe when and how this subagent should be used.": + "Describe when and how this subagent should be used.", + "e.g., Reviews code for best practices and potential bugs.": + "e.g., Reviews code for best practices and potential bugs.", + "Description cannot be empty.": "Description cannot be empty.", + "Failed to launch editor: {{error}}": "Failed to launch editor: {{error}}", + "Failed to save and edit subagent: {{error}}": "Failed to save and edit subagent: {{error}}", // ============================================================================ // Extensions - Management Dialog // ============================================================================ - 'Manage Extensions': 'Manage Extensions', - 'Extension Details': 'Extension Details', - 'View Extension': 'View Extension', - 'Update Extension': 'Update Extension', - 'Disable Extension': 'Disable Extension', - 'Enable Extension': 'Enable Extension', - 'Uninstall Extension': 'Uninstall Extension', - 'Select Scope': 'Select Scope', - 'User Scope': 'User Scope', - 'Workspace Scope': 'Workspace Scope', - 'No extensions found.': 'No extensions found.', - Active: 'Active', - Disabled: 'Disabled', - 'Update available': 'Update available', - 'Up to date': 'Up to date', - 'Checking...': 'Checking...', - 'Updating...': 'Updating...', - Unknown: 'Unknown', - Error: 'Error', - 'Version:': 'Version:', - 'Status:': 'Status:', + "Manage Extensions": "Manage Extensions", + "Extension Details": "Extension Details", + "View Extension": "View Extension", + "Update Extension": "Update Extension", + "Disable Extension": "Disable Extension", + "Enable Extension": "Enable Extension", + "Uninstall Extension": "Uninstall Extension", + "Select Scope": "Select Scope", + "User Scope": "User Scope", + "Workspace Scope": "Workspace Scope", + "No extensions found.": "No extensions found.", + Active: "Active", + Disabled: "Disabled", + "Update available": "Update available", + "Up to date": "Up to date", + "Checking...": "Checking...", + "Updating...": "Updating...", + Unknown: "Unknown", + Error: "Error", + "Version:": "Version:", + "Status:": "Status:", 'Are you sure you want to uninstall extension "{{name}}"?': 'Are you sure you want to uninstall extension "{{name}}"?', - 'This action cannot be undone.': 'This action cannot be undone.', - 'Extension "{{name}}" disabled successfully.': - 'Extension "{{name}}" disabled successfully.', - 'Extension "{{name}}" enabled successfully.': - 'Extension "{{name}}" enabled successfully.', - 'Extension "{{name}}" updated successfully.': - 'Extension "{{name}}" updated successfully.', + "This action cannot be undone.": "This action cannot be undone.", + 'Extension "{{name}}" disabled successfully.': 'Extension "{{name}}" disabled successfully.', + 'Extension "{{name}}" enabled successfully.': 'Extension "{{name}}" enabled successfully.', + 'Extension "{{name}}" updated successfully.': 'Extension "{{name}}" updated successfully.', 'Failed to update extension "{{name}}": {{error}}': 'Failed to update extension "{{name}}": {{error}}', - 'Select the scope for this action:': 'Select the scope for this action:', - 'User - Applies to all projects': 'User - Applies to all projects', - 'Workspace - Applies to current project only': - 'Workspace - Applies to current project only', + "Select the scope for this action:": "Select the scope for this action:", + "User - Applies to all projects": "User - Applies to all projects", + "Workspace - Applies to current project only": "Workspace - Applies to current project only", // Extension dialog - missing keys - 'Name:': 'Name:', - 'MCP Servers:': 'MCP Servers:', - 'Settings:': 'Settings:', - active: 'active', - disabled: 'disabled', - 'View Details': 'View Details', - 'Update failed:': 'Update failed:', - 'Updating {{name}}...': 'Updating {{name}}...', - 'Update complete!': 'Update complete!', - 'User (global)': 'User (global)', - 'Workspace (project-specific)': 'Workspace (project-specific)', + "Name:": "Name:", + "MCP Servers:": "MCP Servers:", + "Settings:": "Settings:", + active: "active", + disabled: "disabled", + "View Details": "View Details", + "Update failed:": "Update failed:", + "Updating {{name}}...": "Updating {{name}}...", + "Update complete!": "Update complete!", + "User (global)": "User (global)", + "Workspace (project-specific)": "Workspace (project-specific)", 'Disable "{{name}}" - Select Scope': 'Disable "{{name}}" - Select Scope', 'Enable "{{name}}" - Select Scope': 'Enable "{{name}}" - Select Scope', - 'No extension selected': 'No extension selected', - 'Press Y/Enter to confirm, N/Esc to cancel': - 'Press Y/Enter to confirm, N/Esc to cancel', - 'Y/Enter to confirm, N/Esc to cancel': 'Y/Enter to confirm, N/Esc to cancel', - '{{count}} extensions installed': '{{count}} extensions installed', + "No extension selected": "No extension selected", + "Press Y/Enter to confirm, N/Esc to cancel": "Press Y/Enter to confirm, N/Esc to cancel", + "Y/Enter to confirm, N/Esc to cancel": "Y/Enter to confirm, N/Esc to cancel", + "{{count}} extensions installed": "{{count}} extensions installed", "Use '/extensions install' to install your first extension.": "Use '/extensions install' to install your first extension.", // Update status values - 'up to date': 'up to date', - 'update available': 'update available', - 'checking...': 'checking...', - 'not updatable': 'not updatable', - error: 'error', + "up to date": "up to date", + "update available": "update available", + "checking...": "checking...", + "not updatable": "not updatable", + error: "error", // ============================================================================ // Commands - General (continued) // ============================================================================ - 'View and edit Qwen Code settings': 'View and edit Qwen Code settings', - Settings: 'Settings', - 'To see changes, Qwen Code must be restarted. Press r to exit and apply changes now.': - 'To see changes, Qwen Code must be restarted. Press r to exit and apply changes now.', + "View and edit Qwen Code settings": "View and edit Qwen Code settings", + Settings: "Settings", + "To see changes, Qwen Code must be restarted. Press r to exit and apply changes now.": + "To see changes, Qwen Code must be restarted. Press r to exit and apply changes now.", 'The command "/{{command}}" is not supported in non-interactive mode.': 'The command "/{{command}}" is not supported in non-interactive mode.', // ============================================================================ // Settings Labels // ============================================================================ - 'Vim Mode': 'Vim Mode', - 'Disable Auto Update': 'Disable Auto Update', - 'Attribution: commit': 'Attribution: commit', - 'Terminal Bell Notification': 'Terminal Bell Notification', - 'Enable Usage Statistics': 'Enable Usage Statistics', - Theme: 'Theme', - 'Preferred Editor': 'Preferred Editor', - 'Auto-connect to IDE': 'Auto-connect to IDE', - 'Enable Prompt Completion': 'Enable Prompt Completion', - 'Debug Keystroke Logging': 'Debug Keystroke Logging', - 'Language: UI': 'Language: UI', - 'Language: Model': 'Language: Model', - 'Output Format': 'Output Format', - 'Hide Window Title': 'Hide Window Title', - 'Show Status in Title': 'Show Status in Title', - 'Hide Tips': 'Hide Tips', - 'Show Line Numbers in Code': 'Show Line Numbers in Code', - 'Show Citations': 'Show Citations', - 'Custom Witty Phrases': 'Custom Witty Phrases', - 'Show Welcome Back Dialog': 'Show Welcome Back Dialog', - 'Enable User Feedback': 'Enable User Feedback', - 'How is Qwen doing this session? (optional)': - 'How is Qwen doing this session? (optional)', - Bad: 'Bad', - Fine: 'Fine', - Good: 'Good', - Dismiss: 'Dismiss', - 'Not Sure Yet': 'Not Sure Yet', - 'Any other key': 'Any other key', - 'Disable Loading Phrases': 'Disable Loading Phrases', - 'Screen Reader Mode': 'Screen Reader Mode', - 'IDE Mode': 'IDE Mode', - 'Max Session Turns': 'Max Session Turns', - 'Skip Next Speaker Check': 'Skip Next Speaker Check', - 'Skip Loop Detection': 'Skip Loop Detection', - 'Skip Startup Context': 'Skip Startup Context', - 'Enable OpenAI Logging': 'Enable OpenAI Logging', - 'OpenAI Logging Directory': 'OpenAI Logging Directory', - Timeout: 'Timeout', - 'Max Retries': 'Max Retries', - 'Disable Cache Control': 'Disable Cache Control', - 'Memory Discovery Max Dirs': 'Memory Discovery Max Dirs', - 'Load Memory From Include Directories': - 'Load Memory From Include Directories', - 'Respect .gitignore': 'Respect .gitignore', - 'Respect .qwenignore': 'Respect .qwenignore', - 'Enable Recursive File Search': 'Enable Recursive File Search', - 'Disable Fuzzy Search': 'Disable Fuzzy Search', - 'Interactive Shell (PTY)': 'Interactive Shell (PTY)', - 'Show Color': 'Show Color', - 'Auto Accept': 'Auto Accept', - 'Use Ripgrep': 'Use Ripgrep', - 'Use Builtin Ripgrep': 'Use Builtin Ripgrep', - 'Enable Tool Output Truncation': 'Enable Tool Output Truncation', - 'Tool Output Truncation Threshold': 'Tool Output Truncation Threshold', - 'Tool Output Truncation Lines': 'Tool Output Truncation Lines', - 'Folder Trust': 'Folder Trust', - 'Vision Model Preview': 'Vision Model Preview', - 'Tool Schema Compliance': 'Tool Schema Compliance', + "Vim Mode": "Vim Mode", + "Disable Auto Update": "Disable Auto Update", + "Attribution: commit": "Attribution: commit", + "Terminal Bell Notification": "Terminal Bell Notification", + "Enable Usage Statistics": "Enable Usage Statistics", + Theme: "Theme", + "Preferred Editor": "Preferred Editor", + "Auto-connect to IDE": "Auto-connect to IDE", + "Enable Prompt Completion": "Enable Prompt Completion", + "Debug Keystroke Logging": "Debug Keystroke Logging", + "Language: UI": "Language: UI", + "Language: Model": "Language: Model", + "Output Format": "Output Format", + "Hide Window Title": "Hide Window Title", + "Show Status in Title": "Show Status in Title", + "Hide Tips": "Hide Tips", + "Show Line Numbers in Code": "Show Line Numbers in Code", + "Show Citations": "Show Citations", + "Custom Witty Phrases": "Custom Witty Phrases", + "Show Welcome Back Dialog": "Show Welcome Back Dialog", + "Enable User Feedback": "Enable User Feedback", + "How is Qwen doing this session? (optional)": "How is Qwen doing this session? (optional)", + Bad: "Bad", + Fine: "Fine", + Good: "Good", + Dismiss: "Dismiss", + "Not Sure Yet": "Not Sure Yet", + "Any other key": "Any other key", + "Disable Loading Phrases": "Disable Loading Phrases", + "Screen Reader Mode": "Screen Reader Mode", + "IDE Mode": "IDE Mode", + "Max Session Turns": "Max Session Turns", + "Skip Next Speaker Check": "Skip Next Speaker Check", + "Skip Loop Detection": "Skip Loop Detection", + "Skip Startup Context": "Skip Startup Context", + "Enable OpenAI Logging": "Enable OpenAI Logging", + "OpenAI Logging Directory": "OpenAI Logging Directory", + Timeout: "Timeout", + "Max Retries": "Max Retries", + "Disable Cache Control": "Disable Cache Control", + "Memory Discovery Max Dirs": "Memory Discovery Max Dirs", + "Load Memory From Include Directories": "Load Memory From Include Directories", + "Respect .gitignore": "Respect .gitignore", + "Respect .qwenignore": "Respect .qwenignore", + "Enable Recursive File Search": "Enable Recursive File Search", + "Disable Fuzzy Search": "Disable Fuzzy Search", + "Interactive Shell (PTY)": "Interactive Shell (PTY)", + "Show Color": "Show Color", + "Auto Accept": "Auto Accept", + "Use Ripgrep": "Use Ripgrep", + "Use Builtin Ripgrep": "Use Builtin Ripgrep", + "Enable Tool Output Truncation": "Enable Tool Output Truncation", + "Tool Output Truncation Threshold": "Tool Output Truncation Threshold", + "Tool Output Truncation Lines": "Tool Output Truncation Lines", + "Folder Trust": "Folder Trust", + "Vision Model Preview": "Vision Model Preview", + "Tool Schema Compliance": "Tool Schema Compliance", // Settings enum options - 'Auto (detect from system)': 'Auto (detect from system)', - Text: 'Text', - JSON: 'JSON', - Plan: 'Plan', - Default: 'Default', - 'Auto Edit': 'Auto Edit', - YOLO: 'YOLO', - 'toggle vim mode on/off': 'toggle vim mode on/off', - 'check session stats. Usage: /stats [model|tools]': - 'check session stats. Usage: /stats [model|tools]', - 'Show model-specific usage statistics.': - 'Show model-specific usage statistics.', - 'Show tool-specific usage statistics.': - 'Show tool-specific usage statistics.', - 'exit the cli': 'exit the cli', - 'Open MCP management dialog, or authenticate with OAuth-enabled servers': - 'Open MCP management dialog, or authenticate with OAuth-enabled servers', - 'List configured MCP servers and tools, or authenticate with OAuth-enabled servers': - 'List configured MCP servers and tools, or authenticate with OAuth-enabled servers', - 'Manage workspace directories': 'Manage workspace directories', - 'Add directories to the workspace. Use comma to separate multiple paths': - 'Add directories to the workspace. Use comma to separate multiple paths', - 'Show all directories in the workspace': - 'Show all directories in the workspace', - 'set external editor preference': 'set external editor preference', - 'Select Editor': 'Select Editor', - 'Editor Preference': 'Editor Preference', - 'These editors are currently supported. Please note that some editors cannot be used in sandbox mode.': - 'These editors are currently supported. Please note that some editors cannot be used in sandbox mode.', - 'Your preferred editor is:': 'Your preferred editor is:', - 'Manage extensions': 'Manage extensions', - 'Manage installed extensions': 'Manage installed extensions', - 'List active extensions': 'List active extensions', - 'Update extensions. Usage: update |--all': - 'Update extensions. Usage: update |--all', - 'Disable an extension': 'Disable an extension', - 'Enable an extension': 'Enable an extension', - 'Install an extension from a git repo or local path': - 'Install an extension from a git repo or local path', - 'Uninstall an extension': 'Uninstall an extension', - 'No extensions installed.': 'No extensions installed.', - 'Usage: /extensions update |--all': - 'Usage: /extensions update |--all', + "Auto (detect from system)": "Auto (detect from system)", + Text: "Text", + JSON: "JSON", + Plan: "Plan", + Default: "Default", + "Auto Edit": "Auto Edit", + YOLO: "YOLO", + "toggle vim mode on/off": "toggle vim mode on/off", + "check session stats. Usage: /stats [model|tools]": + "check session stats. Usage: /stats [model|tools]", + "Show model-specific usage statistics.": "Show model-specific usage statistics.", + "Show tool-specific usage statistics.": "Show tool-specific usage statistics.", + "exit the cli": "exit the cli", + "Open MCP management dialog, or authenticate with OAuth-enabled servers": + "Open MCP management dialog, or authenticate with OAuth-enabled servers", + "List configured MCP servers and tools, or authenticate with OAuth-enabled servers": + "List configured MCP servers and tools, or authenticate with OAuth-enabled servers", + "Manage workspace directories": "Manage workspace directories", + "Add directories to the workspace. Use comma to separate multiple paths": + "Add directories to the workspace. Use comma to separate multiple paths", + "Show all directories in the workspace": "Show all directories in the workspace", + "set external editor preference": "set external editor preference", + "Select Editor": "Select Editor", + "Editor Preference": "Editor Preference", + "These editors are currently supported. Please note that some editors cannot be used in sandbox mode.": + "These editors are currently supported. Please note that some editors cannot be used in sandbox mode.", + "Your preferred editor is:": "Your preferred editor is:", + "Manage extensions": "Manage extensions", + "Manage installed extensions": "Manage installed extensions", + "List active extensions": "List active extensions", + "Update extensions. Usage: update |--all": + "Update extensions. Usage: update |--all", + "Disable an extension": "Disable an extension", + "Enable an extension": "Enable an extension", + "Install an extension from a git repo or local path": + "Install an extension from a git repo or local path", + "Uninstall an extension": "Uninstall an extension", + "No extensions installed.": "No extensions installed.", + "Usage: /extensions update |--all": + "Usage: /extensions update |--all", 'Extension "{{name}}" not found.': 'Extension "{{name}}" not found.', - 'No extensions to update.': 'No extensions to update.', - 'Usage: /extensions install ': 'Usage: /extensions install ', - 'Installing extension from "{{source}}"...': - 'Installing extension from "{{source}}"...', - 'Extension "{{name}}" installed successfully.': - 'Extension "{{name}}" installed successfully.', + "No extensions to update.": "No extensions to update.", + "Usage: /extensions install ": "Usage: /extensions install ", + 'Installing extension from "{{source}}"...': 'Installing extension from "{{source}}"...', + 'Extension "{{name}}" installed successfully.': 'Extension "{{name}}" installed successfully.', 'Failed to install extension from "{{source}}": {{error}}': 'Failed to install extension from "{{source}}": {{error}}', - 'Usage: /extensions uninstall ': - 'Usage: /extensions uninstall ', - 'Uninstalling extension "{{name}}"...': - 'Uninstalling extension "{{name}}"...', + "Usage: /extensions uninstall ": "Usage: /extensions uninstall ", + 'Uninstalling extension "{{name}}"...': 'Uninstalling extension "{{name}}"...', 'Extension "{{name}}" uninstalled successfully.': 'Extension "{{name}}" uninstalled successfully.', 'Failed to uninstall extension "{{name}}": {{error}}': 'Failed to uninstall extension "{{name}}": {{error}}', - 'Usage: /extensions {{command}} [--scope=]': - 'Usage: /extensions {{command}} [--scope=]', + "Usage: /extensions {{command}} [--scope=]": + "Usage: /extensions {{command}} [--scope=]", 'Unsupported scope "{{scope}}", should be one of "user" or "workspace"': 'Unsupported scope "{{scope}}", should be one of "user" or "workspace"', 'Extension "{{name}}" disabled for scope "{{scope}}"': 'Extension "{{name}}" disabled for scope "{{scope}}"', 'Extension "{{name}}" enabled for scope "{{scope}}"': 'Extension "{{name}}" enabled for scope "{{scope}}"', - 'Do you want to continue? [Y/n]: ': 'Do you want to continue? [Y/n]: ', - 'Do you want to continue?': 'Do you want to continue?', + "Do you want to continue? [Y/n]: ": "Do you want to continue? [Y/n]: ", + "Do you want to continue?": "Do you want to continue?", 'Installing extension "{{name}}".': 'Installing extension "{{name}}".', - '**Extensions may introduce unexpected behavior. Ensure you have investigated the extension source and trust the author.**': - '**Extensions may introduce unexpected behavior. Ensure you have investigated the extension source and trust the author.**', - 'This extension will run the following MCP servers:': - 'This extension will run the following MCP servers:', - local: 'local', - remote: 'remote', - 'This extension will add the following commands: {{commands}}.': - 'This extension will add the following commands: {{commands}}.', - 'This extension will append info to your QWEN.md context using {{fileName}}': - 'This extension will append info to your QWEN.md context using {{fileName}}', - 'This extension will exclude the following core tools: {{tools}}': - 'This extension will exclude the following core tools: {{tools}}', - 'This extension will install the following skills:': - 'This extension will install the following skills:', - 'This extension will install the following subagents:': - 'This extension will install the following subagents:', - 'Installation cancelled for "{{name}}".': - 'Installation cancelled for "{{name}}".', - 'You are installing an extension from {{originSource}}. Some features may not work perfectly with Qwen Code.': - 'You are installing an extension from {{originSource}}. Some features may not work perfectly with Qwen Code.', - '--ref and --auto-update are not applicable for marketplace extensions.': - '--ref and --auto-update are not applicable for marketplace extensions.', + "**Extensions may introduce unexpected behavior. Ensure you have investigated the extension source and trust the author.**": + "**Extensions may introduce unexpected behavior. Ensure you have investigated the extension source and trust the author.**", + "This extension will run the following MCP servers:": + "This extension will run the following MCP servers:", + local: "local", + remote: "remote", + "This extension will add the following commands: {{commands}}.": + "This extension will add the following commands: {{commands}}.", + "This extension will append info to your QWEN.md context using {{fileName}}": + "This extension will append info to your QWEN.md context using {{fileName}}", + "This extension will exclude the following core tools: {{tools}}": + "This extension will exclude the following core tools: {{tools}}", + "This extension will install the following skills:": + "This extension will install the following skills:", + "This extension will install the following subagents:": + "This extension will install the following subagents:", + 'Installation cancelled for "{{name}}".': 'Installation cancelled for "{{name}}".', + "You are installing an extension from {{originSource}}. Some features may not work perfectly with Qwen Code.": + "You are installing an extension from {{originSource}}. Some features may not work perfectly with Qwen Code.", + "--ref and --auto-update are not applicable for marketplace extensions.": + "--ref and --auto-update are not applicable for marketplace extensions.", 'Extension "{{name}}" installed successfully and enabled.': 'Extension "{{name}}" installed successfully and enabled.', - 'Installs an extension from a git repository URL, local path, or claude marketplace (marketplace-url:plugin-name).': - 'Installs an extension from a git repository URL, local path, or claude marketplace (marketplace-url:plugin-name).', - 'The github URL, local path, or marketplace source (marketplace-url:plugin-name) of the extension to install.': - 'The github URL, local path, or marketplace source (marketplace-url:plugin-name) of the extension to install.', - 'The git ref to install from.': 'The git ref to install from.', - 'Enable auto-update for this extension.': - 'Enable auto-update for this extension.', - 'Enable pre-release versions for this extension.': - 'Enable pre-release versions for this extension.', - 'Acknowledge the security risks of installing an extension and skip the confirmation prompt.': - 'Acknowledge the security risks of installing an extension and skip the confirmation prompt.', - 'The source argument must be provided.': - 'The source argument must be provided.', + "Installs an extension from a git repository URL, local path, or claude marketplace (marketplace-url:plugin-name).": + "Installs an extension from a git repository URL, local path, or claude marketplace (marketplace-url:plugin-name).", + "The github URL, local path, or marketplace source (marketplace-url:plugin-name) of the extension to install.": + "The github URL, local path, or marketplace source (marketplace-url:plugin-name) of the extension to install.", + "The git ref to install from.": "The git ref to install from.", + "Enable auto-update for this extension.": "Enable auto-update for this extension.", + "Enable pre-release versions for this extension.": + "Enable pre-release versions for this extension.", + "Acknowledge the security risks of installing an extension and skip the confirmation prompt.": + "Acknowledge the security risks of installing an extension and skip the confirmation prompt.", + "The source argument must be provided.": "The source argument must be provided.", 'Extension "{{name}}" successfully uninstalled.': 'Extension "{{name}}" successfully uninstalled.', - 'Uninstalls an extension.': 'Uninstalls an extension.', - 'The name or source path of the extension to uninstall.': - 'The name or source path of the extension to uninstall.', - 'Please include the name of the extension to uninstall as a positional argument.': - 'Please include the name of the extension to uninstall as a positional argument.', - 'Enables an extension.': 'Enables an extension.', - 'The name of the extension to enable.': - 'The name of the extension to enable.', - 'The scope to enable the extenison in. If not set, will be enabled in all scopes.': - 'The scope to enable the extenison in. If not set, will be enabled in all scopes.', + "Uninstalls an extension.": "Uninstalls an extension.", + "The name or source path of the extension to uninstall.": + "The name or source path of the extension to uninstall.", + "Please include the name of the extension to uninstall as a positional argument.": + "Please include the name of the extension to uninstall as a positional argument.", + "Enables an extension.": "Enables an extension.", + "The name of the extension to enable.": "The name of the extension to enable.", + "The scope to enable the extenison in. If not set, will be enabled in all scopes.": + "The scope to enable the extenison in. If not set, will be enabled in all scopes.", 'Extension "{{name}}" successfully enabled for scope "{{scope}}".': 'Extension "{{name}}" successfully enabled for scope "{{scope}}".', 'Extension "{{name}}" successfully enabled in all scopes.': 'Extension "{{name}}" successfully enabled in all scopes.', - 'Invalid scope: {{scope}}. Please use one of {{scopes}}.': - 'Invalid scope: {{scope}}. Please use one of {{scopes}}.', - 'Disables an extension.': 'Disables an extension.', - 'The name of the extension to disable.': - 'The name of the extension to disable.', - 'The scope to disable the extenison in.': - 'The scope to disable the extenison in.', + "Invalid scope: {{scope}}. Please use one of {{scopes}}.": + "Invalid scope: {{scope}}. Please use one of {{scopes}}.", + "Disables an extension.": "Disables an extension.", + "The name of the extension to disable.": "The name of the extension to disable.", + "The scope to disable the extenison in.": "The scope to disable the extenison in.", 'Extension "{{name}}" successfully disabled for scope "{{scope}}".': 'Extension "{{name}}" successfully disabled for scope "{{scope}}".', 'Extension "{{name}}" successfully updated: {{oldVersion}} → {{newVersion}}.': 'Extension "{{name}}" successfully updated: {{oldVersion}} → {{newVersion}}.', 'Unable to install extension "{{name}}" due to missing install metadata': 'Unable to install extension "{{name}}" due to missing install metadata', - 'Extension "{{name}}" is already up to date.': - 'Extension "{{name}}" is already up to date.', - 'Updates all extensions or a named extension to the latest version.': - 'Updates all extensions or a named extension to the latest version.', - 'Update all extensions.': 'Update all extensions.', - 'Either an extension name or --all must be provided': - 'Either an extension name or --all must be provided', - 'Lists installed extensions.': 'Lists installed extensions.', - 'Path:': 'Path:', - 'Source:': 'Source:', - 'Type:': 'Type:', - 'Ref:': 'Ref:', - 'Release tag:': 'Release tag:', - 'Enabled (User):': 'Enabled (User):', - 'Enabled (Workspace):': 'Enabled (Workspace):', - 'Context files:': 'Context files:', - 'Skills:': 'Skills:', - 'Agents:': 'Agents:', - 'MCP servers:': 'MCP servers:', - 'Link extension failed to install.': 'Link extension failed to install.', + 'Extension "{{name}}" is already up to date.': 'Extension "{{name}}" is already up to date.', + "Updates all extensions or a named extension to the latest version.": + "Updates all extensions or a named extension to the latest version.", + "Update all extensions.": "Update all extensions.", + "Either an extension name or --all must be provided": + "Either an extension name or --all must be provided", + "Lists installed extensions.": "Lists installed extensions.", + "Path:": "Path:", + "Source:": "Source:", + "Type:": "Type:", + "Ref:": "Ref:", + "Release tag:": "Release tag:", + "Enabled (User):": "Enabled (User):", + "Enabled (Workspace):": "Enabled (Workspace):", + "Context files:": "Context files:", + "Skills:": "Skills:", + "Agents:": "Agents:", + "MCP servers:": "MCP servers:", + "Link extension failed to install.": "Link extension failed to install.", 'Extension "{{name}}" linked successfully and enabled.': 'Extension "{{name}}" linked successfully and enabled.', - 'Links an extension from a local path. Updates made to the local path will always be reflected.': - 'Links an extension from a local path. Updates made to the local path will always be reflected.', - 'The name of the extension to link.': 'The name of the extension to link.', - 'Set a specific setting for an extension.': - 'Set a specific setting for an extension.', - 'Name of the extension to configure.': 'Name of the extension to configure.', - 'The setting to configure (name or env var).': - 'The setting to configure (name or env var).', - 'The scope to set the setting in.': 'The scope to set the setting in.', - 'List all settings for an extension.': 'List all settings for an extension.', - 'Name of the extension.': 'Name of the extension.', + "Links an extension from a local path. Updates made to the local path will always be reflected.": + "Links an extension from a local path. Updates made to the local path will always be reflected.", + "The name of the extension to link.": "The name of the extension to link.", + "Set a specific setting for an extension.": "Set a specific setting for an extension.", + "Name of the extension to configure.": "Name of the extension to configure.", + "The setting to configure (name or env var).": "The setting to configure (name or env var).", + "The scope to set the setting in.": "The scope to set the setting in.", + "List all settings for an extension.": "List all settings for an extension.", + "Name of the extension.": "Name of the extension.", 'Extension "{{name}}" has no settings to configure.': 'Extension "{{name}}" has no settings to configure.', 'Settings for "{{name}}":': 'Settings for "{{name}}":', - '(workspace)': '(workspace)', - '(user)': '(user)', - '[not set]': '[not set]', - '[value stored in keychain]': '[value stored in keychain]', - 'Value:': 'Value:', - 'Manage extension settings.': 'Manage extension settings.', - 'You need to specify a command (set or list).': - 'You need to specify a command (set or list).', + "(workspace)": "(workspace)", + "(user)": "(user)", + "[not set]": "[not set]", + "[value stored in keychain]": "[value stored in keychain]", + "Value:": "Value:", + "Manage extension settings.": "Manage extension settings.", + "You need to specify a command (set or list).": "You need to specify a command (set or list).", // ============================================================================ // Plugin Choice / Marketplace // ============================================================================ - 'No plugins available in this marketplace.': - 'No plugins available in this marketplace.', + "No plugins available in this marketplace.": "No plugins available in this marketplace.", 'Select a plugin to install from marketplace "{{name}}":': 'Select a plugin to install from marketplace "{{name}}":', - 'Plugin selection cancelled.': 'Plugin selection cancelled.', + "Plugin selection cancelled.": "Plugin selection cancelled.", 'Select a plugin from "{{name}}"': 'Select a plugin from "{{name}}"', - 'Use ↑↓ or j/k to navigate, Enter to select, Escape to cancel': - 'Use ↑↓ or j/k to navigate, Enter to select, Escape to cancel', - '{{count}} more above': '{{count}} more above', - '{{count}} more below': '{{count}} more below', - 'manage IDE integration': 'manage IDE integration', - 'check status of IDE integration': 'check status of IDE integration', - 'install required IDE companion for {{ideName}}': - 'install required IDE companion for {{ideName}}', - 'enable IDE integration': 'enable IDE integration', - 'disable IDE integration': 'disable IDE integration', - '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.': - '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.', - 'Set up GitHub Actions': 'Set up GitHub Actions', - 'Configure terminal keybindings for multiline input (VS Code, Cursor, Windsurf, Trae)': - 'Configure terminal keybindings for multiline input (VS Code, Cursor, Windsurf, Trae)', - 'Please restart your terminal for the changes to take effect.': - 'Please restart your terminal for the changes to take effect.', - 'Failed to configure terminal: {{error}}': - 'Failed to configure terminal: {{error}}', - 'Could not determine {{terminalName}} config path on Windows: APPDATA environment variable is not set.': - 'Could not determine {{terminalName}} config path on Windows: APPDATA environment variable is not set.', - '{{terminalName}} keybindings.json exists but is not a valid JSON array. Please fix the file manually or delete it to allow automatic configuration.': - '{{terminalName}} keybindings.json exists but is not a valid JSON array. Please fix the file manually or delete it to allow automatic configuration.', - 'File: {{file}}': 'File: {{file}}', - 'Failed to parse {{terminalName}} keybindings.json. The file contains invalid JSON. Please fix the file manually or delete it to allow automatic configuration.': - 'Failed to parse {{terminalName}} keybindings.json. The file contains invalid JSON. Please fix the file manually or delete it to allow automatic configuration.', - 'Error: {{error}}': 'Error: {{error}}', - 'Shift+Enter binding already exists': 'Shift+Enter binding already exists', - 'Ctrl+Enter binding already exists': 'Ctrl+Enter binding already exists', - 'Existing keybindings detected. Will not modify to avoid conflicts.': - 'Existing keybindings detected. Will not modify to avoid conflicts.', - 'Please check and modify manually if needed: {{file}}': - 'Please check and modify manually if needed: {{file}}', - 'Added Shift+Enter and Ctrl+Enter keybindings to {{terminalName}}.': - 'Added Shift+Enter and Ctrl+Enter keybindings to {{terminalName}}.', - 'Modified: {{file}}': 'Modified: {{file}}', - '{{terminalName}} keybindings already configured.': - '{{terminalName}} keybindings already configured.', - 'Failed to configure {{terminalName}}.': - 'Failed to configure {{terminalName}}.', - 'Your terminal is already configured for an optimal experience with multiline input (Shift+Enter and Ctrl+Enter).': - 'Your terminal is already configured for an optimal experience with multiline input (Shift+Enter and Ctrl+Enter).', + "Use ↑↓ or j/k to navigate, Enter to select, Escape to cancel": + "Use ↑↓ or j/k to navigate, Enter to select, Escape to cancel", + "{{count}} more above": "{{count}} more above", + "{{count}} more below": "{{count}} more below", + "manage IDE integration": "manage IDE integration", + "check status of IDE integration": "check status of IDE integration", + "install required IDE companion for {{ideName}}": + "install required IDE companion for {{ideName}}", + "enable IDE integration": "enable IDE integration", + "disable IDE integration": "disable IDE integration", + "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.": + "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.", + "Set up GitHub Actions": "Set up GitHub Actions", + "Configure terminal keybindings for multiline input (VS Code, Cursor, Windsurf, Trae)": + "Configure terminal keybindings for multiline input (VS Code, Cursor, Windsurf, Trae)", + "Please restart your terminal for the changes to take effect.": + "Please restart your terminal for the changes to take effect.", + "Failed to configure terminal: {{error}}": "Failed to configure terminal: {{error}}", + "Could not determine {{terminalName}} config path on Windows: APPDATA environment variable is not set.": + "Could not determine {{terminalName}} config path on Windows: APPDATA environment variable is not set.", + "{{terminalName}} keybindings.json exists but is not a valid JSON array. Please fix the file manually or delete it to allow automatic configuration.": + "{{terminalName}} keybindings.json exists but is not a valid JSON array. Please fix the file manually or delete it to allow automatic configuration.", + "File: {{file}}": "File: {{file}}", + "Failed to parse {{terminalName}} keybindings.json. The file contains invalid JSON. Please fix the file manually or delete it to allow automatic configuration.": + "Failed to parse {{terminalName}} keybindings.json. The file contains invalid JSON. Please fix the file manually or delete it to allow automatic configuration.", + "Error: {{error}}": "Error: {{error}}", + "Shift+Enter binding already exists": "Shift+Enter binding already exists", + "Ctrl+Enter binding already exists": "Ctrl+Enter binding already exists", + "Existing keybindings detected. Will not modify to avoid conflicts.": + "Existing keybindings detected. Will not modify to avoid conflicts.", + "Please check and modify manually if needed: {{file}}": + "Please check and modify manually if needed: {{file}}", + "Added Shift+Enter and Ctrl+Enter keybindings to {{terminalName}}.": + "Added Shift+Enter and Ctrl+Enter keybindings to {{terminalName}}.", + "Modified: {{file}}": "Modified: {{file}}", + "{{terminalName}} keybindings already configured.": + "{{terminalName}} keybindings already configured.", + "Failed to configure {{terminalName}}.": "Failed to configure {{terminalName}}.", + "Your terminal is already configured for an optimal experience with multiline input (Shift+Enter and Ctrl+Enter).": + "Your terminal is already configured for an optimal experience with multiline input (Shift+Enter and Ctrl+Enter).", // ============================================================================ // Commands - Hooks // ============================================================================ - 'Manage Qwen Code hooks': 'Manage Qwen Code hooks', - 'List all configured hooks': 'List all configured hooks', - 'Enable a disabled hook': 'Enable a disabled hook', - 'Disable an active hook': 'Disable an active hook', + "Manage Qwen Code hooks": "Manage Qwen Code hooks", + "List all configured hooks": "List all configured hooks", + "Enable a disabled hook": "Enable a disabled hook", + "Disable an active hook": "Disable an active hook", // Hooks - Dialog - Hooks: 'Hooks', - 'Loading hooks...': 'Loading hooks...', - 'Error loading hooks:': 'Error loading hooks:', - 'Press Escape to close': 'Press Escape to close', - 'Press Escape, Ctrl+C, or Ctrl+D to cancel': - 'Press Escape, Ctrl+C, or Ctrl+D to cancel', - 'Press Space, Enter, or Escape to dismiss': - 'Press Space, Enter, or Escape to dismiss', - 'No hook selected': 'No hook selected', + Hooks: "Hooks", + "Loading hooks...": "Loading hooks...", + "Error loading hooks:": "Error loading hooks:", + "Press Escape to close": "Press Escape to close", + "Press Escape, Ctrl+C, or Ctrl+D to cancel": "Press Escape, Ctrl+C, or Ctrl+D to cancel", + "Press Space, Enter, or Escape to dismiss": "Press Space, Enter, or Escape to dismiss", + "No hook selected": "No hook selected", // Hooks - List Step - 'No hook events found.': 'No hook events found.', - '{{count}} hook configured': '{{count}} hook configured', - '{{count}} hooks configured': '{{count}} hooks configured', - 'This menu is read-only. To add or modify hooks, edit settings.json directly or ask Qwen Code.': - 'This menu is read-only. To add or modify hooks, edit settings.json directly or ask Qwen Code.', - 'Enter to select · Esc to cancel': 'Enter to select · Esc to cancel', + "No hook events found.": "No hook events found.", + "{{count}} hook configured": "{{count}} hook configured", + "{{count}} hooks configured": "{{count}} hooks configured", + "This menu is read-only. To add or modify hooks, edit settings.json directly or ask Qwen Code.": + "This menu is read-only. To add or modify hooks, edit settings.json directly or ask Qwen Code.", + "Enter to select · Esc to cancel": "Enter to select · Esc to cancel", // Hooks - Detail Step - 'Exit codes:': 'Exit codes:', - 'Configured hooks:': 'Configured hooks:', - 'No hooks configured for this event.': 'No hooks configured for this event.', - 'To add hooks, edit settings.json directly or ask Qwen.': - 'To add hooks, edit settings.json directly or ask Qwen.', - 'Enter to select · Esc to go back': 'Enter to select · Esc to go back', + "Exit codes:": "Exit codes:", + "Configured hooks:": "Configured hooks:", + "No hooks configured for this event.": "No hooks configured for this event.", + "To add hooks, edit settings.json directly or ask Qwen.": + "To add hooks, edit settings.json directly or ask Qwen.", + "Enter to select · Esc to go back": "Enter to select · Esc to go back", // Hooks - Config Detail Step - 'Hook details': 'Hook details', - 'Event:': 'Event:', - 'Extension:': 'Extension:', - 'Desc:': 'Desc:', - 'No hook config selected': 'No hook config selected', - 'To modify or remove this hook, edit settings.json directly or ask Qwen to help.': - 'To modify or remove this hook, edit settings.json directly or ask Qwen to help.', + "Hook details": "Hook details", + "Event:": "Event:", + "Extension:": "Extension:", + "Desc:": "Desc:", + "No hook config selected": "No hook config selected", + "To modify or remove this hook, edit settings.json directly or ask Qwen to help.": + "To modify or remove this hook, edit settings.json directly or ask Qwen to help.", // Hooks - Disabled Step - 'Hook Configuration - Disabled': 'Hook Configuration - Disabled', - 'All hooks are currently disabled. You have {{count}} that are not running.': - 'All hooks are currently disabled. You have {{count}} that are not running.', - '{{count}} configured hook': '{{count}} configured hook', - '{{count}} configured hooks': '{{count}} configured hooks', - 'When hooks are disabled:': 'When hooks are disabled:', - 'No hook commands will execute': 'No hook commands will execute', - 'StatusLine will not be displayed': 'StatusLine will not be displayed', - 'Tool operations will proceed without hook validation': - 'Tool operations will proceed without hook validation', + "Hook Configuration - Disabled": "Hook Configuration - Disabled", + "All hooks are currently disabled. You have {{count}} that are not running.": + "All hooks are currently disabled. You have {{count}} that are not running.", + "{{count}} configured hook": "{{count}} configured hook", + "{{count}} configured hooks": "{{count}} configured hooks", + "When hooks are disabled:": "When hooks are disabled:", + "No hook commands will execute": "No hook commands will execute", + "StatusLine will not be displayed": "StatusLine will not be displayed", + "Tool operations will proceed without hook validation": + "Tool operations will proceed without hook validation", 'To re-enable hooks, remove "disableAllHooks" from settings.json or ask Qwen Code.': 'To re-enable hooks, remove "disableAllHooks" from settings.json or ask Qwen Code.', // Hooks - Source - Project: 'Project', - User: 'User', - System: 'System', - Extension: 'Extension', - 'Local Settings': 'Local Settings', - 'User Settings': 'User Settings', - 'System Settings': 'System Settings', - Extensions: 'Extensions', + Project: "Project", + User: "User", + System: "System", + Extension: "Extension", + "Local Settings": "Local Settings", + "User Settings": "User Settings", + "System Settings": "System Settings", + Extensions: "Extensions", // Hooks - Status - '✓ Enabled': '✓ Enabled', - '✗ Disabled': '✗ Disabled', + "✓ Enabled": "✓ Enabled", + "✗ Disabled": "✗ Disabled", // Hooks - Event Descriptions (short) - 'Before tool execution': 'Before tool execution', - 'After tool execution': 'After tool execution', - 'After tool execution fails': 'After tool execution fails', - 'When notifications are sent': 'When notifications are sent', - 'When the user submits a prompt': 'When the user submits a prompt', - 'When a new session is started': 'When a new session is started', - 'Right before Qwen Code concludes its response': - 'Right before Qwen Code concludes its response', - 'When a subagent (Agent tool call) is started': - 'When a subagent (Agent tool call) is started', - 'Right before a subagent concludes its response': - 'Right before a subagent concludes its response', - 'Before conversation compaction': 'Before conversation compaction', - 'When a session is ending': 'When a session is ending', - 'When a permission dialog is displayed': - 'When a permission dialog is displayed', + "Before tool execution": "Before tool execution", + "After tool execution": "After tool execution", + "After tool execution fails": "After tool execution fails", + "When notifications are sent": "When notifications are sent", + "When the user submits a prompt": "When the user submits a prompt", + "When a new session is started": "When a new session is started", + "Right before Qwen Code concludes its response": "Right before Qwen Code concludes its response", + "When a subagent (Agent tool call) is started": "When a subagent (Agent tool call) is started", + "Right before a subagent concludes its response": + "Right before a subagent concludes its response", + "Before conversation compaction": "Before conversation compaction", + "When a session is ending": "When a session is ending", + "When a permission dialog is displayed": "When a permission dialog is displayed", // Hooks - Event Descriptions (detailed) - 'Input to command is JSON of tool call arguments.': - 'Input to command is JSON of tool call arguments.', + "Input to command is JSON of tool call arguments.": + "Input to command is JSON of tool call arguments.", 'Input to command is JSON with fields "inputs" (tool call arguments) and "response" (tool call response).': 'Input to command is JSON with fields "inputs" (tool call arguments) and "response" (tool call response).', - 'Input to command is JSON with tool_name, tool_input, tool_use_id, error, error_type, is_interrupt, and is_timeout.': - 'Input to command is JSON with tool_name, tool_input, tool_use_id, error, error_type, is_interrupt, and is_timeout.', - 'Input to command is JSON with notification message and type.': - 'Input to command is JSON with notification message and type.', - 'Input to command is JSON with original user prompt text.': - 'Input to command is JSON with original user prompt text.', - 'Input to command is JSON with session start source.': - 'Input to command is JSON with session start source.', - 'Input to command is JSON with session end reason.': - 'Input to command is JSON with session end reason.', - 'Input to command is JSON with agent_id and agent_type.': - 'Input to command is JSON with agent_id and agent_type.', - 'Input to command is JSON with agent_id, agent_type, and agent_transcript_path.': - 'Input to command is JSON with agent_id, agent_type, and agent_transcript_path.', - 'Input to command is JSON with compaction details.': - 'Input to command is JSON with compaction details.', - 'Input to command is JSON with tool_name, tool_input, and tool_use_id. Output JSON with hookSpecificOutput containing decision to allow or deny.': - 'Input to command is JSON with tool_name, tool_input, and tool_use_id. Output JSON with hookSpecificOutput containing decision to allow or deny.', + "Input to command is JSON with tool_name, tool_input, tool_use_id, error, error_type, is_interrupt, and is_timeout.": + "Input to command is JSON with tool_name, tool_input, tool_use_id, error, error_type, is_interrupt, and is_timeout.", + "Input to command is JSON with notification message and type.": + "Input to command is JSON with notification message and type.", + "Input to command is JSON with original user prompt text.": + "Input to command is JSON with original user prompt text.", + "Input to command is JSON with session start source.": + "Input to command is JSON with session start source.", + "Input to command is JSON with session end reason.": + "Input to command is JSON with session end reason.", + "Input to command is JSON with agent_id and agent_type.": + "Input to command is JSON with agent_id and agent_type.", + "Input to command is JSON with agent_id, agent_type, and agent_transcript_path.": + "Input to command is JSON with agent_id, agent_type, and agent_transcript_path.", + "Input to command is JSON with compaction details.": + "Input to command is JSON with compaction details.", + "Input to command is JSON with tool_name, tool_input, and tool_use_id. Output JSON with hookSpecificOutput containing decision to allow or deny.": + "Input to command is JSON with tool_name, tool_input, and tool_use_id. Output JSON with hookSpecificOutput containing decision to allow or deny.", // Hooks - Exit Code Descriptions - 'stdout/stderr not shown': 'stdout/stderr not shown', - 'show stderr to model and continue conversation': - 'show stderr to model and continue conversation', - 'show stderr to user only': 'show stderr to user only', - 'stdout shown in transcript mode (ctrl+o)': - 'stdout shown in transcript mode (ctrl+o)', - 'show stderr to model immediately': 'show stderr to model immediately', - 'show stderr to user only but continue with tool call': - 'show stderr to user only but continue with tool call', - 'block processing, erase original prompt, and show stderr to user only': - 'block processing, erase original prompt, and show stderr to user only', - 'stdout shown to Qwen': 'stdout shown to Qwen', - 'show stderr to user only (blocking errors ignored)': - 'show stderr to user only (blocking errors ignored)', - 'command completes successfully': 'command completes successfully', - 'stdout shown to subagent': 'stdout shown to subagent', - 'show stderr to subagent and continue having it run': - 'show stderr to subagent and continue having it run', - 'stdout appended as custom compact instructions': - 'stdout appended as custom compact instructions', - 'block compaction': 'block compaction', - 'show stderr to user only but continue with compaction': - 'show stderr to user only but continue with compaction', - 'use hook decision if provided': 'use hook decision if provided', + "stdout/stderr not shown": "stdout/stderr not shown", + "show stderr to model and continue conversation": + "show stderr to model and continue conversation", + "show stderr to user only": "show stderr to user only", + "stdout shown in transcript mode (ctrl+o)": "stdout shown in transcript mode (ctrl+o)", + "show stderr to model immediately": "show stderr to model immediately", + "show stderr to user only but continue with tool call": + "show stderr to user only but continue with tool call", + "block processing, erase original prompt, and show stderr to user only": + "block processing, erase original prompt, and show stderr to user only", + "stdout shown to Qwen": "stdout shown to Qwen", + "show stderr to user only (blocking errors ignored)": + "show stderr to user only (blocking errors ignored)", + "command completes successfully": "command completes successfully", + "stdout shown to subagent": "stdout shown to subagent", + "show stderr to subagent and continue having it run": + "show stderr to subagent and continue having it run", + "stdout appended as custom compact instructions": + "stdout appended as custom compact instructions", + "block compaction": "block compaction", + "show stderr to user only but continue with compaction": + "show stderr to user only but continue with compaction", + "use hook decision if provided": "use hook decision if provided", // Hooks - Messages - 'Config not loaded.': 'Config not loaded.', - 'Hooks are not enabled. Enable hooks in settings to use this feature.': - 'Hooks are not enabled. Enable hooks in settings to use this feature.', - 'No hooks configured. Add hooks in your settings.json file.': - 'No hooks configured. Add hooks in your settings.json file.', - 'Configured Hooks ({{count}} total)': 'Configured Hooks ({{count}} total)', + "Config not loaded.": "Config not loaded.", + "Hooks are not enabled. Enable hooks in settings to use this feature.": + "Hooks are not enabled. Enable hooks in settings to use this feature.", + "No hooks configured. Add hooks in your settings.json file.": + "No hooks configured. Add hooks in your settings.json file.", + "Configured Hooks ({{count}} total)": "Configured Hooks ({{count}} total)", // ============================================================================ // Commands - Session Export // ============================================================================ - 'Export current session message history to a file': - 'Export current session message history to a file', - 'Export session to HTML format': 'Export session to HTML format', - 'Export session to JSON format': 'Export session to JSON format', - 'Export session to JSONL format (one message per line)': - 'Export session to JSONL format (one message per line)', - 'Export session to markdown format': 'Export session to markdown format', + "Export current session message history to a file": + "Export current session message history to a file", + "Export session to HTML format": "Export session to HTML format", + "Export session to JSON format": "Export session to JSON format", + "Export session to JSONL format (one message per line)": + "Export session to JSONL format (one message per line)", + "Export session to markdown format": "Export session to markdown format", // ============================================================================ // Commands - Insights // ============================================================================ - 'generate personalized programming insights from your chat history': - 'generate personalized programming insights from your chat history', + "generate personalized programming insights from your chat history": + "generate personalized programming insights from your chat history", // ============================================================================ // Commands - Session History // ============================================================================ - 'Resume a previous session': 'Resume a previous session', - 'Restore a tool call. This will reset the conversation and file history to the state it was in when the tool call was suggested': - 'Restore a tool call. This will reset the conversation and file history to the state it was in when the tool call was suggested', - 'Could not detect terminal type. Supported terminals: VS Code, Cursor, Windsurf, and Trae.': - 'Could not detect terminal type. Supported terminals: VS Code, Cursor, Windsurf, and Trae.', - 'Terminal "{{terminal}}" is not supported yet.': - 'Terminal "{{terminal}}" is not supported yet.', + "Resume a previous session": "Resume a previous session", + "Restore a tool call. This will reset the conversation and file history to the state it was in when the tool call was suggested": + "Restore a tool call. This will reset the conversation and file history to the state it was in when the tool call was suggested", + "Could not detect terminal type. Supported terminals: VS Code, Cursor, Windsurf, and Trae.": + "Could not detect terminal type. Supported terminals: VS Code, Cursor, Windsurf, and Trae.", + 'Terminal "{{terminal}}" is not supported yet.': 'Terminal "{{terminal}}" is not supported yet.', // ============================================================================ // Commands - Language // ============================================================================ - 'Invalid language. Available: {{options}}': - 'Invalid language. Available: {{options}}', - 'Language subcommands do not accept additional arguments.': - 'Language subcommands do not accept additional arguments.', - 'Current UI language: {{lang}}': 'Current UI language: {{lang}}', - 'Current LLM output language: {{lang}}': - 'Current LLM output language: {{lang}}', - 'LLM output language not set': 'LLM output language not set', - 'Set UI language': 'Set UI language', - 'Set LLM output language': 'Set LLM output language', - 'Usage: /language ui [{{options}}]': 'Usage: /language ui [{{options}}]', - 'Usage: /language output ': 'Usage: /language output ', - 'Example: /language output 中文': 'Example: /language output 中文', - 'Example: /language output English': 'Example: /language output English', - 'Example: /language output 日本語': 'Example: /language output 日本語', - 'Example: /language output Português': 'Example: /language output Português', - 'UI language changed to {{lang}}': 'UI language changed to {{lang}}', - 'LLM output language set to {{lang}}': 'LLM output language set to {{lang}}', - 'LLM output language rule file generated at {{path}}': - 'LLM output language rule file generated at {{path}}', - 'Please restart the application for the changes to take effect.': - 'Please restart the application for the changes to take effect.', - 'Failed to generate LLM output language rule file: {{error}}': - 'Failed to generate LLM output language rule file: {{error}}', - 'Invalid command. Available subcommands:': - 'Invalid command. Available subcommands:', - 'Available subcommands:': 'Available subcommands:', - 'To request additional UI language packs, please open an issue on GitHub.': - 'To request additional UI language packs, please open an issue on GitHub.', - 'Available options:': 'Available options:', - 'Set UI language to {{name}}': 'Set UI language to {{name}}', + "Invalid language. Available: {{options}}": "Invalid language. Available: {{options}}", + "Language subcommands do not accept additional arguments.": + "Language subcommands do not accept additional arguments.", + "Current UI language: {{lang}}": "Current UI language: {{lang}}", + "Current LLM output language: {{lang}}": "Current LLM output language: {{lang}}", + "LLM output language not set": "LLM output language not set", + "Set UI language": "Set UI language", + "Set LLM output language": "Set LLM output language", + "Usage: /language ui [{{options}}]": "Usage: /language ui [{{options}}]", + "Usage: /language output ": "Usage: /language output ", + "Example: /language output 中文": "Example: /language output 中文", + "Example: /language output English": "Example: /language output English", + "Example: /language output 日本語": "Example: /language output 日本語", + "Example: /language output Português": "Example: /language output Português", + "UI language changed to {{lang}}": "UI language changed to {{lang}}", + "LLM output language set to {{lang}}": "LLM output language set to {{lang}}", + "LLM output language rule file generated at {{path}}": + "LLM output language rule file generated at {{path}}", + "Please restart the application for the changes to take effect.": + "Please restart the application for the changes to take effect.", + "Failed to generate LLM output language rule file: {{error}}": + "Failed to generate LLM output language rule file: {{error}}", + "Invalid command. Available subcommands:": "Invalid command. Available subcommands:", + "Available subcommands:": "Available subcommands:", + "To request additional UI language packs, please open an issue on GitHub.": + "To request additional UI language packs, please open an issue on GitHub.", + "Available options:": "Available options:", + "Set UI language to {{name}}": "Set UI language to {{name}}", // ============================================================================ // Commands - Approval Mode // ============================================================================ - 'Tool Approval Mode': 'Tool Approval Mode', - 'Current approval mode: {{mode}}': 'Current approval mode: {{mode}}', - 'Available approval modes:': 'Available approval modes:', - 'Approval mode changed to: {{mode}}': 'Approval mode changed to: {{mode}}', - 'Approval mode changed to: {{mode}} (saved to {{scope}} settings{{location}})': - 'Approval mode changed to: {{mode}} (saved to {{scope}} settings{{location}})', - 'Usage: /approval-mode [--session|--user|--project]': - 'Usage: /approval-mode [--session|--user|--project]', + "Tool Approval Mode": "Tool Approval Mode", + "Current approval mode: {{mode}}": "Current approval mode: {{mode}}", + "Available approval modes:": "Available approval modes:", + "Approval mode changed to: {{mode}}": "Approval mode changed to: {{mode}}", + "Approval mode changed to: {{mode}} (saved to {{scope}} settings{{location}})": + "Approval mode changed to: {{mode}} (saved to {{scope}} settings{{location}})", + "Usage: /approval-mode [--session|--user|--project]": + "Usage: /approval-mode [--session|--user|--project]", - 'Scope subcommands do not accept additional arguments.': - 'Scope subcommands do not accept additional arguments.', - 'Plan mode - Analyze only, do not modify files or execute commands': - 'Plan mode - Analyze only, do not modify files or execute commands', - 'Default mode - Require approval for file edits or shell commands': - 'Default mode - Require approval for file edits or shell commands', - 'Auto-edit mode - Automatically approve file edits': - 'Auto-edit mode - Automatically approve file edits', - 'YOLO mode - Automatically approve all tools': - 'YOLO mode - Automatically approve all tools', - '{{mode}} mode': '{{mode}} mode', - 'Settings service is not available; unable to persist the approval mode.': - 'Settings service is not available; unable to persist the approval mode.', - 'Failed to save approval mode: {{error}}': - 'Failed to save approval mode: {{error}}', - 'Failed to change approval mode: {{error}}': - 'Failed to change approval mode: {{error}}', - 'Apply to current session only (temporary)': - 'Apply to current session only (temporary)', - 'Persist for this project/workspace': 'Persist for this project/workspace', - 'Persist for this user on this machine': - 'Persist for this user on this machine', - 'Analyze only, do not modify files or execute commands': - 'Analyze only, do not modify files or execute commands', - 'Require approval for file edits or shell commands': - 'Require approval for file edits or shell commands', - 'Automatically approve file edits': 'Automatically approve file edits', - 'Automatically approve all tools': 'Automatically approve all tools', - 'Workspace approval mode exists and takes priority. User-level change will have no effect.': - 'Workspace approval mode exists and takes priority. User-level change will have no effect.', - 'Apply To': 'Apply To', - 'Workspace Settings': 'Workspace Settings', + "Scope subcommands do not accept additional arguments.": + "Scope subcommands do not accept additional arguments.", + "Plan mode - Analyze only, do not modify files or execute commands": + "Plan mode - Analyze only, do not modify files or execute commands", + "Default mode - Require approval for file edits or shell commands": + "Default mode - Require approval for file edits or shell commands", + "Auto-edit mode - Automatically approve file edits": + "Auto-edit mode - Automatically approve file edits", + "YOLO mode - Automatically approve all tools": "YOLO mode - Automatically approve all tools", + "{{mode}} mode": "{{mode}} mode", + "Settings service is not available; unable to persist the approval mode.": + "Settings service is not available; unable to persist the approval mode.", + "Failed to save approval mode: {{error}}": "Failed to save approval mode: {{error}}", + "Failed to change approval mode: {{error}}": "Failed to change approval mode: {{error}}", + "Apply to current session only (temporary)": "Apply to current session only (temporary)", + "Persist for this project/workspace": "Persist for this project/workspace", + "Persist for this user on this machine": "Persist for this user on this machine", + "Analyze only, do not modify files or execute commands": + "Analyze only, do not modify files or execute commands", + "Require approval for file edits or shell commands": + "Require approval for file edits or shell commands", + "Automatically approve file edits": "Automatically approve file edits", + "Automatically approve all tools": "Automatically approve all tools", + "Workspace approval mode exists and takes priority. User-level change will have no effect.": + "Workspace approval mode exists and takes priority. User-level change will have no effect.", + "Apply To": "Apply To", + "Workspace Settings": "Workspace Settings", // ============================================================================ // Commands - Memory // ============================================================================ - 'Commands for interacting with memory.': - 'Commands for interacting with memory.', - 'Show the current memory contents.': 'Show the current memory contents.', - 'Show project-level memory contents.': 'Show project-level memory contents.', - 'Show global memory contents.': 'Show global memory contents.', - 'Add content to project-level memory.': - 'Add content to project-level memory.', - 'Add content to global memory.': 'Add content to global memory.', - 'Refresh the memory from the source.': 'Refresh the memory from the source.', - 'Usage: /memory add --project ': - 'Usage: /memory add --project ', - 'Usage: /memory add --global ': - 'Usage: /memory add --global ', + "Commands for interacting with memory.": "Commands for interacting with memory.", + "Show the current memory contents.": "Show the current memory contents.", + "Show project-level memory contents.": "Show project-level memory contents.", + "Show global memory contents.": "Show global memory contents.", + "Add content to project-level memory.": "Add content to project-level memory.", + "Add content to global memory.": "Add content to global memory.", + "Refresh the memory from the source.": "Refresh the memory from the source.", + "Usage: /memory add --project ": + "Usage: /memory add --project ", + "Usage: /memory add --global ": + "Usage: /memory add --global ", 'Attempting to save to project memory: "{{text}}"': 'Attempting to save to project memory: "{{text}}"', 'Attempting to save to global memory: "{{text}}"': 'Attempting to save to global memory: "{{text}}"', - 'Current memory content from {{count}} file(s):': - 'Current memory content from {{count}} file(s):', - 'Memory is currently empty.': 'Memory is currently empty.', - 'Project memory file not found or is currently empty.': - 'Project memory file not found or is currently empty.', - 'Global memory file not found or is currently empty.': - 'Global memory file not found or is currently empty.', - 'Global memory is currently empty.': 'Global memory is currently empty.', - 'Global memory content:\n\n---\n{{content}}\n---': - 'Global memory content:\n\n---\n{{content}}\n---', - 'Project memory content from {{path}}:\n\n---\n{{content}}\n---': - 'Project memory content from {{path}}:\n\n---\n{{content}}\n---', - 'Project memory is currently empty.': 'Project memory is currently empty.', - 'Refreshing memory from source files...': - 'Refreshing memory from source files...', - 'Add content to the memory. Use --global for global memory or --project for project memory.': - 'Add content to the memory. Use --global for global memory or --project for project memory.', - 'Usage: /memory add [--global|--project] ': - 'Usage: /memory add [--global|--project] ', + "Current memory content from {{count}} file(s):": + "Current memory content from {{count}} file(s):", + "Memory is currently empty.": "Memory is currently empty.", + "Project memory file not found or is currently empty.": + "Project memory file not found or is currently empty.", + "Global memory file not found or is currently empty.": + "Global memory file not found or is currently empty.", + "Global memory is currently empty.": "Global memory is currently empty.", + "Global memory content:\n\n---\n{{content}}\n---": + "Global memory content:\n\n---\n{{content}}\n---", + "Project memory content from {{path}}:\n\n---\n{{content}}\n---": + "Project memory content from {{path}}:\n\n---\n{{content}}\n---", + "Project memory is currently empty.": "Project memory is currently empty.", + "Refreshing memory from source files...": "Refreshing memory from source files...", + "Add content to the memory. Use --global for global memory or --project for project memory.": + "Add content to the memory. Use --global for global memory or --project for project memory.", + "Usage: /memory add [--global|--project] ": + "Usage: /memory add [--global|--project] ", 'Attempting to save to memory {{scope}}: "{{fact}}"': 'Attempting to save to memory {{scope}}: "{{fact}}"', // ============================================================================ // Commands - MCP // ============================================================================ - 'Authenticate with an OAuth-enabled MCP server': - 'Authenticate with an OAuth-enabled MCP server', - 'List configured MCP servers and tools': - 'List configured MCP servers and tools', - 'Restarts MCP servers.': 'Restarts MCP servers.', - 'Open MCP management dialog': 'Open MCP management dialog', - 'Could not retrieve tool registry.': 'Could not retrieve tool registry.', - 'No MCP servers configured with OAuth authentication.': - 'No MCP servers configured with OAuth authentication.', - 'MCP servers with OAuth authentication:': - 'MCP servers with OAuth authentication:', - 'Use /mcp auth to authenticate.': - 'Use /mcp auth to authenticate.', + "Authenticate with an OAuth-enabled MCP server": "Authenticate with an OAuth-enabled MCP server", + "List configured MCP servers and tools": "List configured MCP servers and tools", + "Restarts MCP servers.": "Restarts MCP servers.", + "Open MCP management dialog": "Open MCP management dialog", + "Could not retrieve tool registry.": "Could not retrieve tool registry.", + "No MCP servers configured with OAuth authentication.": + "No MCP servers configured with OAuth authentication.", + "MCP servers with OAuth authentication:": "MCP servers with OAuth authentication:", + "Use /mcp auth to authenticate.": "Use /mcp auth to authenticate.", "MCP server '{{name}}' not found.": "MCP server '{{name}}' not found.", "Successfully authenticated and refreshed tools for '{{name}}'.": "Successfully authenticated and refreshed tools for '{{name}}'.", "Failed to authenticate with MCP server '{{name}}': {{error}}": "Failed to authenticate with MCP server '{{name}}': {{error}}", - "Re-discovering tools from '{{name}}'...": - "Re-discovering tools from '{{name}}'...", - "Discovered {{count}} tool(s) from '{{name}}'.": - "Discovered {{count}} tool(s) from '{{name}}'.", - 'Authentication complete. Returning to server details...': - 'Authentication complete. Returning to server details...', - 'Authentication successful.': 'Authentication successful.', - 'If the browser does not open, copy and paste this URL into your browser:': - 'If the browser does not open, copy and paste this URL into your browser:', - 'Make sure to copy the COMPLETE URL - it may wrap across multiple lines.': - 'Make sure to copy the COMPLETE URL - it may wrap across multiple lines.', + "Re-discovering tools from '{{name}}'...": "Re-discovering tools from '{{name}}'...", + "Discovered {{count}} tool(s) from '{{name}}'.": "Discovered {{count}} tool(s) from '{{name}}'.", + "Authentication complete. Returning to server details...": + "Authentication complete. Returning to server details...", + "Authentication successful.": "Authentication successful.", + "If the browser does not open, copy and paste this URL into your browser:": + "If the browser does not open, copy and paste this URL into your browser:", + "Make sure to copy the COMPLETE URL - it may wrap across multiple lines.": + "Make sure to copy the COMPLETE URL - it may wrap across multiple lines.", // ============================================================================ // MCP Management Dialog // ============================================================================ - 'Manage MCP servers': 'Manage MCP servers', - 'Server Detail': 'Server Detail', - 'Disable Server': 'Disable Server', - Tools: 'Tools', - 'Tool Detail': 'Tool Detail', - 'MCP Management': 'MCP Management', - 'Loading...': 'Loading...', - 'Unknown step': 'Unknown step', - 'Esc to back': 'Esc to back', - '↑↓ to navigate · Enter to select · Esc to close': - '↑↓ to navigate · Enter to select · Esc to close', - '↑↓ to navigate · Enter to select · Esc to back': - '↑↓ to navigate · Enter to select · Esc to back', - '↑↓ to navigate · Enter to confirm · Esc to back': - '↑↓ to navigate · Enter to confirm · Esc to back', - 'User Settings (global)': 'User Settings (global)', - 'Workspace Settings (project-specific)': - 'Workspace Settings (project-specific)', - 'Disable server:': 'Disable server:', - 'Select where to add the server to the exclude list:': - 'Select where to add the server to the exclude list:', - 'Press Enter to confirm, Esc to cancel': - 'Press Enter to confirm, Esc to cancel', - 'View tools': 'View tools', - Reconnect: 'Reconnect', - Enable: 'Enable', - Disable: 'Disable', - Authenticate: 'Authenticate', - 'Re-authenticate': 'Re-authenticate', - 'Clear Authentication': 'Clear Authentication', - 'Server:': 'Server:', - 'Command:': 'Command:', - 'Working Directory:': 'Working Directory:', - 'Capabilities:': 'Capabilities:', - 'No server selected': 'No server selected', - prompts: 'prompts', - '(disabled)': '(disabled)', - 'Error:': 'Error:', - tool: 'tool', - tools: 'tools', - connected: 'connected', - connecting: 'connecting', - disconnected: 'disconnected', + "Manage MCP servers": "Manage MCP servers", + "Server Detail": "Server Detail", + "Disable Server": "Disable Server", + Tools: "Tools", + "Tool Detail": "Tool Detail", + "MCP Management": "MCP Management", + "Loading...": "Loading...", + "Unknown step": "Unknown step", + "Esc to back": "Esc to back", + "↑↓ to navigate · Enter to select · Esc to close": + "↑↓ to navigate · Enter to select · Esc to close", + "↑↓ to navigate · Enter to select · Esc to back": + "↑↓ to navigate · Enter to select · Esc to back", + "↑↓ to navigate · Enter to confirm · Esc to back": + "↑↓ to navigate · Enter to confirm · Esc to back", + "User Settings (global)": "User Settings (global)", + "Workspace Settings (project-specific)": "Workspace Settings (project-specific)", + "Disable server:": "Disable server:", + "Select where to add the server to the exclude list:": + "Select where to add the server to the exclude list:", + "Press Enter to confirm, Esc to cancel": "Press Enter to confirm, Esc to cancel", + "View tools": "View tools", + Reconnect: "Reconnect", + Enable: "Enable", + Disable: "Disable", + Authenticate: "Authenticate", + "Re-authenticate": "Re-authenticate", + "Clear Authentication": "Clear Authentication", + "Server:": "Server:", + "Command:": "Command:", + "Working Directory:": "Working Directory:", + "Capabilities:": "Capabilities:", + "No server selected": "No server selected", + prompts: "prompts", + "(disabled)": "(disabled)", + "Error:": "Error:", + tool: "tool", + tools: "tools", + connected: "connected", + connecting: "connecting", + disconnected: "disconnected", // MCP Server List - 'User MCPs': 'User MCPs', - 'Project MCPs': 'Project MCPs', - 'Extension MCPs': 'Extension MCPs', - server: 'server', - servers: 'servers', - 'Add MCP servers to your settings to get started.': - 'Add MCP servers to your settings to get started.', - 'Run qwen --debug to see error logs': 'Run qwen --debug to see error logs', + "User MCPs": "User MCPs", + "Project MCPs": "Project MCPs", + "Extension MCPs": "Extension MCPs", + server: "server", + servers: "servers", + "Add MCP servers to your settings to get started.": + "Add MCP servers to your settings to get started.", + "Run qwen --debug to see error logs": "Run qwen --debug to see error logs", // MCP OAuth Authentication - 'OAuth Authentication': 'OAuth Authentication', - 'Press Enter to start authentication, Esc to go back': - 'Press Enter to start authentication, Esc to go back', - 'Authenticating... Please complete the login in your browser.': - 'Authenticating... Please complete the login in your browser.', - 'Press Enter or Esc to go back': 'Press Enter or Esc to go back', + "OAuth Authentication": "OAuth Authentication", + "Press Enter to start authentication, Esc to go back": + "Press Enter to start authentication, Esc to go back", + "Authenticating... Please complete the login in your browser.": + "Authenticating... Please complete the login in your browser.", + "Press Enter or Esc to go back": "Press Enter or Esc to go back", // MCP Tool List - 'No tools available for this server.': 'No tools available for this server.', - destructive: 'destructive', - 'read-only': 'read-only', - 'open-world': 'open-world', - idempotent: 'idempotent', - 'Tools for {{name}}': 'Tools for {{name}}', - 'Tools for {{serverName}}': 'Tools for {{serverName}}', - '{{current}}/{{total}}': '{{current}}/{{total}}', + "No tools available for this server.": "No tools available for this server.", + destructive: "destructive", + "read-only": "read-only", + "open-world": "open-world", + idempotent: "idempotent", + "Tools for {{name}}": "Tools for {{name}}", + "Tools for {{serverName}}": "Tools for {{serverName}}", + "{{current}}/{{total}}": "{{current}}/{{total}}", // MCP Tool Detail - required: 'required', - Type: 'Type', - Enum: 'Enum', - Parameters: 'Parameters', - 'No tool selected': 'No tool selected', - Annotations: 'Annotations', - Title: 'Title', - 'Read Only': 'Read Only', - Destructive: 'Destructive', - Idempotent: 'Idempotent', - 'Open World': 'Open World', - Server: 'Server', + required: "required", + Type: "Type", + Enum: "Enum", + Parameters: "Parameters", + "No tool selected": "No tool selected", + Annotations: "Annotations", + Title: "Title", + "Read Only": "Read Only", + Destructive: "Destructive", + Idempotent: "Idempotent", + "Open World": "Open World", + Server: "Server", // Invalid tool related translations - '{{count}} invalid tools': '{{count}} invalid tools', - invalid: 'invalid', - 'invalid: {{reason}}': 'invalid: {{reason}}', - 'missing name': 'missing name', - 'missing description': 'missing description', - '(unnamed)': '(unnamed)', - 'Warning: This tool cannot be called by the LLM': - 'Warning: This tool cannot be called by the LLM', - Reason: 'Reason', - 'Tools must have both name and description to be used by the LLM.': - 'Tools must have both name and description to be used by the LLM.', + "{{count}} invalid tools": "{{count}} invalid tools", + invalid: "invalid", + "invalid: {{reason}}": "invalid: {{reason}}", + "missing name": "missing name", + "missing description": "missing description", + "(unnamed)": "(unnamed)", + "Warning: This tool cannot be called by the LLM": + "Warning: This tool cannot be called by the LLM", + Reason: "Reason", + "Tools must have both name and description to be used by the LLM.": + "Tools must have both name and description to be used by the LLM.", // ============================================================================ // Commands - Chat // ============================================================================ - 'Manage conversation history.': 'Manage conversation history.', - 'List saved conversation checkpoints': 'List saved conversation checkpoints', - 'No saved conversation checkpoints found.': - 'No saved conversation checkpoints found.', - 'List of saved conversations:': 'List of saved conversations:', - 'Note: Newest last, oldest first': 'Note: Newest last, oldest first', - 'Save the current conversation as a checkpoint. Usage: /chat save ': - 'Save the current conversation as a checkpoint. Usage: /chat save ', - 'Missing tag. Usage: /chat save ': - 'Missing tag. Usage: /chat save ', - 'Delete a conversation checkpoint. Usage: /chat delete ': - 'Delete a conversation checkpoint. Usage: /chat delete ', - 'Missing tag. Usage: /chat delete ': - 'Missing tag. Usage: /chat delete ', + "Manage conversation history.": "Manage conversation history.", + "List saved conversation checkpoints": "List saved conversation checkpoints", + "No saved conversation checkpoints found.": "No saved conversation checkpoints found.", + "List of saved conversations:": "List of saved conversations:", + "Note: Newest last, oldest first": "Note: Newest last, oldest first", + "Save the current conversation as a checkpoint. Usage: /chat save ": + "Save the current conversation as a checkpoint. Usage: /chat save ", + "Missing tag. Usage: /chat save ": "Missing tag. Usage: /chat save ", + "Delete a conversation checkpoint. Usage: /chat delete ": + "Delete a conversation checkpoint. Usage: /chat delete ", + "Missing tag. Usage: /chat delete ": "Missing tag. Usage: /chat delete ", "Conversation checkpoint '{{tag}}' has been deleted.": "Conversation checkpoint '{{tag}}' has been deleted.", "Error: No checkpoint found with tag '{{tag}}'.": "Error: No checkpoint found with tag '{{tag}}'.", - 'Resume a conversation from a checkpoint. Usage: /chat resume ': - 'Resume a conversation from a checkpoint. Usage: /chat resume ', - 'Missing tag. Usage: /chat resume ': - 'Missing tag. Usage: /chat resume ', - 'No saved checkpoint found with tag: {{tag}}.': - 'No saved checkpoint found with tag: {{tag}}.', - 'A checkpoint with the tag {{tag}} already exists. Do you want to overwrite it?': - 'A checkpoint with the tag {{tag}} already exists. Do you want to overwrite it?', - 'No chat client available to save conversation.': - 'No chat client available to save conversation.', - 'Conversation checkpoint saved with tag: {{tag}}.': - 'Conversation checkpoint saved with tag: {{tag}}.', - 'No conversation found to save.': 'No conversation found to save.', - 'No chat client available to share conversation.': - 'No chat client available to share conversation.', - 'Invalid file format. Only .md and .json are supported.': - 'Invalid file format. Only .md and .json are supported.', - 'Error sharing conversation: {{error}}': - 'Error sharing conversation: {{error}}', - 'Conversation shared to {{filePath}}': 'Conversation shared to {{filePath}}', - 'No conversation found to share.': 'No conversation found to share.', - 'Share the current conversation to a markdown or json file. Usage: /chat share ': - 'Share the current conversation to a markdown or json file. Usage: /chat share ', + "Resume a conversation from a checkpoint. Usage: /chat resume ": + "Resume a conversation from a checkpoint. Usage: /chat resume ", + "Missing tag. Usage: /chat resume ": "Missing tag. Usage: /chat resume ", + "No saved checkpoint found with tag: {{tag}}.": "No saved checkpoint found with tag: {{tag}}.", + "A checkpoint with the tag {{tag}} already exists. Do you want to overwrite it?": + "A checkpoint with the tag {{tag}} already exists. Do you want to overwrite it?", + "No chat client available to save conversation.": + "No chat client available to save conversation.", + "Conversation checkpoint saved with tag: {{tag}}.": + "Conversation checkpoint saved with tag: {{tag}}.", + "No conversation found to save.": "No conversation found to save.", + "No chat client available to share conversation.": + "No chat client available to share conversation.", + "Invalid file format. Only .md and .json are supported.": + "Invalid file format. Only .md and .json are supported.", + "Error sharing conversation: {{error}}": "Error sharing conversation: {{error}}", + "Conversation shared to {{filePath}}": "Conversation shared to {{filePath}}", + "No conversation found to share.": "No conversation found to share.", + "Share the current conversation to a markdown or json file. Usage: /chat share ": + "Share the current conversation to a markdown or json file. Usage: /chat share ", // ============================================================================ // Commands - Summary // ============================================================================ - 'Generate a project summary and save it to .qwen/PROJECT_SUMMARY.md': - 'Generate a project summary and save it to .qwen/PROJECT_SUMMARY.md', - 'No chat client available to generate summary.': - 'No chat client available to generate summary.', - 'Already generating summary, wait for previous request to complete': - 'Already generating summary, wait for previous request to complete', - 'No conversation found to summarize.': 'No conversation found to summarize.', - 'Failed to generate project context summary: {{error}}': - 'Failed to generate project context summary: {{error}}', - 'Saved project summary to {{filePathForDisplay}}.': - 'Saved project summary to {{filePathForDisplay}}.', - 'Saving project summary...': 'Saving project summary...', - 'Generating project summary...': 'Generating project summary...', - 'Failed to generate summary - no text content received from LLM response': - 'Failed to generate summary - no text content received from LLM response', + "Generate a project summary and save it to .qwen/PROJECT_SUMMARY.md": + "Generate a project summary and save it to .qwen/PROJECT_SUMMARY.md", + "No chat client available to generate summary.": "No chat client available to generate summary.", + "Already generating summary, wait for previous request to complete": + "Already generating summary, wait for previous request to complete", + "No conversation found to summarize.": "No conversation found to summarize.", + "Failed to generate project context summary: {{error}}": + "Failed to generate project context summary: {{error}}", + "Saved project summary to {{filePathForDisplay}}.": + "Saved project summary to {{filePathForDisplay}}.", + "Saving project summary...": "Saving project summary...", + "Generating project summary...": "Generating project summary...", + "Failed to generate summary - no text content received from LLM response": + "Failed to generate summary - no text content received from LLM response", // ============================================================================ // Commands - Model // ============================================================================ - 'Switch the model for this session': 'Switch the model for this session', - 'Set fast model for background tasks': 'Set fast model for background tasks', - 'Content generator configuration not available.': - 'Content generator configuration not available.', - 'Authentication type not available.': 'Authentication type not available.', - 'No models available for the current authentication type ({{authType}}).': - 'No models available for the current authentication type ({{authType}}).', + "Switch the model for this session": "Switch the model for this session", + "Set fast model for background tasks": "Set fast model for background tasks", + "Content generator configuration not available.": + "Content generator configuration not available.", + "Authentication type not available.": "Authentication type not available.", + "No models available for the current authentication type ({{authType}}).": + "No models available for the current authentication type ({{authType}}).", // ============================================================================ // Commands - Clear // ============================================================================ - 'Starting a new session, resetting chat, and clearing terminal.': - 'Starting a new session, resetting chat, and clearing terminal.', - 'Starting a new session and clearing.': - 'Starting a new session and clearing.', + "Starting a new session, resetting chat, and clearing terminal.": + "Starting a new session, resetting chat, and clearing terminal.", + "Starting a new session and clearing.": "Starting a new session and clearing.", // ============================================================================ // Commands - Compress // ============================================================================ - 'Already compressing, wait for previous request to complete': - 'Already compressing, wait for previous request to complete', - 'Failed to compress chat history.': 'Failed to compress chat history.', - 'Failed to compress chat history: {{error}}': - 'Failed to compress chat history: {{error}}', - 'Compressing chat history': 'Compressing chat history', - 'Chat history compressed from {{originalTokens}} to {{newTokens}} tokens.': - 'Chat history compressed from {{originalTokens}} to {{newTokens}} tokens.', - 'Compression was not beneficial for this history size.': - 'Compression was not beneficial for this history size.', - 'Chat history compression did not reduce size. This may indicate issues with the compression prompt.': - 'Chat history compression did not reduce size. This may indicate issues with the compression prompt.', - 'Could not compress chat history due to a token counting error.': - 'Could not compress chat history due to a token counting error.', - 'Chat history is already compressed.': 'Chat history is already compressed.', + "Already compressing, wait for previous request to complete": + "Already compressing, wait for previous request to complete", + "Failed to compress chat history.": "Failed to compress chat history.", + "Failed to compress chat history: {{error}}": "Failed to compress chat history: {{error}}", + "Compressing chat history": "Compressing chat history", + "Chat history compressed from {{originalTokens}} to {{newTokens}} tokens.": + "Chat history compressed from {{originalTokens}} to {{newTokens}} tokens.", + "Compression was not beneficial for this history size.": + "Compression was not beneficial for this history size.", + "Chat history compression did not reduce size. This may indicate issues with the compression prompt.": + "Chat history compression did not reduce size. This may indicate issues with the compression prompt.", + "Could not compress chat history due to a token counting error.": + "Could not compress chat history due to a token counting error.", + "Chat history is already compressed.": "Chat history is already compressed.", // ============================================================================ // Commands - Directory // ============================================================================ - 'Configuration is not available.': 'Configuration is not available.', - 'Please provide at least one path to add.': - 'Please provide at least one path to add.', - 'The /directory add command is not supported in restrictive sandbox profiles. Please use --include-directories when starting the session instead.': - 'The /directory add command is not supported in restrictive sandbox profiles. Please use --include-directories when starting the session instead.', + "Configuration is not available.": "Configuration is not available.", + "Please provide at least one path to add.": "Please provide at least one path to add.", + "The /directory add command is not supported in restrictive sandbox profiles. Please use --include-directories when starting the session instead.": + "The /directory add command is not supported in restrictive sandbox profiles. Please use --include-directories when starting the session instead.", "Error adding '{{path}}': {{error}}": "Error adding '{{path}}': {{error}}", - 'Successfully added QWEN.md files from the following directories if there are:\n- {{directories}}': - 'Successfully added QWEN.md files from the following directories if there are:\n- {{directories}}', - 'Error refreshing memory: {{error}}': 'Error refreshing memory: {{error}}', - 'Successfully added directories:\n- {{directories}}': - 'Successfully added directories:\n- {{directories}}', - 'Current workspace directories:\n{{directories}}': - 'Current workspace directories:\n{{directories}}', + "Successfully added QWEN.md files from the following directories if there are:\n- {{directories}}": + "Successfully added QWEN.md files from the following directories if there are:\n- {{directories}}", + "Error refreshing memory: {{error}}": "Error refreshing memory: {{error}}", + "Successfully added directories:\n- {{directories}}": + "Successfully added directories:\n- {{directories}}", + "Current workspace directories:\n{{directories}}": + "Current workspace directories:\n{{directories}}", // ============================================================================ // Commands - Docs // ============================================================================ - 'Please open the following URL in your browser to view the documentation:\n{{url}}': - 'Please open the following URL in your browser to view the documentation:\n{{url}}', - 'Opening documentation in your browser: {{url}}': - 'Opening documentation in your browser: {{url}}', + "Please open the following URL in your browser to view the documentation:\n{{url}}": + "Please open the following URL in your browser to view the documentation:\n{{url}}", + "Opening documentation in your browser: {{url}}": + "Opening documentation in your browser: {{url}}", // ============================================================================ // Dialogs - Tool Confirmation // ============================================================================ - 'Do you want to proceed?': 'Do you want to proceed?', - 'Yes, allow once': 'Yes, allow once', - 'Allow always': 'Allow always', - Yes: 'Yes', - No: 'No', - 'No (esc)': 'No (esc)', - 'Yes, allow always for this session': 'Yes, allow always for this session', - 'Modify in progress:': 'Modify in progress:', - 'Save and close external editor to continue': - 'Save and close external editor to continue', - 'Apply this change?': 'Apply this change?', - 'Yes, allow always': 'Yes, allow always', - 'Modify with external editor': 'Modify with external editor', - 'No, suggest changes (esc)': 'No, suggest changes (esc)', + "Do you want to proceed?": "Do you want to proceed?", + "Yes, allow once": "Yes, allow once", + "Allow always": "Allow always", + Yes: "Yes", + No: "No", + "No (esc)": "No (esc)", + "Yes, allow always for this session": "Yes, allow always for this session", + "Modify in progress:": "Modify in progress:", + "Save and close external editor to continue": "Save and close external editor to continue", + "Apply this change?": "Apply this change?", + "Yes, allow always": "Yes, allow always", + "Modify with external editor": "Modify with external editor", + "No, suggest changes (esc)": "No, suggest changes (esc)", "Allow execution of: '{{command}}'?": "Allow execution of: '{{command}}'?", - 'Yes, allow always ...': 'Yes, allow always ...', - 'Always allow in this project': 'Always allow in this project', - 'Always allow {{action}} in this project': - 'Always allow {{action}} in this project', - 'Always allow for this user': 'Always allow for this user', - 'Always allow {{action}} for this user': - 'Always allow {{action}} for this user', - 'Yes, restore previous mode ({{mode}})': - 'Yes, restore previous mode ({{mode}})', - 'Yes, and auto-accept edits': 'Yes, and auto-accept edits', - 'Yes, and manually approve edits': 'Yes, and manually approve edits', - 'No, keep planning (esc)': 'No, keep planning (esc)', - 'URLs to fetch:': 'URLs to fetch:', - 'MCP Server: {{server}}': 'MCP Server: {{server}}', - 'Tool: {{tool}}': 'Tool: {{tool}}', + "Yes, allow always ...": "Yes, allow always ...", + "Always allow in this project": "Always allow in this project", + "Always allow {{action}} in this project": "Always allow {{action}} in this project", + "Always allow for this user": "Always allow for this user", + "Always allow {{action}} for this user": "Always allow {{action}} for this user", + "Yes, restore previous mode ({{mode}})": "Yes, restore previous mode ({{mode}})", + "Yes, and auto-accept edits": "Yes, and auto-accept edits", + "Yes, and manually approve edits": "Yes, and manually approve edits", + "No, keep planning (esc)": "No, keep planning (esc)", + "URLs to fetch:": "URLs to fetch:", + "MCP Server: {{server}}": "MCP Server: {{server}}", + "Tool: {{tool}}": "Tool: {{tool}}", 'Allow execution of MCP tool "{{tool}}" from server "{{server}}"?': 'Allow execution of MCP tool "{{tool}}" from server "{{server}}"?', 'Yes, always allow tool "{{tool}}" from server "{{server}}"': @@ -1254,742 +1170,699 @@ export default { // ============================================================================ // Dialogs - Shell Confirmation // ============================================================================ - 'Shell Command Execution': 'Shell Command Execution', - 'A custom command wants to run the following shell commands:': - 'A custom command wants to run the following shell commands:', + "Shell Command Execution": "Shell Command Execution", + "A custom command wants to run the following shell commands:": + "A custom command wants to run the following shell commands:", // ============================================================================ // Dialogs - Pro Quota // ============================================================================ - 'Pro quota limit reached for {{model}}.': - 'Pro quota limit reached for {{model}}.', - 'Change auth (executes the /auth command)': - 'Change auth (executes the /auth command)', - 'Continue with {{model}}': 'Continue with {{model}}', + "Pro quota limit reached for {{model}}.": "Pro quota limit reached for {{model}}.", + "Change auth (executes the /auth command)": "Change auth (executes the /auth command)", + "Continue with {{model}}": "Continue with {{model}}", // ============================================================================ // Dialogs - Welcome Back // ============================================================================ - 'Current Plan:': 'Current Plan:', - 'Progress: {{done}}/{{total}} tasks completed': - 'Progress: {{done}}/{{total}} tasks completed', - ', {{inProgress}} in progress': ', {{inProgress}} in progress', - 'Pending Tasks:': 'Pending Tasks:', - 'What would you like to do?': 'What would you like to do?', - 'Choose how to proceed with your session:': - 'Choose how to proceed with your session:', - 'Start new chat session': 'Start new chat session', - 'Continue previous conversation': 'Continue previous conversation', - '👋 Welcome back! (Last updated: {{timeAgo}})': - '👋 Welcome back! (Last updated: {{timeAgo}})', - '🎯 Overall Goal:': '🎯 Overall Goal:', + "Current Plan:": "Current Plan:", + "Progress: {{done}}/{{total}} tasks completed": "Progress: {{done}}/{{total}} tasks completed", + ", {{inProgress}} in progress": ", {{inProgress}} in progress", + "Pending Tasks:": "Pending Tasks:", + "What would you like to do?": "What would you like to do?", + "Choose how to proceed with your session:": "Choose how to proceed with your session:", + "Start new chat session": "Start new chat session", + "Continue previous conversation": "Continue previous conversation", + "👋 Welcome back! (Last updated: {{timeAgo}})": "👋 Welcome back! (Last updated: {{timeAgo}})", + "🎯 Overall Goal:": "🎯 Overall Goal:", // ============================================================================ // Dialogs - Auth // ============================================================================ - 'Get started': 'Get started', - 'Select Authentication Method': 'Select Authentication Method', - 'OpenAI API key is required to use OpenAI authentication.': - 'OpenAI API key is required to use OpenAI authentication.', - 'You must select an auth method to proceed. Press Ctrl+C again to exit.': - 'You must select an auth method to proceed. Press Ctrl+C again to exit.', - 'Terms of Services and Privacy Notice': - 'Terms of Services and Privacy Notice', - 'Paid \u00B7 Up to 6,000 requests/5 hrs \u00B7 All Alibaba Cloud Coding Plan Models': - 'Paid \u00B7 Up to 6,000 requests/5 hrs \u00B7 All Alibaba Cloud Coding Plan Models', - 'Alibaba Cloud Coding Plan': 'Alibaba Cloud Coding Plan', - 'Bring your own API key': 'Bring your own API key', - 'API-KEY': 'API-KEY', - 'Use coding plan credentials or your own api-keys/providers.': - 'Use coding plan credentials or your own api-keys/providers.', - OpenAI: 'OpenAI', - 'Failed to login. Message: {{message}}': - 'Failed to login. Message: {{message}}', - 'Authentication is enforced to be {{enforcedType}}, but you are currently using {{currentType}}.': - 'Authentication is enforced to be {{enforcedType}}, but you are currently using {{currentType}}.', - 'Please visit this URL to authorize:': 'Please visit this URL to authorize:', - 'Or scan the QR code below:': 'Or scan the QR code below:', - 'Waiting for authorization': 'Waiting for authorization', - 'Time remaining:': 'Time remaining:', - '(Press ESC or CTRL+C to cancel)': '(Press ESC or CTRL+C to cancel)', - 'OAuth token expired (over {{seconds}} seconds). Please select authentication method again.': - 'OAuth token expired (over {{seconds}} seconds). Please select authentication method again.', - 'Press any key to return to authentication type selection.': - 'Press any key to return to authentication type selection.', - 'Authentication timed out. Please try again.': - 'Authentication timed out. Please try again.', - 'Waiting for auth... (Press ESC or CTRL+C to cancel)': - 'Waiting for auth... (Press ESC or CTRL+C to cancel)', - 'Missing API key for OpenAI-compatible auth. Set settings.security.auth.apiKey, or set the {{envKeyHint}} environment variable.': - 'Missing API key for OpenAI-compatible auth. Set settings.security.auth.apiKey, or set the {{envKeyHint}} environment variable.', - '{{envKeyHint}} environment variable not found.': - '{{envKeyHint}} environment variable not found.', - '{{envKeyHint}} environment variable not found. Please set it in your .env file or environment variables.': - '{{envKeyHint}} environment variable not found. Please set it in your .env file or environment variables.', - '{{envKeyHint}} environment variable not found (or set settings.security.auth.apiKey). Please set it in your .env file or environment variables.': - '{{envKeyHint}} environment variable not found (or set settings.security.auth.apiKey). Please set it in your .env file or environment variables.', - 'Missing API key for OpenAI-compatible auth. Set the {{envKeyHint}} environment variable.': - 'Missing API key for OpenAI-compatible auth. Set the {{envKeyHint}} environment variable.', - 'Anthropic provider missing required baseUrl in modelProviders[].baseUrl.': - 'Anthropic provider missing required baseUrl in modelProviders[].baseUrl.', - 'ANTHROPIC_BASE_URL environment variable not found.': - 'ANTHROPIC_BASE_URL environment variable not found.', - 'Invalid auth method selected.': 'Invalid auth method selected.', - 'Failed to authenticate. Message: {{message}}': - 'Failed to authenticate. Message: {{message}}', - 'Authenticated successfully with {{authType}} credentials.': - 'Authenticated successfully with {{authType}} credentials.', + "Get started": "Get started", + "Select Authentication Method": "Select Authentication Method", + "OpenAI API key is required to use OpenAI authentication.": + "OpenAI API key is required to use OpenAI authentication.", + "You must select an auth method to proceed. Press Ctrl+C again to exit.": + "You must select an auth method to proceed. Press Ctrl+C again to exit.", + "Terms of Services and Privacy Notice": "Terms of Services and Privacy Notice", + "Paid \u00B7 Up to 6,000 requests/5 hrs \u00B7 All Alibaba Cloud Coding Plan Models": + "Paid \u00B7 Up to 6,000 requests/5 hrs \u00B7 All Alibaba Cloud Coding Plan Models", + "Alibaba Cloud Coding Plan": "Alibaba Cloud Coding Plan", + "Bring your own API key": "Bring your own API key", + "API-KEY": "API-KEY", + "Use coding plan credentials or your own api-keys/providers.": + "Use coding plan credentials or your own api-keys/providers.", + OpenAI: "OpenAI", + "Failed to login. Message: {{message}}": "Failed to login. Message: {{message}}", + "Authentication is enforced to be {{enforcedType}}, but you are currently using {{currentType}}.": + "Authentication is enforced to be {{enforcedType}}, but you are currently using {{currentType}}.", + "Please visit this URL to authorize:": "Please visit this URL to authorize:", + "Or scan the QR code below:": "Or scan the QR code below:", + "Waiting for authorization": "Waiting for authorization", + "Time remaining:": "Time remaining:", + "(Press ESC or CTRL+C to cancel)": "(Press ESC or CTRL+C to cancel)", + "OAuth token expired (over {{seconds}} seconds). Please select authentication method again.": + "OAuth token expired (over {{seconds}} seconds). Please select authentication method again.", + "Press any key to return to authentication type selection.": + "Press any key to return to authentication type selection.", + "Authentication timed out. Please try again.": "Authentication timed out. Please try again.", + "Waiting for auth... (Press ESC or CTRL+C to cancel)": + "Waiting for auth... (Press ESC or CTRL+C to cancel)", + "Missing API key for OpenAI-compatible auth. Set settings.security.auth.apiKey, or set the {{envKeyHint}} environment variable.": + "Missing API key for OpenAI-compatible auth. Set settings.security.auth.apiKey, or set the {{envKeyHint}} environment variable.", + "{{envKeyHint}} environment variable not found.": + "{{envKeyHint}} environment variable not found.", + "{{envKeyHint}} environment variable not found. Please set it in your .env file or environment variables.": + "{{envKeyHint}} environment variable not found. Please set it in your .env file or environment variables.", + "{{envKeyHint}} environment variable not found (or set settings.security.auth.apiKey). Please set it in your .env file or environment variables.": + "{{envKeyHint}} environment variable not found (or set settings.security.auth.apiKey). Please set it in your .env file or environment variables.", + "Missing API key for OpenAI-compatible auth. Set the {{envKeyHint}} environment variable.": + "Missing API key for OpenAI-compatible auth. Set the {{envKeyHint}} environment variable.", + "Anthropic provider missing required baseUrl in modelProviders[].baseUrl.": + "Anthropic provider missing required baseUrl in modelProviders[].baseUrl.", + "ANTHROPIC_BASE_URL environment variable not found.": + "ANTHROPIC_BASE_URL environment variable not found.", + "Invalid auth method selected.": "Invalid auth method selected.", + "Failed to authenticate. Message: {{message}}": "Failed to authenticate. Message: {{message}}", + "Authenticated successfully with {{authType}} credentials.": + "Authenticated successfully with {{authType}} credentials.", 'Invalid QWEN_DEFAULT_AUTH_TYPE value: "{{value}}". Valid values are: {{validValues}}': 'Invalid QWEN_DEFAULT_AUTH_TYPE value: "{{value}}". Valid values are: {{validValues}}', - 'OpenAI Configuration Required': 'OpenAI Configuration Required', - 'Please enter your OpenAI configuration. You can get an API key from': - 'Please enter your OpenAI configuration. You can get an API key from', - 'API Key:': 'API Key:', - 'Invalid credentials: {{errorMessage}}': - 'Invalid credentials: {{errorMessage}}', - 'Failed to validate credentials': 'Failed to validate credentials', - 'Press Enter to continue, Tab/↑↓ to navigate, Esc to cancel': - 'Press Enter to continue, Tab/↑↓ to navigate, Esc to cancel', + "OpenAI Configuration Required": "OpenAI Configuration Required", + "Please enter your OpenAI configuration. You can get an API key from": + "Please enter your OpenAI configuration. You can get an API key from", + "API Key:": "API Key:", + "Invalid credentials: {{errorMessage}}": "Invalid credentials: {{errorMessage}}", + "Failed to validate credentials": "Failed to validate credentials", + "Press Enter to continue, Tab/↑↓ to navigate, Esc to cancel": + "Press Enter to continue, Tab/↑↓ to navigate, Esc to cancel", // ============================================================================ // Dialogs - Model // ============================================================================ - 'Select Model': 'Select Model', - '(Press Esc to close)': '(Press Esc to close)', - 'Current (effective) configuration': 'Current (effective) configuration', - AuthType: 'AuthType', - 'API Key': 'API Key', - unset: 'unset', - '(default)': '(default)', - '(set)': '(set)', - '(not set)': '(not set)', - Modality: 'Modality', - 'Context Window': 'Context Window', - text: 'text', - 'text-only': 'text-only', - image: 'image', - pdf: 'pdf', - audio: 'audio', - video: 'video', - 'not set': 'not set', - none: 'none', - unknown: 'unknown', + "Select Model": "Select Model", + "(Press Esc to close)": "(Press Esc to close)", + "Current (effective) configuration": "Current (effective) configuration", + AuthType: "AuthType", + "API Key": "API Key", + unset: "unset", + "(default)": "(default)", + "(set)": "(set)", + "(not set)": "(not set)", + Modality: "Modality", + "Context Window": "Context Window", + text: "text", + "text-only": "text-only", + image: "image", + pdf: "pdf", + audio: "audio", + video: "video", + "not set": "not set", + none: "none", + unknown: "unknown", "Failed to switch model to '{{modelId}}'.\n\n{{error}}": "Failed to switch model to '{{modelId}}'.\n\n{{error}}", - 'Qwen 3.6 Plus — efficient hybrid model with leading coding performance': - 'Qwen 3.6 Plus — efficient hybrid model with leading coding performance', - 'The latest Qwen Vision model from Alibaba Cloud ModelStudio (version: qwen3-vl-plus-2025-09-23)': - 'The latest Qwen Vision model from Alibaba Cloud ModelStudio (version: qwen3-vl-plus-2025-09-23)', + "Qwen 3.6 Plus — efficient hybrid model with leading coding performance": + "Qwen 3.6 Plus — efficient hybrid model with leading coding performance", + "The latest Qwen Vision model from Alibaba Cloud ModelStudio (version: qwen3-vl-plus-2025-09-23)": + "The latest Qwen Vision model from Alibaba Cloud ModelStudio (version: qwen3-vl-plus-2025-09-23)", // ============================================================================ // Dialogs - Permissions // ============================================================================ - 'Manage folder trust settings': 'Manage folder trust settings', - 'Manage permission rules': 'Manage permission rules', - Allow: 'Allow', - Ask: 'Ask', - Deny: 'Deny', - Workspace: 'Workspace', + "Manage folder trust settings": "Manage folder trust settings", + "Manage permission rules": "Manage permission rules", + Allow: "Allow", + Ask: "Ask", + Deny: "Deny", + Workspace: "Workspace", "Qwen Code won't ask before using allowed tools.": "Qwen Code won't ask before using allowed tools.", - 'Qwen Code will ask before using these tools.': - 'Qwen Code will ask before using these tools.', - 'Qwen Code is not allowed to use denied tools.': - 'Qwen Code is not allowed to use denied tools.', - 'Manage trusted directories for this workspace.': - 'Manage trusted directories for this workspace.', - 'Any use of the {{tool}} tool': 'Any use of the {{tool}} tool', - "{{tool}} commands matching '{{pattern}}'": - "{{tool}} commands matching '{{pattern}}'", - 'From user settings': 'From user settings', - 'From project settings': 'From project settings', - 'From session': 'From session', - 'Project settings (local)': 'Project settings (local)', - 'Saved in .qwen/settings.local.json': 'Saved in .qwen/settings.local.json', - 'Project settings': 'Project settings', - 'Checked in at .qwen/settings.json': 'Checked in at .qwen/settings.json', - 'User settings': 'User settings', - 'Saved in at ~/.qwen/settings.json': 'Saved in at ~/.qwen/settings.json', - 'Add a new rule…': 'Add a new rule…', - 'Add {{type}} permission rule': 'Add {{type}} permission rule', - 'Permission rules are a tool name, optionally followed by a specifier in parentheses.': - 'Permission rules are a tool name, optionally followed by a specifier in parentheses.', - 'e.g.,': 'e.g.,', - or: 'or', - 'Enter permission rule…': 'Enter permission rule…', - 'Enter to submit · Esc to cancel': 'Enter to submit · Esc to cancel', - 'Where should this rule be saved?': 'Where should this rule be saved?', - 'Enter to confirm · Esc to cancel': 'Enter to confirm · Esc to cancel', - 'Delete {{type}} rule?': 'Delete {{type}} rule?', - 'Are you sure you want to delete this permission rule?': - 'Are you sure you want to delete this permission rule?', - 'Permissions:': 'Permissions:', - '(←/→ or tab to cycle)': '(←/→ or tab to cycle)', - 'Press ↑↓ to navigate · Enter to select · Type to search · Esc to cancel': - 'Press ↑↓ to navigate · Enter to select · Type to search · Esc to cancel', - 'Search…': 'Search…', - 'Use /trust to manage folder trust settings for this workspace.': - 'Use /trust to manage folder trust settings for this workspace.', + "Qwen Code will ask before using these tools.": "Qwen Code will ask before using these tools.", + "Qwen Code is not allowed to use denied tools.": "Qwen Code is not allowed to use denied tools.", + "Manage trusted directories for this workspace.": + "Manage trusted directories for this workspace.", + "Any use of the {{tool}} tool": "Any use of the {{tool}} tool", + "{{tool}} commands matching '{{pattern}}'": "{{tool}} commands matching '{{pattern}}'", + "From user settings": "From user settings", + "From project settings": "From project settings", + "From session": "From session", + "Project settings (local)": "Project settings (local)", + "Saved in .qwen/settings.local.json": "Saved in .qwen/settings.local.json", + "Project settings": "Project settings", + "Checked in at .qwen/settings.json": "Checked in at .qwen/settings.json", + "User settings": "User settings", + "Saved in at ~/.qwen/settings.json": "Saved in at ~/.qwen/settings.json", + "Add a new rule…": "Add a new rule…", + "Add {{type}} permission rule": "Add {{type}} permission rule", + "Permission rules are a tool name, optionally followed by a specifier in parentheses.": + "Permission rules are a tool name, optionally followed by a specifier in parentheses.", + "e.g.,": "e.g.,", + or: "or", + "Enter permission rule…": "Enter permission rule…", + "Enter to submit · Esc to cancel": "Enter to submit · Esc to cancel", + "Where should this rule be saved?": "Where should this rule be saved?", + "Enter to confirm · Esc to cancel": "Enter to confirm · Esc to cancel", + "Delete {{type}} rule?": "Delete {{type}} rule?", + "Are you sure you want to delete this permission rule?": + "Are you sure you want to delete this permission rule?", + "Permissions:": "Permissions:", + "(←/→ or tab to cycle)": "(←/→ or tab to cycle)", + "Press ↑↓ to navigate · Enter to select · Type to search · Esc to cancel": + "Press ↑↓ to navigate · Enter to select · Type to search · Esc to cancel", + "Search…": "Search…", + "Use /trust to manage folder trust settings for this workspace.": + "Use /trust to manage folder trust settings for this workspace.", // Workspace directory management - 'Add directory…': 'Add directory…', - 'Add directory to workspace': 'Add directory to workspace', - 'Qwen Code can read files in the workspace, and make edits when auto-accept edits is on.': - 'Qwen Code can read files in the workspace, and make edits when auto-accept edits is on.', - 'Qwen Code will be able to read files in this directory and make edits when auto-accept edits is on.': - 'Qwen Code will be able to read files in this directory and make edits when auto-accept edits is on.', - 'Enter the path to the directory:': 'Enter the path to the directory:', - 'Enter directory path…': 'Enter directory path…', - 'Tab to complete · Enter to add · Esc to cancel': - 'Tab to complete · Enter to add · Esc to cancel', - 'Remove directory?': 'Remove directory?', - 'Are you sure you want to remove this directory from the workspace?': - 'Are you sure you want to remove this directory from the workspace?', - ' (Original working directory)': ' (Original working directory)', - ' (from settings)': ' (from settings)', - 'Directory does not exist.': 'Directory does not exist.', - 'Path is not a directory.': 'Path is not a directory.', - 'This directory is already in the workspace.': - 'This directory is already in the workspace.', - 'Already covered by existing directory: {{dir}}': - 'Already covered by existing directory: {{dir}}', + "Add directory…": "Add directory…", + "Add directory to workspace": "Add directory to workspace", + "Qwen Code can read files in the workspace, and make edits when auto-accept edits is on.": + "Qwen Code can read files in the workspace, and make edits when auto-accept edits is on.", + "Qwen Code will be able to read files in this directory and make edits when auto-accept edits is on.": + "Qwen Code will be able to read files in this directory and make edits when auto-accept edits is on.", + "Enter the path to the directory:": "Enter the path to the directory:", + "Enter directory path…": "Enter directory path…", + "Tab to complete · Enter to add · Esc to cancel": + "Tab to complete · Enter to add · Esc to cancel", + "Remove directory?": "Remove directory?", + "Are you sure you want to remove this directory from the workspace?": + "Are you sure you want to remove this directory from the workspace?", + " (Original working directory)": " (Original working directory)", + " (from settings)": " (from settings)", + "Directory does not exist.": "Directory does not exist.", + "Path is not a directory.": "Path is not a directory.", + "This directory is already in the workspace.": "This directory is already in the workspace.", + "Already covered by existing directory: {{dir}}": + "Already covered by existing directory: {{dir}}", // ============================================================================ // Status Bar // ============================================================================ - 'Using:': 'Using:', - '{{count}} open file': '{{count}} open file', - '{{count}} open files': '{{count}} open files', - '(ctrl+g to view)': '(ctrl+g to view)', - '{{count}} {{name}} file': '{{count}} {{name}} file', - '{{count}} {{name}} files': '{{count}} {{name}} files', - '{{count}} MCP server': '{{count}} MCP server', - '{{count}} MCP servers': '{{count}} MCP servers', - '{{count}} Blocked': '{{count}} Blocked', - '(ctrl+t to view)': '(ctrl+t to view)', - '(ctrl+t to toggle)': '(ctrl+t to toggle)', - 'Press Ctrl+C again to exit.': 'Press Ctrl+C again to exit.', - 'Press Ctrl+D again to exit.': 'Press Ctrl+D again to exit.', - 'Press Esc again to clear.': 'Press Esc again to clear.', + "Using:": "Using:", + "{{count}} open file": "{{count}} open file", + "{{count}} open files": "{{count}} open files", + "(ctrl+g to view)": "(ctrl+g to view)", + "{{count}} {{name}} file": "{{count}} {{name}} file", + "{{count}} {{name}} files": "{{count}} {{name}} files", + "{{count}} MCP server": "{{count}} MCP server", + "{{count}} MCP servers": "{{count}} MCP servers", + "{{count}} Blocked": "{{count}} Blocked", + "(ctrl+t to view)": "(ctrl+t to view)", + "(ctrl+t to toggle)": "(ctrl+t to toggle)", + "Press Ctrl+C again to exit.": "Press Ctrl+C again to exit.", + "Press Ctrl+D again to exit.": "Press Ctrl+D again to exit.", + "Press Esc again to clear.": "Press Esc again to clear.", // ============================================================================ // MCP Status // ============================================================================ - 'No MCP servers configured.': 'No MCP servers configured.', - '⏳ MCP servers are starting up ({{count}} initializing)...': - '⏳ MCP servers are starting up ({{count}} initializing)...', - 'Note: First startup may take longer. Tool availability will update automatically.': - 'Note: First startup may take longer. Tool availability will update automatically.', - 'Configured MCP servers:': 'Configured MCP servers:', - Ready: 'Ready', - 'Starting... (first startup may take longer)': - 'Starting... (first startup may take longer)', - Disconnected: 'Disconnected', - '{{count}} tool': '{{count}} tool', - '{{count}} tools': '{{count}} tools', - '{{count}} prompt': '{{count}} prompt', - '{{count}} prompts': '{{count}} prompts', - '(from {{extensionName}})': '(from {{extensionName}})', - OAuth: 'OAuth', - 'OAuth expired': 'OAuth expired', - 'OAuth not authenticated': 'OAuth not authenticated', - 'tools and prompts will appear when ready': - 'tools and prompts will appear when ready', - '{{count}} tools cached': '{{count}} tools cached', - 'Tools:': 'Tools:', - 'Parameters:': 'Parameters:', - 'Prompts:': 'Prompts:', - Blocked: 'Blocked', - '💡 Tips:': '💡 Tips:', - Use: 'Use', - 'to show server and tool descriptions': - 'to show server and tool descriptions', - 'to show tool parameter schemas': 'to show tool parameter schemas', - 'to hide descriptions': 'to hide descriptions', - 'to authenticate with OAuth-enabled servers': - 'to authenticate with OAuth-enabled servers', - Press: 'Press', - 'to toggle tool descriptions on/off': 'to toggle tool descriptions on/off', + "No MCP servers configured.": "No MCP servers configured.", + "⏳ MCP servers are starting up ({{count}} initializing)...": + "⏳ MCP servers are starting up ({{count}} initializing)...", + "Note: First startup may take longer. Tool availability will update automatically.": + "Note: First startup may take longer. Tool availability will update automatically.", + "Configured MCP servers:": "Configured MCP servers:", + Ready: "Ready", + "Starting... (first startup may take longer)": "Starting... (first startup may take longer)", + Disconnected: "Disconnected", + "{{count}} tool": "{{count}} tool", + "{{count}} tools": "{{count}} tools", + "{{count}} prompt": "{{count}} prompt", + "{{count}} prompts": "{{count}} prompts", + "(from {{extensionName}})": "(from {{extensionName}})", + OAuth: "OAuth", + "OAuth expired": "OAuth expired", + "OAuth not authenticated": "OAuth not authenticated", + "tools and prompts will appear when ready": "tools and prompts will appear when ready", + "{{count}} tools cached": "{{count}} tools cached", + "Tools:": "Tools:", + "Parameters:": "Parameters:", + "Prompts:": "Prompts:", + Blocked: "Blocked", + "💡 Tips:": "💡 Tips:", + Use: "Use", + "to show server and tool descriptions": "to show server and tool descriptions", + "to show tool parameter schemas": "to show tool parameter schemas", + "to hide descriptions": "to hide descriptions", + "to authenticate with OAuth-enabled servers": "to authenticate with OAuth-enabled servers", + Press: "Press", + "to toggle tool descriptions on/off": "to toggle tool descriptions on/off", "Starting OAuth authentication for MCP server '{{name}}'...": "Starting OAuth authentication for MCP server '{{name}}'...", - 'Restarting MCP servers...': 'Restarting MCP servers...', + "Restarting MCP servers...": "Restarting MCP servers...", // ============================================================================ // Startup Tips // ============================================================================ - 'Tips:': 'Tips:', - 'Use /compress when the conversation gets long to summarize history and free up context.': - 'Use /compress when the conversation gets long to summarize history and free up context.', - 'Start a fresh idea with /clear or /new; the previous session stays available in history.': - 'Start a fresh idea with /clear or /new; the previous session stays available in history.', - 'Use /bug to submit issues to the maintainers when something goes off.': - 'Use /bug to submit issues to the maintainers when something goes off.', - 'Switch auth type quickly with /auth.': - 'Switch auth type quickly with /auth.', - 'You can run any shell commands from Qwen Code using ! (e.g. !ls).': - 'You can run any shell commands from Qwen Code using ! (e.g. !ls).', - 'Type / to open the command popup; Tab autocompletes slash commands and saved prompts.': - 'Type / to open the command popup; Tab autocompletes slash commands and saved prompts.', - 'You can resume a previous conversation by running qwen --continue or qwen --resume.': - 'You can resume a previous conversation by running qwen --continue or qwen --resume.', - 'You can switch permission mode quickly with Shift+Tab or /approval-mode.': - 'You can switch permission mode quickly with Shift+Tab or /approval-mode.', - 'You can switch permission mode quickly with Tab or /approval-mode.': - 'You can switch permission mode quickly with Tab or /approval-mode.', - 'Try /insight to generate personalized insights from your chat history.': - 'Try /insight to generate personalized insights from your chat history.', + "Tips:": "Tips:", + "Use /compress when the conversation gets long to summarize history and free up context.": + "Use /compress when the conversation gets long to summarize history and free up context.", + "Start a fresh idea with /clear or /new; the previous session stays available in history.": + "Start a fresh idea with /clear or /new; the previous session stays available in history.", + "Use /bug to submit issues to the maintainers when something goes off.": + "Use /bug to submit issues to the maintainers when something goes off.", + "Switch auth type quickly with /auth.": "Switch auth type quickly with /auth.", + "You can run any shell commands from Qwen Code using ! (e.g. !ls).": + "You can run any shell commands from Qwen Code using ! (e.g. !ls).", + "Type / to open the command popup; Tab autocompletes slash commands and saved prompts.": + "Type / to open the command popup; Tab autocompletes slash commands and saved prompts.", + "You can resume a previous conversation by running qwen --continue or qwen --resume.": + "You can resume a previous conversation by running qwen --continue or qwen --resume.", + "You can switch permission mode quickly with Shift+Tab or /approval-mode.": + "You can switch permission mode quickly with Shift+Tab or /approval-mode.", + "You can switch permission mode quickly with Tab or /approval-mode.": + "You can switch permission mode quickly with Tab or /approval-mode.", + "Try /insight to generate personalized insights from your chat history.": + "Try /insight to generate personalized insights from your chat history.", // ============================================================================ // Exit Screen / Stats // ============================================================================ - 'Agent powering down. Goodbye!': 'Agent powering down. Goodbye!', - 'To continue this session, run': 'To continue this session, run', - 'Interaction Summary': 'Interaction Summary', - 'Session ID:': 'Session ID:', - 'Tool Calls:': 'Tool Calls:', - 'Success Rate:': 'Success Rate:', - 'User Agreement:': 'User Agreement:', - reviewed: 'reviewed', - 'Code Changes:': 'Code Changes:', - Performance: 'Performance', - 'Wall Time:': 'Wall Time:', - 'Agent Active:': 'Agent Active:', - 'API Time:': 'API Time:', - 'Tool Time:': 'Tool Time:', - 'Session Stats': 'Session Stats', - 'Model Usage': 'Model Usage', - Reqs: 'Reqs', - 'Input Tokens': 'Input Tokens', - 'Output Tokens': 'Output Tokens', - 'Savings Highlight:': 'Savings Highlight:', - 'of input tokens were served from the cache, reducing costs.': - 'of input tokens were served from the cache, reducing costs.', - 'Tip: For a full token breakdown, run `/stats model`.': - 'Tip: For a full token breakdown, run `/stats model`.', - 'Model Stats For Nerds': 'Model Stats For Nerds', - 'Tool Stats For Nerds': 'Tool Stats For Nerds', - Metric: 'Metric', - API: 'API', - Requests: 'Requests', - Errors: 'Errors', - 'Avg Latency': 'Avg Latency', - Tokens: 'Tokens', - Total: 'Total', - Prompt: 'Prompt', - Cached: 'Cached', - Thoughts: 'Thoughts', - Tool: 'Tool', - Output: 'Output', - 'No API calls have been made in this session.': - 'No API calls have been made in this session.', - 'Tool Name': 'Tool Name', - Calls: 'Calls', - 'Success Rate': 'Success Rate', - 'Avg Duration': 'Avg Duration', - 'User Decision Summary': 'User Decision Summary', - 'Total Reviewed Suggestions:': 'Total Reviewed Suggestions:', - ' » Accepted:': ' » Accepted:', - ' » Rejected:': ' » Rejected:', - ' » Modified:': ' » Modified:', - ' Overall Agreement Rate:': ' Overall Agreement Rate:', - 'No tool calls have been made in this session.': - 'No tool calls have been made in this session.', - 'Session start time is unavailable, cannot calculate stats.': - 'Session start time is unavailable, cannot calculate stats.', + "Agent powering down. Goodbye!": "Agent powering down. Goodbye!", + "To continue this session, run": "To continue this session, run", + "Interaction Summary": "Interaction Summary", + "Session ID:": "Session ID:", + "Tool Calls:": "Tool Calls:", + "Success Rate:": "Success Rate:", + "User Agreement:": "User Agreement:", + reviewed: "reviewed", + "Code Changes:": "Code Changes:", + Performance: "Performance", + "Wall Time:": "Wall Time:", + "Agent Active:": "Agent Active:", + "API Time:": "API Time:", + "Tool Time:": "Tool Time:", + "Session Stats": "Session Stats", + "Model Usage": "Model Usage", + Reqs: "Reqs", + "Input Tokens": "Input Tokens", + "Output Tokens": "Output Tokens", + "Savings Highlight:": "Savings Highlight:", + "of input tokens were served from the cache, reducing costs.": + "of input tokens were served from the cache, reducing costs.", + "Tip: For a full token breakdown, run `/stats model`.": + "Tip: For a full token breakdown, run `/stats model`.", + "Model Stats For Nerds": "Model Stats For Nerds", + "Tool Stats For Nerds": "Tool Stats For Nerds", + Metric: "Metric", + API: "API", + Requests: "Requests", + Errors: "Errors", + "Avg Latency": "Avg Latency", + Tokens: "Tokens", + Total: "Total", + Prompt: "Prompt", + Cached: "Cached", + Thoughts: "Thoughts", + Tool: "Tool", + Output: "Output", + "No API calls have been made in this session.": "No API calls have been made in this session.", + "Tool Name": "Tool Name", + Calls: "Calls", + "Success Rate": "Success Rate", + "Avg Duration": "Avg Duration", + "User Decision Summary": "User Decision Summary", + "Total Reviewed Suggestions:": "Total Reviewed Suggestions:", + " » Accepted:": " » Accepted:", + " » Rejected:": " » Rejected:", + " » Modified:": " » Modified:", + " Overall Agreement Rate:": " Overall Agreement Rate:", + "No tool calls have been made in this session.": "No tool calls have been made in this session.", + "Session start time is unavailable, cannot calculate stats.": + "Session start time is unavailable, cannot calculate stats.", // ============================================================================ // Command Format Migration // ============================================================================ - 'Command Format Migration': 'Command Format Migration', - 'Found {{count}} TOML command file:': 'Found {{count}} TOML command file:', - 'Found {{count}} TOML command files:': 'Found {{count}} TOML command files:', - '... and {{count}} more': '... and {{count}} more', - 'The TOML format is deprecated. Would you like to migrate them to Markdown format?': - 'The TOML format is deprecated. Would you like to migrate them to Markdown format?', - '(Backups will be created and original files will be preserved)': - '(Backups will be created and original files will be preserved)', + "Command Format Migration": "Command Format Migration", + "Found {{count}} TOML command file:": "Found {{count}} TOML command file:", + "Found {{count}} TOML command files:": "Found {{count}} TOML command files:", + "... and {{count}} more": "... and {{count}} more", + "The TOML format is deprecated. Would you like to migrate them to Markdown format?": + "The TOML format is deprecated. Would you like to migrate them to Markdown format?", + "(Backups will be created and original files will be preserved)": + "(Backups will be created and original files will be preserved)", // ============================================================================ // Loading Phrases // ============================================================================ - 'Waiting for user confirmation...': 'Waiting for user confirmation...', - '(esc to cancel, {{time}})': '(esc to cancel, {{time}})', + "Waiting for user confirmation...": "Waiting for user confirmation...", + "(esc to cancel, {{time}})": "(esc to cancel, {{time}})", // ============================================================================ // Loading Phrases // ============================================================================ WITTY_LOADING_PHRASES: [ "I'm Feeling Lucky", - 'Shipping awesomeness... ', - 'Painting the serifs back on...', - 'Navigating the slime mold...', - 'Consulting the digital spirits...', - 'Reticulating splines...', - 'Warming up the AI hamsters...', - 'Asking the magic conch shell...', - 'Generating witty retort...', - 'Polishing the algorithms...', + "Shipping awesomeness... ", + "Painting the serifs back on...", + "Navigating the slime mold...", + "Consulting the digital spirits...", + "Reticulating splines...", + "Warming up the AI hamsters...", + "Asking the magic conch shell...", + "Generating witty retort...", + "Polishing the algorithms...", "Don't rush perfection (or my code)...", - 'Brewing fresh bytes...', - 'Counting electrons...', - 'Engaging cognitive processors...', - 'Checking for syntax errors in the universe...', - 'One moment, optimizing humor...', - 'Shuffling punchlines...', - 'Untangling neural nets...', - 'Compiling brilliance...', - 'Loading wit.exe...', - 'Summoning the cloud of wisdom...', - 'Preparing a witty response...', + "Brewing fresh bytes...", + "Counting electrons...", + "Engaging cognitive processors...", + "Checking for syntax errors in the universe...", + "One moment, optimizing humor...", + "Shuffling punchlines...", + "Untangling neural nets...", + "Compiling brilliance...", + "Loading wit.exe...", + "Summoning the cloud of wisdom...", + "Preparing a witty response...", "Just a sec, I'm debugging reality...", - 'Confuzzling the options...', - 'Tuning the cosmic frequencies...', - 'Crafting a response worthy of your patience...', - 'Compiling the 1s and 0s...', - 'Resolving dependencies... and existential crises...', - 'Defragmenting memories... both RAM and personal...', - 'Rebooting the humor module...', - 'Caching the essentials (mostly cat memes)...', - 'Optimizing for ludicrous speed', + "Confuzzling the options...", + "Tuning the cosmic frequencies...", + "Crafting a response worthy of your patience...", + "Compiling the 1s and 0s...", + "Resolving dependencies... and existential crises...", + "Defragmenting memories... both RAM and personal...", + "Rebooting the humor module...", + "Caching the essentials (mostly cat memes)...", + "Optimizing for ludicrous speed", "Swapping bits... don't tell the bytes...", - 'Garbage collecting... be right back...', - 'Assembling the interwebs...', - 'Converting coffee into code...', - 'Updating the syntax for reality...', - 'Rewiring the synapses...', - 'Looking for a misplaced semicolon...', + "Garbage collecting... be right back...", + "Assembling the interwebs...", + "Converting coffee into code...", + "Updating the syntax for reality...", + "Rewiring the synapses...", + "Looking for a misplaced semicolon...", "Greasin' the cogs of the machine...", - 'Pre-heating the servers...', - 'Calibrating the flux capacitor...', - 'Engaging the improbability drive...', - 'Channeling the Force...', - 'Aligning the stars for optimal response...', - 'So say we all...', - 'Loading the next great idea...', + "Pre-heating the servers...", + "Calibrating the flux capacitor...", + "Engaging the improbability drive...", + "Channeling the Force...", + "Aligning the stars for optimal response...", + "So say we all...", + "Loading the next great idea...", "Just a moment, I'm in the zone...", - 'Preparing to dazzle you with brilliance...', + "Preparing to dazzle you with brilliance...", "Just a tick, I'm polishing my wit...", "Hold tight, I'm crafting a masterpiece...", "Just a jiffy, I'm debugging the universe...", "Just a moment, I'm aligning the pixels...", "Just a sec, I'm optimizing the humor...", "Just a moment, I'm tuning the algorithms...", - 'Warp speed engaged...', - 'Mining for more Dilithium crystals...', + "Warp speed engaged...", + "Mining for more Dilithium crystals...", "Don't panic...", - 'Following the white rabbit...', - 'The truth is in here... somewhere...', - 'Blowing on the cartridge...', - 'Loading... Do a barrel roll!', - 'Waiting for the respawn...', - 'Finishing the Kessel Run in less than 12 parsecs...', + "Following the white rabbit...", + "The truth is in here... somewhere...", + "Blowing on the cartridge...", + "Loading... Do a barrel roll!", + "Waiting for the respawn...", + "Finishing the Kessel Run in less than 12 parsecs...", "The cake is not a lie, it's just still loading...", - 'Fiddling with the character creation screen...', + "Fiddling with the character creation screen...", "Just a moment, I'm finding the right meme...", "Pressing 'A' to continue...", - 'Herding digital cats...', - 'Polishing the pixels...', - 'Finding a suitable loading screen pun...', - 'Distracting you with this witty phrase...', - 'Almost there... probably...', - 'Our hamsters are working as fast as they can...', - 'Giving Cloudy a pat on the head...', - 'Petting the cat...', - 'Rickrolling my boss...', - 'Never gonna give you up, never gonna let you down...', - 'Slapping the bass...', - 'Tasting the snozberries...', + "Herding digital cats...", + "Polishing the pixels...", + "Finding a suitable loading screen pun...", + "Distracting you with this witty phrase...", + "Almost there... probably...", + "Our hamsters are working as fast as they can...", + "Giving Cloudy a pat on the head...", + "Petting the cat...", + "Rickrolling my boss...", + "Never gonna give you up, never gonna let you down...", + "Slapping the bass...", + "Tasting the snozberries...", "I'm going the distance, I'm going for speed...", - 'Is this the real life? Is this just fantasy?...', + "Is this the real life? Is this just fantasy?...", "I've got a good feeling about this...", - 'Poking the bear...', - 'Doing research on the latest memes...', - 'Figuring out how to make this more witty...', - 'Hmmm... let me think...', - 'What do you call a fish with no eyes? A fsh...', - 'Why did the computer go to therapy? It had too many bytes...', + "Poking the bear...", + "Doing research on the latest memes...", + "Figuring out how to make this more witty...", + "Hmmm... let me think...", + "What do you call a fish with no eyes? A fsh...", + "Why did the computer go to therapy? It had too many bytes...", "Why don't programmers like nature? It has too many bugs...", - 'Why do programmers prefer dark mode? Because light attracts bugs...', - 'Why did the developer go broke? Because they used up all their cache...', + "Why do programmers prefer dark mode? Because light attracts bugs...", + "Why did the developer go broke? Because they used up all their cache...", "What can you do with a broken pencil? Nothing, it's pointless...", - 'Applying percussive maintenance...', - 'Searching for the correct USB orientation...', - 'Ensuring the magic smoke stays inside the wires...', - 'Trying to exit Vim...', - 'Spinning up the hamster wheel...', + "Applying percussive maintenance...", + "Searching for the correct USB orientation...", + "Ensuring the magic smoke stays inside the wires...", + "Trying to exit Vim...", + "Spinning up the hamster wheel...", "That's not a bug, it's an undocumented feature...", - 'Engage.', + "Engage.", "I'll be back... with an answer.", - 'My other process is a TARDIS...', - 'Communing with the machine spirit...', - 'Letting the thoughts marinate...', - 'Just remembered where I put my keys...', - 'Pondering the orb...', + "My other process is a TARDIS...", + "Communing with the machine spirit...", + "Letting the thoughts marinate...", + "Just remembered where I put my keys...", + "Pondering the orb...", "I've seen things you people wouldn't believe... like a user who reads loading messages.", - 'Initiating thoughtful gaze...', + "Initiating thoughtful gaze...", "What's a computer's favorite snack? Microchips.", "Why do Java developers wear glasses? Because they don't C#.", - 'Charging the laser... pew pew!', - 'Dividing by zero... just kidding!', - 'Looking for an adult superviso... I mean, processing.', - 'Making it go beep boop.', - 'Buffering... because even AIs need a moment.', - 'Entangling quantum particles for a faster response...', - 'Polishing the chrome... on the algorithms.', - 'Are you not entertained? (Working on it!)', - 'Summoning the code gremlins... to help, of course.', - 'Just waiting for the dial-up tone to finish...', - 'Recalibrating the humor-o-meter.', - 'My other loading screen is even funnier.', + "Charging the laser... pew pew!", + "Dividing by zero... just kidding!", + "Looking for an adult superviso... I mean, processing.", + "Making it go beep boop.", + "Buffering... because even AIs need a moment.", + "Entangling quantum particles for a faster response...", + "Polishing the chrome... on the algorithms.", + "Are you not entertained? (Working on it!)", + "Summoning the code gremlins... to help, of course.", + "Just waiting for the dial-up tone to finish...", + "Recalibrating the humor-o-meter.", + "My other loading screen is even funnier.", "Pretty sure there's a cat walking on the keyboard somewhere...", - 'Enhancing... Enhancing... Still loading.', + "Enhancing... Enhancing... Still loading.", "It's not a bug, it's a feature... of this loading screen.", - 'Have you tried turning it off and on again? (The loading screen, not me.)', - 'Constructing additional pylons...', + "Have you tried turning it off and on again? (The loading screen, not me.)", + "Constructing additional pylons...", ], // ============================================================================ // Extension Settings Input // ============================================================================ - 'Enter value...': 'Enter value...', - 'Enter sensitive value...': 'Enter sensitive value...', - 'Press Enter to submit, Escape to cancel': - 'Press Enter to submit, Escape to cancel', + "Enter value...": "Enter value...", + "Enter sensitive value...": "Enter sensitive value...", + "Press Enter to submit, Escape to cancel": "Press Enter to submit, Escape to cancel", // ============================================================================ // Command Migration Tool // ============================================================================ - 'Markdown file already exists: {{filename}}': - 'Markdown file already exists: {{filename}}', - 'TOML Command Format Deprecation Notice': - 'TOML Command Format Deprecation Notice', - 'Found {{count}} command file(s) in TOML format:': - 'Found {{count}} command file(s) in TOML format:', - 'The TOML format for commands is being deprecated in favor of Markdown format.': - 'The TOML format for commands is being deprecated in favor of Markdown format.', - 'Markdown format is more readable and easier to edit.': - 'Markdown format is more readable and easier to edit.', - 'You can migrate these files automatically using:': - 'You can migrate these files automatically using:', - 'Or manually convert each file:': 'Or manually convert each file:', - 'TOML: prompt = "..." / description = "..."': - 'TOML: prompt = "..." / description = "..."', - 'Markdown: YAML frontmatter + content': - 'Markdown: YAML frontmatter + content', - 'The migration tool will:': 'The migration tool will:', - 'Convert TOML files to Markdown': 'Convert TOML files to Markdown', - 'Create backups of original files': 'Create backups of original files', - 'Preserve all command functionality': 'Preserve all command functionality', - 'TOML format will continue to work for now, but migration is recommended.': - 'TOML format will continue to work for now, but migration is recommended.', + "Markdown file already exists: {{filename}}": "Markdown file already exists: {{filename}}", + "TOML Command Format Deprecation Notice": "TOML Command Format Deprecation Notice", + "Found {{count}} command file(s) in TOML format:": + "Found {{count}} command file(s) in TOML format:", + "The TOML format for commands is being deprecated in favor of Markdown format.": + "The TOML format for commands is being deprecated in favor of Markdown format.", + "Markdown format is more readable and easier to edit.": + "Markdown format is more readable and easier to edit.", + "You can migrate these files automatically using:": + "You can migrate these files automatically using:", + "Or manually convert each file:": "Or manually convert each file:", + 'TOML: prompt = "..." / description = "..."': 'TOML: prompt = "..." / description = "..."', + "Markdown: YAML frontmatter + content": "Markdown: YAML frontmatter + content", + "The migration tool will:": "The migration tool will:", + "Convert TOML files to Markdown": "Convert TOML files to Markdown", + "Create backups of original files": "Create backups of original files", + "Preserve all command functionality": "Preserve all command functionality", + "TOML format will continue to work for now, but migration is recommended.": + "TOML format will continue to work for now, but migration is recommended.", // ============================================================================ // Extensions - Explore Command // ============================================================================ - 'Open extensions page in your browser': - 'Open extensions page in your browser', - 'Unknown extensions source: {{source}}.': - 'Unknown extensions source: {{source}}.', - 'Would open extensions page in your browser: {{url}} (skipped in test environment)': - 'Would open extensions page in your browser: {{url}} (skipped in test environment)', - 'View available extensions at {{url}}': - 'View available extensions at {{url}}', - 'Opening extensions page in your browser: {{url}}': - 'Opening extensions page in your browser: {{url}}', - 'Failed to open browser. Check out the extensions gallery at {{url}}': - 'Failed to open browser. Check out the extensions gallery at {{url}}', + "Open extensions page in your browser": "Open extensions page in your browser", + "Unknown extensions source: {{source}}.": "Unknown extensions source: {{source}}.", + "Would open extensions page in your browser: {{url}} (skipped in test environment)": + "Would open extensions page in your browser: {{url}} (skipped in test environment)", + "View available extensions at {{url}}": "View available extensions at {{url}}", + "Opening extensions page in your browser: {{url}}": + "Opening extensions page in your browser: {{url}}", + "Failed to open browser. Check out the extensions gallery at {{url}}": + "Failed to open browser. Check out the extensions gallery at {{url}}", // ============================================================================ // Retry / Rate Limit // ============================================================================ - 'Rate limit error: {{reason}}': 'Rate limit error: {{reason}}', - 'Retrying in {{seconds}} seconds… (attempt {{attempt}}/{{maxRetries}})': - 'Retrying in {{seconds}} seconds… (attempt {{attempt}}/{{maxRetries}})', - 'Press Ctrl+Y to retry': 'Press Ctrl+Y to retry', - 'No failed request to retry.': 'No failed request to retry.', - 'to retry last request': 'to retry last request', + "Rate limit error: {{reason}}": "Rate limit error: {{reason}}", + "Retrying in {{seconds}} seconds… (attempt {{attempt}}/{{maxRetries}})": + "Retrying in {{seconds}} seconds… (attempt {{attempt}}/{{maxRetries}})", + "Press Ctrl+Y to retry": "Press Ctrl+Y to retry", + "No failed request to retry.": "No failed request to retry.", + "to retry last request": "to retry last request", // ============================================================================ // Coding Plan Authentication // ============================================================================ - 'API key cannot be empty.': 'API key cannot be empty.', - 'You can get your Coding Plan API key here': - 'You can get your Coding Plan API key here', - 'API key is stored in settings.env. You can migrate it to a .env file for better security.': - 'API key is stored in settings.env. You can migrate it to a .env file for better security.', - 'New model configurations are available for Alibaba Cloud Coding Plan. Update now?': - 'New model configurations are available for Alibaba Cloud Coding Plan. Update now?', - 'Coding Plan configuration updated successfully. New models are now available.': - 'Coding Plan configuration updated successfully. New models are now available.', - 'Coding Plan API key not found. Please re-authenticate with Coding Plan.': - 'Coding Plan API key not found. Please re-authenticate with Coding Plan.', - 'Failed to update Coding Plan configuration: {{message}}': - 'Failed to update Coding Plan configuration: {{message}}', + "API key cannot be empty.": "API key cannot be empty.", + "You can get your Coding Plan API key here": "You can get your Coding Plan API key here", + "API key is stored in settings.env. You can migrate it to a .env file for better security.": + "API key is stored in settings.env. You can migrate it to a .env file for better security.", + "New model configurations are available for Alibaba Cloud Coding Plan. Update now?": + "New model configurations are available for Alibaba Cloud Coding Plan. Update now?", + "Coding Plan configuration updated successfully. New models are now available.": + "Coding Plan configuration updated successfully. New models are now available.", + "Coding Plan API key not found. Please re-authenticate with Coding Plan.": + "Coding Plan API key not found. Please re-authenticate with Coding Plan.", + "Failed to update Coding Plan configuration: {{message}}": + "Failed to update Coding Plan configuration: {{message}}", // ============================================================================ // Custom API Key Configuration // ============================================================================ - 'You can configure your API key and models in settings.json': - 'You can configure your API key and models in settings.json', - 'Refer to the documentation for setup instructions': - 'Refer to the documentation for setup instructions', + "You can configure your API key and models in settings.json": + "You can configure your API key and models in settings.json", + "Refer to the documentation for setup instructions": + "Refer to the documentation for setup instructions", // ============================================================================ // Auth Dialog - View Titles and Labels // ============================================================================ - 'Coding Plan': 'Coding Plan', + "Coding Plan": "Coding Plan", "Paste your api key of ModelStudio Coding Plan and you're all set!": "Paste your api key of ModelStudio Coding Plan and you're all set!", - Custom: 'Custom', - 'More instructions about configuring `modelProviders` manually.': - 'More instructions about configuring `modelProviders` manually.', - 'Select API-KEY configuration mode:': 'Select API-KEY configuration mode:', - '(Press Escape to go back)': '(Press Escape to go back)', - '(Press Enter to submit, Escape to cancel)': - '(Press Enter to submit, Escape to cancel)', - 'Select Region for Coding Plan': 'Select Region for Coding Plan', - 'Choose based on where your account is registered': - 'Choose based on where your account is registered', - 'Enter Coding Plan API Key': 'Enter Coding Plan API Key', + Custom: "Custom", + "More instructions about configuring `modelProviders` manually.": + "More instructions about configuring `modelProviders` manually.", + "Select API-KEY configuration mode:": "Select API-KEY configuration mode:", + "(Press Escape to go back)": "(Press Escape to go back)", + "(Press Enter to submit, Escape to cancel)": "(Press Enter to submit, Escape to cancel)", + "Select Region for Coding Plan": "Select Region for Coding Plan", + "Choose based on where your account is registered": + "Choose based on where your account is registered", + "Enter Coding Plan API Key": "Enter Coding Plan API Key", // ============================================================================ // Coding Plan International Updates // ============================================================================ - 'New model configurations are available for {{region}}. Update now?': - 'New model configurations are available for {{region}}. Update now?', + "New model configurations are available for {{region}}. Update now?": + "New model configurations are available for {{region}}. Update now?", '{{region}} configuration updated successfully. Model switched to "{{model}}".': '{{region}} configuration updated successfully. Model switched to "{{model}}".', - 'Authenticated successfully with {{region}}. API key and model configs saved to settings.json (backed up).': - 'Authenticated successfully with {{region}}. API key and model configs saved to settings.json (backed up).', + "Authenticated successfully with {{region}}. API key and model configs saved to settings.json (backed up).": + "Authenticated successfully with {{region}}. API key and model configs saved to settings.json (backed up).", // ============================================================================ // Context Usage Component // ============================================================================ - 'Context Usage': 'Context Usage', - 'No API response yet. Send a message to see actual usage.': - 'No API response yet. Send a message to see actual usage.', - 'Estimated pre-conversation overhead': 'Estimated pre-conversation overhead', - 'Context window': 'Context window', - tokens: 'tokens', - Used: 'Used', - Free: 'Free', - 'Autocompact buffer': 'Autocompact buffer', - 'Usage by category': 'Usage by category', - 'System prompt': 'System prompt', - 'Built-in tools': 'Built-in tools', - 'MCP tools': 'MCP tools', - 'Memory files': 'Memory files', - Skills: 'Skills', - Messages: 'Messages', - 'Show context window usage breakdown.': - 'Show context window usage breakdown.', - 'Run /context detail for per-item breakdown.': - 'Run /context detail for per-item breakdown.', - 'body loaded': 'body loaded', - memory: 'memory', - '{{region}} configuration updated successfully.': - '{{region}} configuration updated successfully.', - 'Authenticated successfully with {{region}}. API key and model configs saved to settings.json.': - 'Authenticated successfully with {{region}}. API key and model configs saved to settings.json.', - 'Tip: Use /model to switch between available Coding Plan models.': - 'Tip: Use /model to switch between available Coding Plan models.', + "Context Usage": "Context Usage", + "No API response yet. Send a message to see actual usage.": + "No API response yet. Send a message to see actual usage.", + "Estimated pre-conversation overhead": "Estimated pre-conversation overhead", + "Context window": "Context window", + tokens: "tokens", + Used: "Used", + Free: "Free", + "Autocompact buffer": "Autocompact buffer", + "Usage by category": "Usage by category", + "System prompt": "System prompt", + "Built-in tools": "Built-in tools", + "MCP tools": "MCP tools", + "Memory files": "Memory files", + Skills: "Skills", + Messages: "Messages", + "Show context window usage breakdown.": "Show context window usage breakdown.", + "Run /context detail for per-item breakdown.": "Run /context detail for per-item breakdown.", + "body loaded": "body loaded", + memory: "memory", + "{{region}} configuration updated successfully.": + "{{region}} configuration updated successfully.", + "Authenticated successfully with {{region}}. API key and model configs saved to settings.json.": + "Authenticated successfully with {{region}}. API key and model configs saved to settings.json.", + "Tip: Use /model to switch between available Coding Plan models.": + "Tip: Use /model to switch between available Coding Plan models.", // ============================================================================ // Ask User Question Tool // ============================================================================ - 'Please answer the following question(s):': - 'Please answer the following question(s):', - 'Cannot ask user questions in non-interactive mode. Please run in interactive mode to use this tool.': - 'Cannot ask user questions in non-interactive mode. Please run in interactive mode to use this tool.', - 'User declined to answer the questions.': - 'User declined to answer the questions.', - 'User has provided the following answers:': - 'User has provided the following answers:', - 'Failed to process user answers:': 'Failed to process user answers:', - 'Type something...': 'Type something...', - Submit: 'Submit', - 'Submit answers': 'Submit answers', - Cancel: 'Cancel', - 'Your answers:': 'Your answers:', - '(not answered)': '(not answered)', - 'Ready to submit your answers?': 'Ready to submit your answers?', - '↑/↓: Navigate | ←/→: Switch tabs | Enter: Select': - '↑/↓: Navigate | ←/→: Switch tabs | Enter: Select', - '↑/↓: Navigate | ←/→: Switch tabs | Space/Enter: Toggle | Esc: Cancel': - '↑/↓: Navigate | ←/→: Switch tabs | Space/Enter: Toggle | Esc: Cancel', - '↑/↓: Navigate | Space/Enter: Toggle | Esc: Cancel': - '↑/↓: Navigate | Space/Enter: Toggle | Esc: Cancel', - '↑/↓: Navigate | Enter: Select | Esc: Cancel': - '↑/↓: Navigate | Enter: Select | Esc: Cancel', + "Please answer the following question(s):": "Please answer the following question(s):", + "Cannot ask user questions in non-interactive mode. Please run in interactive mode to use this tool.": + "Cannot ask user questions in non-interactive mode. Please run in interactive mode to use this tool.", + "User declined to answer the questions.": "User declined to answer the questions.", + "User has provided the following answers:": "User has provided the following answers:", + "Failed to process user answers:": "Failed to process user answers:", + "Type something...": "Type something...", + Submit: "Submit", + "Submit answers": "Submit answers", + Cancel: "Cancel", + "Your answers:": "Your answers:", + "(not answered)": "(not answered)", + "Ready to submit your answers?": "Ready to submit your answers?", + "↑/↓: Navigate | ←/→: Switch tabs | Enter: Select": + "↑/↓: Navigate | ←/→: Switch tabs | Enter: Select", + "↑/↓: Navigate | ←/→: Switch tabs | Space/Enter: Toggle | Esc: Cancel": + "↑/↓: Navigate | ←/→: Switch tabs | Space/Enter: Toggle | Esc: Cancel", + "↑/↓: Navigate | Space/Enter: Toggle | Esc: Cancel": + "↑/↓: Navigate | Space/Enter: Toggle | Esc: Cancel", + "↑/↓: Navigate | Enter: Select | Esc: Cancel": "↑/↓: Navigate | Enter: Select | Esc: Cancel", // ============================================================================ // Commands - Auth // ============================================================================ - 'Authenticate using Alibaba Cloud Coding Plan': - 'Authenticate using Alibaba Cloud Coding Plan', - 'Region for Coding Plan (china/global)': - 'Region for Coding Plan (china/global)', - 'API key for Coding Plan': 'API key for Coding Plan', - 'Show current authentication status': 'Show current authentication status', - 'Authentication completed successfully.': - 'Authentication completed successfully.', - 'Processing Alibaba Cloud Coding Plan authentication...': - 'Processing Alibaba Cloud Coding Plan authentication...', - 'Successfully authenticated with Alibaba Cloud Coding Plan.': - 'Successfully authenticated with Alibaba Cloud Coding Plan.', - 'Failed to authenticate with Coding Plan: {{error}}': - 'Failed to authenticate with Coding Plan: {{error}}', - '中国 (China)': '中国 (China)', - '阿里云百炼 (aliyun.com)': '阿里云百炼 (aliyun.com)', - Global: 'Global', - 'Alibaba Cloud (alibabacloud.com)': 'Alibaba Cloud (alibabacloud.com)', - 'Select region for Coding Plan:': 'Select region for Coding Plan:', - 'Enter your Coding Plan API key: ': 'Enter your Coding Plan API key: ', - 'Select authentication method:': 'Select authentication method:', - '\n=== Authentication Status ===\n': '\n=== Authentication Status ===\n', - '⚠️ No authentication method configured.\n': - '⚠️ No authentication method configured.\n', - 'Run one of the following commands to get started:\n': - 'Run one of the following commands to get started:\n', - ' qwen auth coding-plan - Authenticate with Alibaba Cloud Coding Plan\n': - ' qwen auth coding-plan - Authenticate with Alibaba Cloud Coding Plan\n', - 'Or simply run:': 'Or simply run:', - ' qwen auth - Interactive authentication setup\n': - ' qwen auth - Interactive authentication setup\n', - '✓ Authentication Method: Alibaba Cloud Coding Plan': - '✓ Authentication Method: Alibaba Cloud Coding Plan', - '中国 (China) - 阿里云百炼': '中国 (China) - 阿里云百炼', - 'Global - Alibaba Cloud': 'Global - Alibaba Cloud', - ' Region: {{region}}': ' Region: {{region}}', - ' Current Model: {{model}}': ' Current Model: {{model}}', - ' Config Version: {{version}}': ' Config Version: {{version}}', - ' Status: API key configured\n': ' Status: API key configured\n', - '⚠️ Authentication Method: Alibaba Cloud Coding Plan (Incomplete)': - '⚠️ Authentication Method: Alibaba Cloud Coding Plan (Incomplete)', - ' Issue: API key not found in environment or settings\n': - ' Issue: API key not found in environment or settings\n', - ' Run `qwen auth coding-plan` to re-configure.\n': - ' Run `qwen auth coding-plan` to re-configure.\n', - '✓ Authentication Method: {{type}}': '✓ Authentication Method: {{type}}', - ' Status: Configured\n': ' Status: Configured\n', - 'Failed to check authentication status: {{error}}': - 'Failed to check authentication status: {{error}}', - 'Select an option:': 'Select an option:', - 'Raw mode not available. Please run in an interactive terminal.': - 'Raw mode not available. Please run in an interactive terminal.', - '(Use ↑ ↓ arrows to navigate, Enter to select, Ctrl+C to exit)\n': - '(Use ↑ ↓ arrows to navigate, Enter to select, Ctrl+C to exit)\n', - verbose: 'verbose', - 'Show full tool output and thinking in verbose mode (toggle with Ctrl+O).': - 'Show full tool output and thinking in verbose mode (toggle with Ctrl+O).', - 'Press Ctrl+O to show full tool output': - 'Press Ctrl+O to show full tool output', + "Authenticate using Alibaba Cloud Coding Plan": "Authenticate using Alibaba Cloud Coding Plan", + "Region for Coding Plan (china/global)": "Region for Coding Plan (china/global)", + "API key for Coding Plan": "API key for Coding Plan", + "Show current authentication status": "Show current authentication status", + "Authentication completed successfully.": "Authentication completed successfully.", + "Processing Alibaba Cloud Coding Plan authentication...": + "Processing Alibaba Cloud Coding Plan authentication...", + "Successfully authenticated with Alibaba Cloud Coding Plan.": + "Successfully authenticated with Alibaba Cloud Coding Plan.", + "Failed to authenticate with Coding Plan: {{error}}": + "Failed to authenticate with Coding Plan: {{error}}", + "中国 (China)": "中国 (China)", + "阿里云百炼 (aliyun.com)": "阿里云百炼 (aliyun.com)", + Global: "Global", + "Alibaba Cloud (alibabacloud.com)": "Alibaba Cloud (alibabacloud.com)", + "Select region for Coding Plan:": "Select region for Coding Plan:", + "Enter your Coding Plan API key: ": "Enter your Coding Plan API key: ", + "Select authentication method:": "Select authentication method:", + "\n=== Authentication Status ===\n": "\n=== Authentication Status ===\n", + "⚠️ No authentication method configured.\n": "⚠️ No authentication method configured.\n", + "Run one of the following commands to get started:\n": + "Run one of the following commands to get started:\n", + " qwen auth coding-plan - Authenticate with Alibaba Cloud Coding Plan\n": + " qwen auth coding-plan - Authenticate with Alibaba Cloud Coding Plan\n", + "Or simply run:": "Or simply run:", + " qwen auth - Interactive authentication setup\n": + " qwen auth - Interactive authentication setup\n", + "✓ Authentication Method: Alibaba Cloud Coding Plan": + "✓ Authentication Method: Alibaba Cloud Coding Plan", + "中国 (China) - 阿里云百炼": "中国 (China) - 阿里云百炼", + "Global - Alibaba Cloud": "Global - Alibaba Cloud", + " Region: {{region}}": " Region: {{region}}", + " Current Model: {{model}}": " Current Model: {{model}}", + " Config Version: {{version}}": " Config Version: {{version}}", + " Status: API key configured\n": " Status: API key configured\n", + "⚠️ Authentication Method: Alibaba Cloud Coding Plan (Incomplete)": + "⚠️ Authentication Method: Alibaba Cloud Coding Plan (Incomplete)", + " Issue: API key not found in environment or settings\n": + " Issue: API key not found in environment or settings\n", + " Run `qwen auth coding-plan` to re-configure.\n": + " Run `qwen auth coding-plan` to re-configure.\n", + "✓ Authentication Method: {{type}}": "✓ Authentication Method: {{type}}", + " Status: Configured\n": " Status: Configured\n", + "Failed to check authentication status: {{error}}": + "Failed to check authentication status: {{error}}", + "Select an option:": "Select an option:", + "Raw mode not available. Please run in an interactive terminal.": + "Raw mode not available. Please run in an interactive terminal.", + "(Use ↑ ↓ arrows to navigate, Enter to select, Ctrl+C to exit)\n": + "(Use ↑ ↓ arrows to navigate, Enter to select, Ctrl+C to exit)\n", + verbose: "verbose", + "Show full tool output and thinking in verbose mode (toggle with Ctrl+O).": + "Show full tool output and thinking in verbose mode (toggle with Ctrl+O).", + "Press Ctrl+O to show full tool output": "Press Ctrl+O to show full tool output", - 'Switch to plan mode or exit plan mode': - 'Switch to plan mode or exit plan mode', - 'Exited plan mode. Previous approval mode restored.': - 'Exited plan mode. Previous approval mode restored.', - 'Enabled plan mode. The agent will analyze and plan without executing tools.': - 'Enabled plan mode. The agent will analyze and plan without executing tools.', + "Switch to plan mode or exit plan mode": "Switch to plan mode or exit plan mode", + "Exited plan mode. Previous approval mode restored.": + "Exited plan mode. Previous approval mode restored.", + "Enabled plan mode. The agent will analyze and plan without executing tools.": + "Enabled plan mode. The agent will analyze and plan without executing tools.", 'Already in plan mode. Use "/plan exit" to exit plan mode.': 'Already in plan mode. Use "/plan exit" to exit plan mode.', 'Not in plan mode. Use "/plan" to enter plan mode first.': diff --git a/apps/airiscode-cli/src/i18n/locales/ja.js b/apps/airiscode-cli/src/i18n/locales/ja.js index 136ccc843..5bfaf9403 100644 --- a/apps/airiscode-cli/src/i18n/locales/ja.js +++ b/apps/airiscode-cli/src/i18n/locales/ja.js @@ -10,922 +10,848 @@ export default { // ============================================================================ // Help / UI Components // ============================================================================ - 'Basics:': '基本操作:', - 'Add context': 'コンテキストを追加', - 'Use {{symbol}} to specify files for context (e.g., {{example}}) to target specific files or folders.': - '{{symbol}} を使用してコンテキスト用のファイルを指定します(例: {{example}}) また、特定のファイルやフォルダを対象にできます', - '@': '@', - '@src/myFile.ts': '@src/myFile.ts', - 'Shell mode': 'シェルモード', - 'YOLO mode': 'YOLOモード', - 'plan mode': 'プランモード', - 'auto-accept edits': '編集を自動承認', - 'Accepting edits': '編集を承認中', - '(shift + tab to cycle)': '(Shift + Tab で切り替え)', - 'Execute shell commands via {{symbol}} (e.g., {{example1}}) or use natural language (e.g., {{example2}}).': - '{{symbol}} でシェルコマンドを実行(例: {{example1}})、または自然言語で入力(例: {{example2}})', - '!': '!', - '!npm run start': '!npm run start', - 'start server': 'サーバーを起動', - 'Commands:': 'コマンド:', - 'shell command': 'シェルコマンド', - 'Model Context Protocol command (from external servers)': - 'Model Context Protocol コマンド(外部サーバーから)', - 'Keyboard Shortcuts:': 'キーボードショートカット:', - 'Jump through words in the input': '入力欄の単語間を移動', - 'Close dialogs, cancel requests, or quit application': - 'ダイアログを閉じる、リクエストをキャンセル、またはアプリを終了', - 'New line': '改行', - 'New line (Alt+Enter works for certain linux distros)': - '改行(一部のLinuxディストリビューションではAlt+Enterが有効)', - 'Clear the screen': '画面をクリア', - 'Open input in external editor': '外部エディタで入力を開く', - 'Send message': 'メッセージを送信', - 'Initializing...': '初期化中...', - 'Connecting to MCP servers... ({{connected}}/{{total}})': - 'MCPサーバーに接続中... ({{connected}}/{{total}})', - 'Type your message or @path/to/file': - 'メッセージを入力、@パス/ファイルでファイルを添付(D&D対応)', + "Basics:": "基本操作:", + "Add context": "コンテキストを追加", + "Use {{symbol}} to specify files for context (e.g., {{example}}) to target specific files or folders.": + "{{symbol}} を使用してコンテキスト用のファイルを指定します(例: {{example}}) また、特定のファイルやフォルダを対象にできます", + "@": "@", + "@src/myFile.ts": "@src/myFile.ts", + "Shell mode": "シェルモード", + "YOLO mode": "YOLOモード", + "plan mode": "プランモード", + "auto-accept edits": "編集を自動承認", + "Accepting edits": "編集を承認中", + "(shift + tab to cycle)": "(Shift + Tab で切り替え)", + "Execute shell commands via {{symbol}} (e.g., {{example1}}) or use natural language (e.g., {{example2}}).": + "{{symbol}} でシェルコマンドを実行(例: {{example1}})、または自然言語で入力(例: {{example2}})", + "!": "!", + "!npm run start": "!npm run start", + "start server": "サーバーを起動", + "Commands:": "コマンド:", + "shell command": "シェルコマンド", + "Model Context Protocol command (from external servers)": + "Model Context Protocol コマンド(外部サーバーから)", + "Keyboard Shortcuts:": "キーボードショートカット:", + "Jump through words in the input": "入力欄の単語間を移動", + "Close dialogs, cancel requests, or quit application": + "ダイアログを閉じる、リクエストをキャンセル、またはアプリを終了", + "New line": "改行", + "New line (Alt+Enter works for certain linux distros)": + "改行(一部のLinuxディストリビューションではAlt+Enterが有効)", + "Clear the screen": "画面をクリア", + "Open input in external editor": "外部エディタで入力を開く", + "Send message": "メッセージを送信", + "Initializing...": "初期化中...", + "Connecting to MCP servers... ({{connected}}/{{total}})": + "MCPサーバーに接続中... ({{connected}}/{{total}})", + "Type your message or @path/to/file": "メッセージを入力、@パス/ファイルでファイルを添付(D&D対応)", "Press 'i' for INSERT mode and 'Esc' for NORMAL mode.": "'i' でINSERTモード、'Esc' でNORMALモード", - 'Cancel operation / Clear input (double press)': - '操作をキャンセル / 入力をクリア(2回押し)', - 'Cycle approval modes': '承認モードを切り替え', - 'Cycle through your prompt history': 'プロンプト履歴を順に表示', - 'For a full list of shortcuts, see {{docPath}}': - 'ショートカットの完全なリストは {{docPath}} を参照', - 'docs/keyboard-shortcuts.md': 'docs/keyboard-shortcuts.md', - 'for help on Qwen Code': 'Qwen Code のヘルプ', - 'show version info': 'バージョン情報を表示', - 'submit a bug report': 'バグレポートを送信', - 'About Qwen Code': 'Qwen Code について', + "Cancel operation / Clear input (double press)": "操作をキャンセル / 入力をクリア(2回押し)", + "Cycle approval modes": "承認モードを切り替え", + "Cycle through your prompt history": "プロンプト履歴を順に表示", + "For a full list of shortcuts, see {{docPath}}": + "ショートカットの完全なリストは {{docPath}} を参照", + "docs/keyboard-shortcuts.md": "docs/keyboard-shortcuts.md", + "for help on Qwen Code": "Qwen Code のヘルプ", + "show version info": "バージョン情報を表示", + "submit a bug report": "バグレポートを送信", + "About Qwen Code": "Qwen Code について", // ============================================================================ // System Information Fields // ============================================================================ - 'CLI Version': 'CLIバージョン', - 'Git Commit': 'Gitコミット', - Model: 'モデル', - 'Fast Model': '高速モデル', - Sandbox: 'サンドボックス', - 'OS Platform': 'OSプラットフォーム', - 'OS Arch': 'OSアーキテクチャ', - 'OS Release': 'OSリリース', - 'Node.js Version': 'Node.js バージョン', - 'NPM Version': 'NPM バージョン', - 'Session ID': 'セッションID', - 'Auth Method': '認証方式', - 'Base URL': 'ベースURL', - 'Memory Usage': 'メモリ使用量', - 'IDE Client': 'IDEクライアント', + "CLI Version": "CLIバージョン", + "Git Commit": "Gitコミット", + Model: "モデル", + "Fast Model": "高速モデル", + Sandbox: "サンドボックス", + "OS Platform": "OSプラットフォーム", + "OS Arch": "OSアーキテクチャ", + "OS Release": "OSリリース", + "Node.js Version": "Node.js バージョン", + "NPM Version": "NPM バージョン", + "Session ID": "セッションID", + "Auth Method": "認証方式", + "Base URL": "ベースURL", + "Memory Usage": "メモリ使用量", + "IDE Client": "IDEクライアント", // ============================================================================ // Commands - General // ============================================================================ - 'Analyzes the project and creates a tailored QWEN.md file.': - 'プロジェクトを分析し、カスタマイズされた QWEN.md ファイルを作成', - 'List available Qwen Code tools. Usage: /tools [desc]': - '利用可能な Qwen Code ツールを一覧表示。使い方: /tools [desc]', - 'List available skills.': '利用可能なスキルを一覧表示する。', - 'Available Qwen Code CLI tools:': '利用可能な Qwen Code CLI ツール:', - 'No tools available': '利用可能なツールはありません', - 'View or change the approval mode for tool usage': - 'ツール使用の承認モードを表示または変更', - 'View or change the language setting': '言語設定を表示または変更', - 'change the theme': 'テーマを変更', - 'Select Theme': 'テーマを選択', - Preview: 'プレビュー', - '(Use Enter to select, Tab to configure scope)': - '(Enter で選択、Tab でスコープを設定)', - '(Use Enter to apply scope, Tab to select theme)': - '(Enter でスコープを適用、Tab でテーマを選択)', - 'Theme configuration unavailable due to NO_COLOR env variable.': - 'NO_COLOR 環境変数のためテーマ設定は利用できません', + "Analyzes the project and creates a tailored QWEN.md file.": + "プロジェクトを分析し、カスタマイズされた QWEN.md ファイルを作成", + "List available Qwen Code tools. Usage: /tools [desc]": + "利用可能な Qwen Code ツールを一覧表示。使い方: /tools [desc]", + "List available skills.": "利用可能なスキルを一覧表示する。", + "Available Qwen Code CLI tools:": "利用可能な Qwen Code CLI ツール:", + "No tools available": "利用可能なツールはありません", + "View or change the approval mode for tool usage": "ツール使用の承認モードを表示または変更", + "View or change the language setting": "言語設定を表示または変更", + "change the theme": "テーマを変更", + "Select Theme": "テーマを選択", + Preview: "プレビュー", + "(Use Enter to select, Tab to configure scope)": "(Enter で選択、Tab でスコープを設定)", + "(Use Enter to apply scope, Tab to select theme)": "(Enter でスコープを適用、Tab でテーマを選択)", + "Theme configuration unavailable due to NO_COLOR env variable.": + "NO_COLOR 環境変数のためテーマ設定は利用できません", 'Theme "{{themeName}}" not found.': 'テーマ "{{themeName}}" が見つかりません', 'Theme "{{themeName}}" not found in selected scope.': '選択したスコープにテーマ "{{themeName}}" が見つかりません', - 'Clear conversation history and free up context': - '会話履歴をクリアしてコンテキストを解放', - 'Compresses the context by replacing it with a summary.': - 'コンテキストを要約に置き換えて圧縮', - 'open full Qwen Code documentation in your browser': - 'ブラウザで Qwen Code のドキュメントを開く', - 'Configuration not available.': '設定が利用できません', - 'change the auth method': '認証方式を変更', - 'Configure authentication information for login': - 'ログイン用の認証情報を設定', - 'Copy the last result or code snippet to clipboard': - '最後の結果またはコードスニペットをクリップボードにコピー', + "Clear conversation history and free up context": "会話履歴をクリアしてコンテキストを解放", + "Compresses the context by replacing it with a summary.": "コンテキストを要約に置き換えて圧縮", + "open full Qwen Code documentation in your browser": "ブラウザで Qwen Code のドキュメントを開く", + "Configuration not available.": "設定が利用できません", + "change the auth method": "認証方式を変更", + "Configure authentication information for login": "ログイン用の認証情報を設定", + "Copy the last result or code snippet to clipboard": + "最後の結果またはコードスニペットをクリップボードにコピー", // ============================================================================ // Commands - Agents // ============================================================================ - 'Manage subagents for specialized task delegation.': - '専門タスクを委任するサブエージェントを管理', - 'Manage existing subagents (view, edit, delete).': - '既存のサブエージェントを管理(表示、編集、削除)', - 'Create a new subagent with guided setup.': - 'ガイド付きセットアップで新しいサブエージェントを作成', + "Manage subagents for specialized task delegation.": "専門タスクを委任するサブエージェントを管理", + "Manage existing subagents (view, edit, delete).": + "既存のサブエージェントを管理(表示、編集、削除)", + "Create a new subagent with guided setup.": + "ガイド付きセットアップで新しいサブエージェントを作成", // ============================================================================ // Agents - Management Dialog // ============================================================================ - Agents: 'エージェント', - 'Choose Action': 'アクションを選択', - 'Edit {{name}}': '{{name}} を編集', - 'Edit Tools: {{name}}': 'ツールを編集: {{name}}', - 'Edit Color: {{name}}': '色を編集: {{name}}', - 'Delete {{name}}': '{{name}} を削除', - 'Unknown Step': '不明なステップ', - 'Esc to close': 'Esc で閉じる', - 'Enter to select, ↑↓ to navigate, Esc to close': - 'Enter で選択、↑↓ で移動、Esc で閉じる', - 'Esc to go back': 'Esc で戻る', - 'Enter to confirm, Esc to cancel': 'Enter で確定、Esc でキャンセル', - 'Enter to select, ↑↓ to navigate, Esc to go back': - 'Enter で選択、↑↓ で移動、Esc で戻る', - 'Enter to submit, Esc to go back': 'Enter で送信、Esc で戻る', - 'Invalid step: {{step}}': '無効なステップ: {{step}}', - 'No subagents found.': 'サブエージェントが見つかりません', + Agents: "エージェント", + "Choose Action": "アクションを選択", + "Edit {{name}}": "{{name}} を編集", + "Edit Tools: {{name}}": "ツールを編集: {{name}}", + "Edit Color: {{name}}": "色を編集: {{name}}", + "Delete {{name}}": "{{name}} を削除", + "Unknown Step": "不明なステップ", + "Esc to close": "Esc で閉じる", + "Enter to select, ↑↓ to navigate, Esc to close": "Enter で選択、↑↓ で移動、Esc で閉じる", + "Esc to go back": "Esc で戻る", + "Enter to confirm, Esc to cancel": "Enter で確定、Esc でキャンセル", + "Enter to select, ↑↓ to navigate, Esc to go back": "Enter で選択、↑↓ で移動、Esc で戻る", + "Enter to submit, Esc to go back": "Enter で送信、Esc で戻る", + "Invalid step: {{step}}": "無効なステップ: {{step}}", + "No subagents found.": "サブエージェントが見つかりません", "Use '/agents create' to create your first subagent.": "'/agents create' で最初のサブエージェントを作成してください", - '(built-in)': '(組み込み)', - '(overridden by project level agent)': - '(プロジェクトレベルのエージェントで上書き)', - 'Project Level ({{path}})': 'プロジェクトレベル ({{path}})', - 'User Level ({{path}})': 'ユーザーレベル ({{path}})', - 'Built-in Agents': '組み込みエージェント', - 'Using: {{count}} agents': '使用中: {{count}} エージェント', - 'View Agent': 'エージェントを表示', - 'Edit Agent': 'エージェントを編集', - 'Delete Agent': 'エージェントを削除', - Back: '戻る', - 'No agent selected': 'エージェントが選択されていません', - 'File Path: ': 'ファイルパス: ', - 'Tools: ': 'ツール: ', - 'Color: ': '色: ', - 'Description:': '説明:', - 'System Prompt:': 'システムプロンプト:', - 'Open in editor': 'エディタで開く', - 'Edit tools': 'ツールを編集', - 'Edit color': '色を編集', - '❌ Error:': '❌ エラー:', + "(built-in)": "(組み込み)", + "(overridden by project level agent)": "(プロジェクトレベルのエージェントで上書き)", + "Project Level ({{path}})": "プロジェクトレベル ({{path}})", + "User Level ({{path}})": "ユーザーレベル ({{path}})", + "Built-in Agents": "組み込みエージェント", + "Using: {{count}} agents": "使用中: {{count}} エージェント", + "View Agent": "エージェントを表示", + "Edit Agent": "エージェントを編集", + "Delete Agent": "エージェントを削除", + Back: "戻る", + "No agent selected": "エージェントが選択されていません", + "File Path: ": "ファイルパス: ", + "Tools: ": "ツール: ", + "Color: ": "色: ", + "Description:": "説明:", + "System Prompt:": "システムプロンプト:", + "Open in editor": "エディタで開く", + "Edit tools": "ツールを編集", + "Edit color": "色を編集", + "❌ Error:": "❌ エラー:", 'Are you sure you want to delete agent "{{name}}"?': 'エージェント "{{name}}" を削除してもよろしいですか?', - 'Project Level (.qwen/agents/)': 'プロジェクトレベル (.qwen/agents/)', - 'User Level (~/.qwen/agents/)': 'ユーザーレベル (~/.qwen/agents/)', - '✅ Subagent Created Successfully!': - '✅ サブエージェントの作成に成功しました!', + "Project Level (.qwen/agents/)": "プロジェクトレベル (.qwen/agents/)", + "User Level (~/.qwen/agents/)": "ユーザーレベル (~/.qwen/agents/)", + "✅ Subagent Created Successfully!": "✅ サブエージェントの作成に成功しました!", 'Subagent "{{name}}" has been saved to {{level}} level.': 'サブエージェント "{{name}}" を {{level}} に保存しました', - 'Name: ': '名前: ', - 'Location: ': '場所: ', - '❌ Error saving subagent:': '❌ サブエージェント保存エラー:', - 'Warnings:': '警告:', - 'Step {{n}}: Choose Location': 'ステップ {{n}}: 場所を選択', - 'Step {{n}}: Choose Generation Method': 'ステップ {{n}}: 作成方法を選択', - 'Generate with Qwen Code (Recommended)': 'Qwen Code で生成(推奨)', - 'Manual Creation': '手動作成', - 'Generating subagent configuration...': 'サブエージェント設定を生成中...', - 'Failed to generate subagent: {{error}}': - 'サブエージェントの生成に失敗: {{error}}', - 'Step {{n}}: Describe Your Subagent': - 'ステップ {{n}}: サブエージェントを説明', - 'Step {{n}}: Enter Subagent Name': 'ステップ {{n}}: サブエージェント名を入力', - 'Step {{n}}: Enter System Prompt': 'ステップ {{n}}: システムプロンプトを入力', - 'Step {{n}}: Enter Description': 'ステップ {{n}}: 説明を入力', - 'Step {{n}}: Select Tools': 'ステップ {{n}}: ツールを選択', - 'All Tools (Default)': '全ツール(デフォルト)', - 'All Tools': '全ツール', - 'Read-only Tools': '読み取り専用ツール', - 'Read & Edit Tools': '読み取り&編集ツール', - 'Read & Edit & Execution Tools': '読み取り&編集&実行ツール', - 'Selected tools:': '選択されたツール:', - 'Step {{n}}: Choose Background Color': 'ステップ {{n}}: 背景色を選択', - 'Step {{n}}: Confirm and Save': 'ステップ {{n}}: 確認して保存', - 'Esc to cancel': 'Esc でキャンセル', - cancel: 'キャンセル', - 'go back': '戻る', - '↑↓ to navigate, ': '↑↓ で移動、', - 'Name cannot be empty.': '名前は空にできません', - 'System prompt cannot be empty.': 'システムプロンプトは空にできません', - 'Description cannot be empty.': '説明は空にできません', - 'Failed to launch editor: {{error}}': 'エディタの起動に失敗: {{error}}', - 'Failed to save and edit subagent: {{error}}': - 'サブエージェントの保存と編集に失敗: {{error}}', + "Name: ": "名前: ", + "Location: ": "場所: ", + "❌ Error saving subagent:": "❌ サブエージェント保存エラー:", + "Warnings:": "警告:", + "Step {{n}}: Choose Location": "ステップ {{n}}: 場所を選択", + "Step {{n}}: Choose Generation Method": "ステップ {{n}}: 作成方法を選択", + "Generate with Qwen Code (Recommended)": "Qwen Code で生成(推奨)", + "Manual Creation": "手動作成", + "Generating subagent configuration...": "サブエージェント設定を生成中...", + "Failed to generate subagent: {{error}}": "サブエージェントの生成に失敗: {{error}}", + "Step {{n}}: Describe Your Subagent": "ステップ {{n}}: サブエージェントを説明", + "Step {{n}}: Enter Subagent Name": "ステップ {{n}}: サブエージェント名を入力", + "Step {{n}}: Enter System Prompt": "ステップ {{n}}: システムプロンプトを入力", + "Step {{n}}: Enter Description": "ステップ {{n}}: 説明を入力", + "Step {{n}}: Select Tools": "ステップ {{n}}: ツールを選択", + "All Tools (Default)": "全ツール(デフォルト)", + "All Tools": "全ツール", + "Read-only Tools": "読み取り専用ツール", + "Read & Edit Tools": "読み取り&編集ツール", + "Read & Edit & Execution Tools": "読み取り&編集&実行ツール", + "Selected tools:": "選択されたツール:", + "Step {{n}}: Choose Background Color": "ステップ {{n}}: 背景色を選択", + "Step {{n}}: Confirm and Save": "ステップ {{n}}: 確認して保存", + "Esc to cancel": "Esc でキャンセル", + cancel: "キャンセル", + "go back": "戻る", + "↑↓ to navigate, ": "↑↓ で移動、", + "Name cannot be empty.": "名前は空にできません", + "System prompt cannot be empty.": "システムプロンプトは空にできません", + "Description cannot be empty.": "説明は空にできません", + "Failed to launch editor: {{error}}": "エディタの起動に失敗: {{error}}", + "Failed to save and edit subagent: {{error}}": "サブエージェントの保存と編集に失敗: {{error}}", 'Name "{{name}}" already exists at {{level}} level - will overwrite existing subagent': '"{{name}}" は {{level}} に既に存在します - 既存のサブエージェントを上書きします', 'Name "{{name}}" exists at user level - project level will take precedence': '"{{name}}" はユーザーレベルに存在します - プロジェクトレベルが優先されます', 'Name "{{name}}" exists at project level - existing subagent will take precedence': '"{{name}}" はプロジェクトレベルに存在します - 既存のサブエージェントが優先されます', - 'Description is over {{length}} characters': - '説明が {{length}} 文字を超えています', - 'System prompt is over {{length}} characters': - 'システムプロンプトが {{length}} 文字を超えています', - 'Describe what this subagent should do and when it should be used. (Be comprehensive for best results)': - 'このサブエージェントの役割と使用タイミングを説明してください(詳細に記述するほど良い結果が得られます)', - 'e.g., Expert code reviewer that reviews code based on best practices...': - '例: ベストプラクティスに基づいてコードをレビューするエキスパートレビュアー...', - 'All tools selected, including MCP tools': - 'MCPツールを含むすべてのツールを選択', - 'Read-only tools:': '読み取り専用ツール:', - 'Edit tools:': '編集ツール:', - 'Execution tools:': '実行ツール:', - 'Press Enter to save, e to save and edit, Esc to go back': - 'Enter で保存、e で保存して編集、Esc で戻る', - 'Press Enter to continue, {{navigation}}Esc to {{action}}': - 'Enter で続行、{{navigation}}Esc で{{action}}', - 'Enter a clear, unique name for this subagent.': - 'このサブエージェントの明確で一意な名前を入力してください', - 'e.g., Code Reviewer': '例: コードレビュアー', + "Description is over {{length}} characters": "説明が {{length}} 文字を超えています", + "System prompt is over {{length}} characters": + "システムプロンプトが {{length}} 文字を超えています", + "Describe what this subagent should do and when it should be used. (Be comprehensive for best results)": + "このサブエージェントの役割と使用タイミングを説明してください(詳細に記述するほど良い結果が得られます)", + "e.g., Expert code reviewer that reviews code based on best practices...": + "例: ベストプラクティスに基づいてコードをレビューするエキスパートレビュアー...", + "All tools selected, including MCP tools": "MCPツールを含むすべてのツールを選択", + "Read-only tools:": "読み取り専用ツール:", + "Edit tools:": "編集ツール:", + "Execution tools:": "実行ツール:", + "Press Enter to save, e to save and edit, Esc to go back": + "Enter で保存、e で保存して編集、Esc で戻る", + "Press Enter to continue, {{navigation}}Esc to {{action}}": + "Enter で続行、{{navigation}}Esc で{{action}}", + "Enter a clear, unique name for this subagent.": + "このサブエージェントの明確で一意な名前を入力してください", + "e.g., Code Reviewer": "例: コードレビュアー", "Write the system prompt that defines this subagent's behavior. Be comprehensive for best results.": - 'このサブエージェントの動作を定義するシステムプロンプトを記述してください (詳細に書くほど良い結果が得られます)', - 'e.g., You are an expert code reviewer...': - '例: あなたはエキスパートコードレビュアーです...', - 'Describe when and how this subagent should be used.': - 'このサブエージェントをいつどのように使用するかを説明してください', - 'e.g., Reviews code for best practices and potential bugs.': - '例: ベストプラクティスと潜在的なバグについてコードをレビューします。', + "このサブエージェントの動作を定義するシステムプロンプトを記述してください (詳細に書くほど良い結果が得られます)", + "e.g., You are an expert code reviewer...": "例: あなたはエキスパートコードレビュアーです...", + "Describe when and how this subagent should be used.": + "このサブエージェントをいつどのように使用するかを説明してください", + "e.g., Reviews code for best practices and potential bugs.": + "例: ベストプラクティスと潜在的なバグについてコードをレビューします。", // Commands - General (continued) - '(Use Enter to select{{tabText}})': '(Enter で選択{{tabText}})', - ', Tab to change focus': '、Tab でフォーカス変更', - 'To see changes, Qwen Code must be restarted. Press r to exit and apply changes now.': - '変更を確認するには Qwen Code を再起動する必要があります。 r を押して終了し、変更を適用してください', + "(Use Enter to select{{tabText}})": "(Enter で選択{{tabText}})", + ", Tab to change focus": "、Tab でフォーカス変更", + "To see changes, Qwen Code must be restarted. Press r to exit and apply changes now.": + "変更を確認するには Qwen Code を再起動する必要があります。 r を押して終了し、変更を適用してください", 'The command "/{{command}}" is not supported in non-interactive mode.': 'コマンド "/{{command}}" は非対話モードではサポートされていません', - 'View and edit Qwen Code settings': 'Qwen Code の設定を表示・編集', - Settings: '設定', - 'Vim Mode': 'Vim モード', - 'Disable Auto Update': '自動更新を無効化', - Language: '言語', - 'Output Format': '出力形式', - 'Hide Tips': 'ヒントを非表示', - 'Hide Banner': 'バナーを非表示', - 'Show Memory Usage': 'メモリ使用量を表示', - 'Show Line Numbers': '行番号を表示', - Text: 'テキスト', - JSON: 'JSON', - Plan: 'プラン', - Default: 'デフォルト', - 'Auto Edit': '自動編集', - YOLO: 'YOLO', - 'toggle vim mode on/off': 'Vim モードのオン/オフを切り替え', - 'exit the cli': 'CLIを終了', - Timeout: 'タイムアウト', - 'Max Retries': '最大リトライ回数', - 'Auto Accept': '自動承認', - 'Folder Trust': 'フォルダの信頼', - 'Enable Prompt Completion': 'プロンプト補完を有効化', - 'Debug Keystroke Logging': 'キーストロークのデバッグログ', - 'Hide Window Title': 'ウィンドウタイトルを非表示', - 'Show Status in Title': 'タイトルにステータスを表示', - 'Hide Context Summary': 'コンテキスト要約を非表示', - 'Hide CWD': '作業ディレクトリを非表示', - 'Hide Sandbox Status': 'サンドボックス状態を非表示', - 'Hide Model Info': 'モデル情報を非表示', - 'Hide Footer': 'フッターを非表示', - 'Show Citations': '引用を表示', - 'Custom Witty Phrases': 'カスタムウィットフレーズ', - 'Enable Welcome Back': 'ウェルカムバック機能を有効化', - 'Disable Loading Phrases': 'ローディングフレーズを無効化', - 'Screen Reader Mode': 'スクリーンリーダーモード', - 'IDE Mode': 'IDEモード', - 'Max Session Turns': '最大セッションターン数', - 'Skip Next Speaker Check': '次の発言者チェックをスキップ', - 'Skip Loop Detection': 'ループ検出をスキップ', - 'Skip Startup Context': '起動時コンテキストをスキップ', - 'Enable OpenAI Logging': 'OpenAI ログを有効化', - 'OpenAI Logging Directory': 'OpenAI ログディレクトリ', - 'Disable Cache Control': 'キャッシュ制御を無効化', - 'Memory Discovery Max Dirs': 'メモリ検出の最大ディレクトリ数', - 'Load Memory From Include Directories': - 'インクルードディレクトリからメモリを読み込み', - 'Respect .gitignore': '.gitignore を優先', - 'Respect .qwenignore': '.qwenignore を優先', - 'Enable Recursive File Search': '再帰的ファイル検索を有効化', - 'Disable Fuzzy Search': 'ファジー検索を無効化', - 'Enable Interactive Shell': '対話型シェルを有効化', - 'Show Color': '色を表示', - 'Use Ripgrep': 'Ripgrep を使用', - 'Use Builtin Ripgrep': '組み込み Ripgrep を使用', - 'Enable Tool Output Truncation': 'ツール出力の切り詰めを有効化', - 'Tool Output Truncation Threshold': 'ツール出力切り詰めのしきい値', - 'Tool Output Truncation Lines': 'ツール出力の切り詰め行数', - 'Vision Model Preview': 'ビジョンモデルプレビュー', - 'Tool Schema Compliance': 'ツールスキーマ準拠', - 'Auto (detect from system)': '自動(システムから検出)', - 'check session stats. Usage: /stats [model|tools]': - 'セッション統計を確認。使い方: /stats [model|tools]', - 'Show model-specific usage statistics.': 'モデル別の使用統計を表示', - 'Show tool-specific usage statistics.': 'ツール別の使用統計を表示', - 'Open MCP management dialog, or authenticate with OAuth-enabled servers': - 'MCP管理ダイアログを開く、またはOAuth対応サーバーで認証', - 'List configured MCP servers and tools, or authenticate with OAuth-enabled servers': - '設定済みのMCPサーバーとツールを一覧表示、またはOAuth対応サーバーで認証', - 'Manage workspace directories': 'ワークスペースディレクトリを管理', - 'Add directories to the workspace. Use comma to separate multiple paths': - 'ワークスペースにディレクトリを追加。複数パスはカンマで区切ってください', - 'Show all directories in the workspace': - 'ワークスペース内のすべてのディレクトリを表示', - 'set external editor preference': '外部エディタの設定', - 'Manage extensions': '拡張機能を管理', - 'Manage installed extensions': 'インストール済みの拡張機能を管理する', - 'List active extensions': '有効な拡張機能を一覧表示', - 'Update extensions. Usage: update |--all': - '拡張機能を更新。使い方: update <拡張機能名>|--all', - 'You are installing an extension from {{originSource}}. Some features may not work perfectly with Qwen Code.': - '{{originSource}} から拡張機能をインストールしています。一部の機能は Qwen Code で完全に動作しない可能性があります。', - 'manage IDE integration': 'IDE連携を管理', - 'check status of IDE integration': 'IDE連携の状態を確認', - 'install required IDE companion for {{ideName}}': - '{{ideName}} 用の必要なIDEコンパニオンをインストール', - 'enable IDE integration': 'IDE連携を有効化', - 'disable IDE integration': 'IDE連携を無効化', - '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.': - '現在の環境ではIDE連携はサポートされていません。この機能を使用するには、VS Code または VS Code 派生エディタで Qwen Code を実行してください', - 'Set up GitHub Actions': 'GitHub Actions を設定', - 'Configure terminal keybindings for multiline input (VS Code, Cursor, Windsurf, Trae)': - '複数行入力用のターミナルキーバインドを設定(VS Code、Cursor、Windsurf、Trae)', - 'Please restart your terminal for the changes to take effect.': - '変更を有効にするにはターミナルを再起動してください', - 'Failed to configure terminal: {{error}}': - 'ターミナルの設定に失敗: {{error}}', - 'Could not determine {{terminalName}} config path on Windows: APPDATA environment variable is not set.': - 'Windows で {{terminalName}} の設定パスを特定できません: APPDATA 環境変数が設定されていません', - '{{terminalName}} keybindings.json exists but is not a valid JSON array. Please fix the file manually or delete it to allow automatic configuration.': - '{{terminalName}} の keybindings.json は存在しますが、有効なJSON配列ではありません。ファイルを手動で修正するか、削除して自動設定を許可してください', - 'File: {{file}}': 'ファイル: {{file}}', - 'Failed to parse {{terminalName}} keybindings.json. The file contains invalid JSON. Please fix the file manually or delete it to allow automatic configuration.': - '{{terminalName}} の keybindings.json の解析に失敗しました。ファイルに無効なJSONが含まれています。手動で修正するか、削除して自動設定を許可してください', - 'Error: {{error}}': 'エラー: {{error}}', - 'Shift+Enter binding already exists': 'Shift+Enter バインドは既に存在します', - 'Ctrl+Enter binding already exists': 'Ctrl+Enter バインドは既に存在します', - 'Existing keybindings detected. Will not modify to avoid conflicts.': - '既存のキーバインドが検出されました。競合を避けるため変更をしません', - 'Please check and modify manually if needed: {{file}}': - '必要に応じて手動で確認・変更してください: {{file}}', - 'Added Shift+Enter and Ctrl+Enter keybindings to {{terminalName}}.': - '{{terminalName}} に Shift+Enter と Ctrl+Enter のキーバインドを追加しました', - 'Modified: {{file}}': '変更済み: {{file}}', - '{{terminalName}} keybindings already configured.': - '{{terminalName}} のキーバインドは既に設定されています', - 'Failed to configure {{terminalName}}.': - '{{terminalName}} の設定に失敗しました', - 'Your terminal is already configured for an optimal experience with multiline input (Shift+Enter and Ctrl+Enter).': - 'ターミナルは複数行入力(Shift+Enter と Ctrl+Enter)に最適化されています', + "View and edit Qwen Code settings": "Qwen Code の設定を表示・編集", + Settings: "設定", + "Vim Mode": "Vim モード", + "Disable Auto Update": "自動更新を無効化", + Language: "言語", + "Output Format": "出力形式", + "Hide Tips": "ヒントを非表示", + "Hide Banner": "バナーを非表示", + "Show Memory Usage": "メモリ使用量を表示", + "Show Line Numbers": "行番号を表示", + Text: "テキスト", + JSON: "JSON", + Plan: "プラン", + Default: "デフォルト", + "Auto Edit": "自動編集", + YOLO: "YOLO", + "toggle vim mode on/off": "Vim モードのオン/オフを切り替え", + "exit the cli": "CLIを終了", + Timeout: "タイムアウト", + "Max Retries": "最大リトライ回数", + "Auto Accept": "自動承認", + "Folder Trust": "フォルダの信頼", + "Enable Prompt Completion": "プロンプト補完を有効化", + "Debug Keystroke Logging": "キーストロークのデバッグログ", + "Hide Window Title": "ウィンドウタイトルを非表示", + "Show Status in Title": "タイトルにステータスを表示", + "Hide Context Summary": "コンテキスト要約を非表示", + "Hide CWD": "作業ディレクトリを非表示", + "Hide Sandbox Status": "サンドボックス状態を非表示", + "Hide Model Info": "モデル情報を非表示", + "Hide Footer": "フッターを非表示", + "Show Citations": "引用を表示", + "Custom Witty Phrases": "カスタムウィットフレーズ", + "Enable Welcome Back": "ウェルカムバック機能を有効化", + "Disable Loading Phrases": "ローディングフレーズを無効化", + "Screen Reader Mode": "スクリーンリーダーモード", + "IDE Mode": "IDEモード", + "Max Session Turns": "最大セッションターン数", + "Skip Next Speaker Check": "次の発言者チェックをスキップ", + "Skip Loop Detection": "ループ検出をスキップ", + "Skip Startup Context": "起動時コンテキストをスキップ", + "Enable OpenAI Logging": "OpenAI ログを有効化", + "OpenAI Logging Directory": "OpenAI ログディレクトリ", + "Disable Cache Control": "キャッシュ制御を無効化", + "Memory Discovery Max Dirs": "メモリ検出の最大ディレクトリ数", + "Load Memory From Include Directories": "インクルードディレクトリからメモリを読み込み", + "Respect .gitignore": ".gitignore を優先", + "Respect .qwenignore": ".qwenignore を優先", + "Enable Recursive File Search": "再帰的ファイル検索を有効化", + "Disable Fuzzy Search": "ファジー検索を無効化", + "Enable Interactive Shell": "対話型シェルを有効化", + "Show Color": "色を表示", + "Use Ripgrep": "Ripgrep を使用", + "Use Builtin Ripgrep": "組み込み Ripgrep を使用", + "Enable Tool Output Truncation": "ツール出力の切り詰めを有効化", + "Tool Output Truncation Threshold": "ツール出力切り詰めのしきい値", + "Tool Output Truncation Lines": "ツール出力の切り詰め行数", + "Vision Model Preview": "ビジョンモデルプレビュー", + "Tool Schema Compliance": "ツールスキーマ準拠", + "Auto (detect from system)": "自動(システムから検出)", + "check session stats. Usage: /stats [model|tools]": + "セッション統計を確認。使い方: /stats [model|tools]", + "Show model-specific usage statistics.": "モデル別の使用統計を表示", + "Show tool-specific usage statistics.": "ツール別の使用統計を表示", + "Open MCP management dialog, or authenticate with OAuth-enabled servers": + "MCP管理ダイアログを開く、またはOAuth対応サーバーで認証", + "List configured MCP servers and tools, or authenticate with OAuth-enabled servers": + "設定済みのMCPサーバーとツールを一覧表示、またはOAuth対応サーバーで認証", + "Manage workspace directories": "ワークスペースディレクトリを管理", + "Add directories to the workspace. Use comma to separate multiple paths": + "ワークスペースにディレクトリを追加。複数パスはカンマで区切ってください", + "Show all directories in the workspace": "ワークスペース内のすべてのディレクトリを表示", + "set external editor preference": "外部エディタの設定", + "Manage extensions": "拡張機能を管理", + "Manage installed extensions": "インストール済みの拡張機能を管理する", + "List active extensions": "有効な拡張機能を一覧表示", + "Update extensions. Usage: update |--all": + "拡張機能を更新。使い方: update <拡張機能名>|--all", + "You are installing an extension from {{originSource}}. Some features may not work perfectly with Qwen Code.": + "{{originSource}} から拡張機能をインストールしています。一部の機能は Qwen Code で完全に動作しない可能性があります。", + "manage IDE integration": "IDE連携を管理", + "check status of IDE integration": "IDE連携の状態を確認", + "install required IDE companion for {{ideName}}": + "{{ideName}} 用の必要なIDEコンパニオンをインストール", + "enable IDE integration": "IDE連携を有効化", + "disable IDE integration": "IDE連携を無効化", + "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.": + "現在の環境ではIDE連携はサポートされていません。この機能を使用するには、VS Code または VS Code 派生エディタで Qwen Code を実行してください", + "Set up GitHub Actions": "GitHub Actions を設定", + "Configure terminal keybindings for multiline input (VS Code, Cursor, Windsurf, Trae)": + "複数行入力用のターミナルキーバインドを設定(VS Code、Cursor、Windsurf、Trae)", + "Please restart your terminal for the changes to take effect.": + "変更を有効にするにはターミナルを再起動してください", + "Failed to configure terminal: {{error}}": "ターミナルの設定に失敗: {{error}}", + "Could not determine {{terminalName}} config path on Windows: APPDATA environment variable is not set.": + "Windows で {{terminalName}} の設定パスを特定できません: APPDATA 環境変数が設定されていません", + "{{terminalName}} keybindings.json exists but is not a valid JSON array. Please fix the file manually or delete it to allow automatic configuration.": + "{{terminalName}} の keybindings.json は存在しますが、有効なJSON配列ではありません。ファイルを手動で修正するか、削除して自動設定を許可してください", + "File: {{file}}": "ファイル: {{file}}", + "Failed to parse {{terminalName}} keybindings.json. The file contains invalid JSON. Please fix the file manually or delete it to allow automatic configuration.": + "{{terminalName}} の keybindings.json の解析に失敗しました。ファイルに無効なJSONが含まれています。手動で修正するか、削除して自動設定を許可してください", + "Error: {{error}}": "エラー: {{error}}", + "Shift+Enter binding already exists": "Shift+Enter バインドは既に存在します", + "Ctrl+Enter binding already exists": "Ctrl+Enter バインドは既に存在します", + "Existing keybindings detected. Will not modify to avoid conflicts.": + "既存のキーバインドが検出されました。競合を避けるため変更をしません", + "Please check and modify manually if needed: {{file}}": + "必要に応じて手動で確認・変更してください: {{file}}", + "Added Shift+Enter and Ctrl+Enter keybindings to {{terminalName}}.": + "{{terminalName}} に Shift+Enter と Ctrl+Enter のキーバインドを追加しました", + "Modified: {{file}}": "変更済み: {{file}}", + "{{terminalName}} keybindings already configured.": + "{{terminalName}} のキーバインドは既に設定されています", + "Failed to configure {{terminalName}}.": "{{terminalName}} の設定に失敗しました", + "Your terminal is already configured for an optimal experience with multiline input (Shift+Enter and Ctrl+Enter).": + "ターミナルは複数行入力(Shift+Enter と Ctrl+Enter)に最適化されています", // ============================================================================ // Commands - Hooks // ============================================================================ - 'Manage Qwen Code hooks': 'Qwen Code のフックを管理する', - 'List all configured hooks': '設定済みのフックをすべて表示する', - 'Enable a disabled hook': '無効なフックを有効にする', - 'Disable an active hook': '有効なフックを無効にする', + "Manage Qwen Code hooks": "Qwen Code のフックを管理する", + "List all configured hooks": "設定済みのフックをすべて表示する", + "Enable a disabled hook": "無効なフックを有効にする", + "Disable an active hook": "有効なフックを無効にする", // Hooks - Dialog - Hooks: 'フック', - 'Loading hooks...': 'フックを読み込んでいます...', - 'Error loading hooks:': 'フックの読み込みエラー:', - 'Press Escape to close': 'Escape キーで閉じる', - 'Press Escape, Ctrl+C, or Ctrl+D to cancel': - 'Escape、Ctrl+C、Ctrl+D でキャンセル', - 'Press Space, Enter, or Escape to dismiss': 'Space、Enter、Escape で閉じる', - 'No hook selected': 'フックが選択されていません', + Hooks: "フック", + "Loading hooks...": "フックを読み込んでいます...", + "Error loading hooks:": "フックの読み込みエラー:", + "Press Escape to close": "Escape キーで閉じる", + "Press Escape, Ctrl+C, or Ctrl+D to cancel": "Escape、Ctrl+C、Ctrl+D でキャンセル", + "Press Space, Enter, or Escape to dismiss": "Space、Enter、Escape で閉じる", + "No hook selected": "フックが選択されていません", // Hooks - List Step - 'No hook events found.': 'フックイベントが見つかりません。', - '{{count}} hook configured': '{{count}} 件のフックが設定されています', - '{{count}} hooks configured': '{{count}} 件のフックが設定されています', - 'This menu is read-only. To add or modify hooks, edit settings.json directly or ask Qwen Code.': - 'このメニューは読み取り専用です。フックを追加または変更するには、settings.json を直接編集するか、Qwen Code に尋ねてください。', - 'Enter to select · Esc to cancel': 'Enter で選択 · Esc でキャンセル', + "No hook events found.": "フックイベントが見つかりません。", + "{{count}} hook configured": "{{count}} 件のフックが設定されています", + "{{count}} hooks configured": "{{count}} 件のフックが設定されています", + "This menu is read-only. To add or modify hooks, edit settings.json directly or ask Qwen Code.": + "このメニューは読み取り専用です。フックを追加または変更するには、settings.json を直接編集するか、Qwen Code に尋ねてください。", + "Enter to select · Esc to cancel": "Enter で選択 · Esc でキャンセル", // Hooks - Detail Step - 'Exit codes:': '終了コード:', - 'Configured hooks:': '設定済みのフック:', - 'No hooks configured for this event.': - 'このイベントにはフックが設定されていません。', - 'To add hooks, edit settings.json directly or ask Qwen.': - 'フックを追加するには、settings.json を直接編集するか、Qwen に尋ねてください。', - 'Enter to select · Esc to go back': 'Enter で選択 · Esc で戻る', + "Exit codes:": "終了コード:", + "Configured hooks:": "設定済みのフック:", + "No hooks configured for this event.": "このイベントにはフックが設定されていません。", + "To add hooks, edit settings.json directly or ask Qwen.": + "フックを追加するには、settings.json を直接編集するか、Qwen に尋ねてください。", + "Enter to select · Esc to go back": "Enter で選択 · Esc で戻る", // Hooks - Config Detail Step - 'Hook details': 'フック詳細', - 'Event:': 'イベント:', - 'Extension:': '拡張機能:', - 'Desc:': '説明:', - 'No hook config selected': 'フック設定が選択されていません', - 'To modify or remove this hook, edit settings.json directly or ask Qwen to help.': - 'このフックを変更または削除するには、settings.json を直接編集するか、Qwen に尋ねてください。', + "Hook details": "フック詳細", + "Event:": "イベント:", + "Extension:": "拡張機能:", + "Desc:": "説明:", + "No hook config selected": "フック設定が選択されていません", + "To modify or remove this hook, edit settings.json directly or ask Qwen to help.": + "このフックを変更または削除するには、settings.json を直接編集するか、Qwen に尋ねてください。", // Hooks - Disabled Step - 'Hook Configuration - Disabled': 'フック設定 - 無効', - 'All hooks are currently disabled. You have {{count}} that are not running.': - 'すべてのフックは現在無効です。{{count}} が実行されていません。', - '{{count}} configured hook': '{{count}} 個の設定されたフック', - '{{count}} configured hooks': '{{count}} 個の設定されたフック', - 'When hooks are disabled:': 'フックが無効な場合:', - 'No hook commands will execute': 'フックコマンドは実行されません', - 'StatusLine will not be displayed': 'StatusLine は表示されません', - 'Tool operations will proceed without hook validation': - 'ツール操作はフック検証なしで続行されます', + "Hook Configuration - Disabled": "フック設定 - 無効", + "All hooks are currently disabled. You have {{count}} that are not running.": + "すべてのフックは現在無効です。{{count}} が実行されていません。", + "{{count}} configured hook": "{{count}} 個の設定されたフック", + "{{count}} configured hooks": "{{count}} 個の設定されたフック", + "When hooks are disabled:": "フックが無効な場合:", + "No hook commands will execute": "フックコマンドは実行されません", + "StatusLine will not be displayed": "StatusLine は表示されません", + "Tool operations will proceed without hook validation": + "ツール操作はフック検証なしで続行されます", 'To re-enable hooks, remove "disableAllHooks" from settings.json or ask Qwen Code.': 'フックを再有効化するには、settings.json から "disableAllHooks" を削除するか、Qwen Code に尋ねてください。', // Hooks - Source - Project: 'プロジェクト', - User: 'ユーザー', - System: 'システム', - Extension: '拡張機能', - 'Local Settings': 'ローカル設定', - 'User Settings': 'ユーザー設定', - 'System Settings': 'システム設定', - Extensions: '拡張機能', + Project: "プロジェクト", + User: "ユーザー", + System: "システム", + Extension: "拡張機能", + "Local Settings": "ローカル設定", + "User Settings": "ユーザー設定", + "System Settings": "システム設定", + Extensions: "拡張機能", // Hooks - Status - '✓ Enabled': '✓ 有効', - '✗ Disabled': '✗ 無効', + "✓ Enabled": "✓ 有効", + "✗ Disabled": "✗ 無効", // Hooks - Event Descriptions (short) - 'Before tool execution': 'ツール実行前', - 'After tool execution': 'ツール実行後', - 'After tool execution fails': 'ツール実行失敗時', - 'When notifications are sent': '通知送信時', - 'When the user submits a prompt': 'ユーザーがプロンプトを送信した時', - 'When a new session is started': '新しいセッションが開始された時', - 'Right before Qwen Code concludes its response': - 'Qwen Code が応答を終了する直前', - 'When a subagent (Agent tool call) is started': - 'サブエージェント(Agent ツール呼び出し)が開始された時', - 'Right before a subagent concludes its response': - 'サブエージェントが応答を終了する直前', - 'Before conversation compaction': '会話圧縮前', - 'When a session is ending': 'セッション終了時', - 'When a permission dialog is displayed': '権限ダイアログ表示時', + "Before tool execution": "ツール実行前", + "After tool execution": "ツール実行後", + "After tool execution fails": "ツール実行失敗時", + "When notifications are sent": "通知送信時", + "When the user submits a prompt": "ユーザーがプロンプトを送信した時", + "When a new session is started": "新しいセッションが開始された時", + "Right before Qwen Code concludes its response": "Qwen Code が応答を終了する直前", + "When a subagent (Agent tool call) is started": + "サブエージェント(Agent ツール呼び出し)が開始された時", + "Right before a subagent concludes its response": "サブエージェントが応答を終了する直前", + "Before conversation compaction": "会話圧縮前", + "When a session is ending": "セッション終了時", + "When a permission dialog is displayed": "権限ダイアログ表示時", // Hooks - Event Descriptions (detailed) - 'Input to command is JSON of tool call arguments.': - 'コマンドへの入力はツール呼び出し引数の JSON です。', + "Input to command is JSON of tool call arguments.": + "コマンドへの入力はツール呼び出し引数の JSON です。", 'Input to command is JSON with fields "inputs" (tool call arguments) and "response" (tool call response).': 'コマンドへの入力は "inputs"(ツール呼び出し引数)と "response"(ツール呼び出し応答)フィールドを持つ JSON です。', - 'Input to command is JSON with tool_name, tool_input, tool_use_id, error, error_type, is_interrupt, and is_timeout.': - 'コマンドへの入力は tool_name、tool_input、tool_use_id、error、error_type、is_interrupt、is_timeout を持つ JSON です。', - 'Input to command is JSON with notification message and type.': - 'コマンドへの入力は通知メッセージとタイプを持つ JSON です。', - 'Input to command is JSON with original user prompt text.': - 'コマンドへの入力は元のユーザープロンプトテキストを持つ JSON です。', - 'Input to command is JSON with session start source.': - 'コマンドへの入力はセッション開始ソースを持つ JSON です。', - 'Input to command is JSON with session end reason.': - 'コマンドへの入力はセッション終了理由を持つ JSON です。', - 'Input to command is JSON with agent_id and agent_type.': - 'コマンドへの入力は agent_id と agent_type を持つ JSON です。', - 'Input to command is JSON with agent_id, agent_type, and agent_transcript_path.': - 'コマンドへの入力は agent_id、agent_type、agent_transcript_path を持つ JSON です。', - 'Input to command is JSON with compaction details.': - 'コマンドへの入力は圧縮詳細を持つ JSON です。', - 'Input to command is JSON with tool_name, tool_input, and tool_use_id. Output JSON with hookSpecificOutput containing decision to allow or deny.': - 'コマンドへの入力は tool_name、tool_input、tool_use_id を持つ JSON です。許可または拒否の決定を含む hookSpecificOutput を持つ JSON を出力します。', + "Input to command is JSON with tool_name, tool_input, tool_use_id, error, error_type, is_interrupt, and is_timeout.": + "コマンドへの入力は tool_name、tool_input、tool_use_id、error、error_type、is_interrupt、is_timeout を持つ JSON です。", + "Input to command is JSON with notification message and type.": + "コマンドへの入力は通知メッセージとタイプを持つ JSON です。", + "Input to command is JSON with original user prompt text.": + "コマンドへの入力は元のユーザープロンプトテキストを持つ JSON です。", + "Input to command is JSON with session start source.": + "コマンドへの入力はセッション開始ソースを持つ JSON です。", + "Input to command is JSON with session end reason.": + "コマンドへの入力はセッション終了理由を持つ JSON です。", + "Input to command is JSON with agent_id and agent_type.": + "コマンドへの入力は agent_id と agent_type を持つ JSON です。", + "Input to command is JSON with agent_id, agent_type, and agent_transcript_path.": + "コマンドへの入力は agent_id、agent_type、agent_transcript_path を持つ JSON です。", + "Input to command is JSON with compaction details.": + "コマンドへの入力は圧縮詳細を持つ JSON です。", + "Input to command is JSON with tool_name, tool_input, and tool_use_id. Output JSON with hookSpecificOutput containing decision to allow or deny.": + "コマンドへの入力は tool_name、tool_input、tool_use_id を持つ JSON です。許可または拒否の決定を含む hookSpecificOutput を持つ JSON を出力します。", // Hooks - Exit Code Descriptions - 'stdout/stderr not shown': 'stdout/stderr は表示されません', - 'show stderr to model and continue conversation': - 'stderr をモデルに表示し、会話を続ける', - 'show stderr to user only': 'stderr をユーザーのみに表示', - 'stdout shown in transcript mode (ctrl+o)': - 'stdout はトランスクリプトモードで表示 (ctrl+o)', - 'show stderr to model immediately': 'stderr をモデルに即座に表示', - 'show stderr to user only but continue with tool call': - 'stderr をユーザーのみに表示し、ツール呼び出しを続ける', - 'block processing, erase original prompt, and show stderr to user only': - '処理をブロックし、元のプロンプトを消去し、stderr をユーザーのみに表示', - 'stdout shown to Qwen': 'stdout をモデルに表示', - 'show stderr to user only (blocking errors ignored)': - 'stderr をユーザーのみに表示(ブロッキングエラーは無視)', - 'command completes successfully': 'コマンドが正常に完了', - 'stdout shown to subagent': 'stdout をサブエージェントに表示', - 'show stderr to subagent and continue having it run': - 'stderr をサブエージェントに表示し、実行を続ける', - 'stdout appended as custom compact instructions': - 'stdout をカスタム圧縮指示として追加', - 'block compaction': '圧縮をブロック', - 'show stderr to user only but continue with compaction': - 'stderr をユーザーのみに表示し、圧縮を続ける', - 'use hook decision if provided': '提供されている場合はフックの決定を使用', + "stdout/stderr not shown": "stdout/stderr は表示されません", + "show stderr to model and continue conversation": "stderr をモデルに表示し、会話を続ける", + "show stderr to user only": "stderr をユーザーのみに表示", + "stdout shown in transcript mode (ctrl+o)": "stdout はトランスクリプトモードで表示 (ctrl+o)", + "show stderr to model immediately": "stderr をモデルに即座に表示", + "show stderr to user only but continue with tool call": + "stderr をユーザーのみに表示し、ツール呼び出しを続ける", + "block processing, erase original prompt, and show stderr to user only": + "処理をブロックし、元のプロンプトを消去し、stderr をユーザーのみに表示", + "stdout shown to Qwen": "stdout をモデルに表示", + "show stderr to user only (blocking errors ignored)": + "stderr をユーザーのみに表示(ブロッキングエラーは無視)", + "command completes successfully": "コマンドが正常に完了", + "stdout shown to subagent": "stdout をサブエージェントに表示", + "show stderr to subagent and continue having it run": + "stderr をサブエージェントに表示し、実行を続ける", + "stdout appended as custom compact instructions": "stdout をカスタム圧縮指示として追加", + "block compaction": "圧縮をブロック", + "show stderr to user only but continue with compaction": + "stderr をユーザーのみに表示し、圧縮を続ける", + "use hook decision if provided": "提供されている場合はフックの決定を使用", // Hooks - Messages - 'Config not loaded.': '設定が読み込まれていません。', - 'Hooks are not enabled. Enable hooks in settings to use this feature.': - 'フックが有効になっていません。この機能を使用するには設定でフックを有効にしてください。', - 'No hooks configured. Add hooks in your settings.json file.': - 'フックが設定されていません。settings.json ファイルにフックを追加してください。', - 'Configured Hooks ({{count}} total)': '設定済みのフック(合計 {{count}} 件)', + "Config not loaded.": "設定が読み込まれていません。", + "Hooks are not enabled. Enable hooks in settings to use this feature.": + "フックが有効になっていません。この機能を使用するには設定でフックを有効にしてください。", + "No hooks configured. Add hooks in your settings.json file.": + "フックが設定されていません。settings.json ファイルにフックを追加してください。", + "Configured Hooks ({{count}} total)": "設定済みのフック(合計 {{count}} 件)", // ============================================================================ // Commands - Session Export // ============================================================================ - 'Export current session message history to a file': - '現在のセッションのメッセージ履歴をファイルにエクスポートする', - 'Export session to HTML format': 'セッションを HTML 形式でエクスポートする', - 'Export session to JSON format': 'セッションを JSON 形式でエクスポートする', - 'Export session to JSONL format (one message per line)': - 'セッションを JSONL 形式でエクスポートする(1 行に 1 メッセージ)', - 'Export session to markdown format': - 'セッションを Markdown 形式でエクスポートする', + "Export current session message history to a file": + "現在のセッションのメッセージ履歴をファイルにエクスポートする", + "Export session to HTML format": "セッションを HTML 形式でエクスポートする", + "Export session to JSON format": "セッションを JSON 形式でエクスポートする", + "Export session to JSONL format (one message per line)": + "セッションを JSONL 形式でエクスポートする(1 行に 1 メッセージ)", + "Export session to markdown format": "セッションを Markdown 形式でエクスポートする", // ============================================================================ // Commands - Insights // ============================================================================ - 'generate personalized programming insights from your chat history': - 'チャット履歴からパーソナライズされたプログラミングインサイトを生成する', + "generate personalized programming insights from your chat history": + "チャット履歴からパーソナライズされたプログラミングインサイトを生成する", // ============================================================================ // Commands - Session History // ============================================================================ - 'Resume a previous session': '前のセッションを再開する', - 'Restore a tool call. This will reset the conversation and file history to the state it was in when the tool call was suggested': - 'ツール呼び出しを復元します。これにより、会話とファイルの履歴はそのツール呼び出しが提案された時点の状態に戻ります', - 'Could not detect terminal type. Supported terminals: VS Code, Cursor, Windsurf, and Trae.': - 'ターミナルの種類を検出できませんでした。サポートされているターミナル: VS Code、Cursor、Windsurf、Trae', + "Resume a previous session": "前のセッションを再開する", + "Restore a tool call. This will reset the conversation and file history to the state it was in when the tool call was suggested": + "ツール呼び出しを復元します。これにより、会話とファイルの履歴はそのツール呼び出しが提案された時点の状態に戻ります", + "Could not detect terminal type. Supported terminals: VS Code, Cursor, Windsurf, and Trae.": + "ターミナルの種類を検出できませんでした。サポートされているターミナル: VS Code、Cursor、Windsurf、Trae", 'Terminal "{{terminal}}" is not supported yet.': 'ターミナル "{{terminal}}" はまだサポートされていません', // Commands - Language - 'Invalid language. Available: {{options}}': - '無効な言語です。使用可能: {{options}}', - 'Language subcommands do not accept additional arguments.': - '言語サブコマンドは追加の引数を受け付けません', - 'Current UI language: {{lang}}': '現在のUI言語: {{lang}}', - 'Current LLM output language: {{lang}}': '現在のLLM出力言語: {{lang}}', - 'LLM output language not set': 'LLM出力言語が設定されていません', - 'Set UI language': 'UI言語を設定', - 'Set LLM output language': 'LLM出力言語を設定', - 'Usage: /language ui [{{options}}]': '使い方: /language ui [{{options}}]', - 'Usage: /language output ': '使い方: /language output <言語>', - 'Example: /language output 中文': '例: /language output 中文', - 'Example: /language output English': '例: /language output English', - 'Example: /language output 日本語': '例: /language output 日本語', - 'Example: /language output Português': '例: /language output Português', - 'UI language changed to {{lang}}': 'UI言語を {{lang}} に変更しました', - 'LLM output language rule file generated at {{path}}': - 'LLM出力言語ルールファイルを {{path}} に生成しました', - 'Please restart the application for the changes to take effect.': - '変更を有効にするにはアプリケーションを再起動してください', - 'Failed to generate LLM output language rule file: {{error}}': - 'LLM出力言語ルールファイルの生成に失敗: {{error}}', - 'Invalid command. Available subcommands:': - '無効なコマンドです。使用可能なサブコマンド:', - 'Available subcommands:': '使用可能なサブコマンド:', - 'To request additional UI language packs, please open an issue on GitHub.': - '追加のUI言語パックをリクエストするには、GitHub で Issue を作成してください', - 'Available options:': '使用可能なオプション:', - 'Set UI language to {{name}}': 'UI言語を {{name}} に設定', + "Invalid language. Available: {{options}}": "無効な言語です。使用可能: {{options}}", + "Language subcommands do not accept additional arguments.": + "言語サブコマンドは追加の引数を受け付けません", + "Current UI language: {{lang}}": "現在のUI言語: {{lang}}", + "Current LLM output language: {{lang}}": "現在のLLM出力言語: {{lang}}", + "LLM output language not set": "LLM出力言語が設定されていません", + "Set UI language": "UI言語を設定", + "Set LLM output language": "LLM出力言語を設定", + "Usage: /language ui [{{options}}]": "使い方: /language ui [{{options}}]", + "Usage: /language output ": "使い方: /language output <言語>", + "Example: /language output 中文": "例: /language output 中文", + "Example: /language output English": "例: /language output English", + "Example: /language output 日本語": "例: /language output 日本語", + "Example: /language output Português": "例: /language output Português", + "UI language changed to {{lang}}": "UI言語を {{lang}} に変更しました", + "LLM output language rule file generated at {{path}}": + "LLM出力言語ルールファイルを {{path}} に生成しました", + "Please restart the application for the changes to take effect.": + "変更を有効にするにはアプリケーションを再起動してください", + "Failed to generate LLM output language rule file: {{error}}": + "LLM出力言語ルールファイルの生成に失敗: {{error}}", + "Invalid command. Available subcommands:": "無効なコマンドです。使用可能なサブコマンド:", + "Available subcommands:": "使用可能なサブコマンド:", + "To request additional UI language packs, please open an issue on GitHub.": + "追加のUI言語パックをリクエストするには、GitHub で Issue を作成してください", + "Available options:": "使用可能なオプション:", + "Set UI language to {{name}}": "UI言語を {{name}} に設定", // Approval Mode - 'Approval Mode': '承認モード', - 'Current approval mode: {{mode}}': '現在の承認モード: {{mode}}', - 'Available approval modes:': '利用可能な承認モード:', - 'Approval mode changed to: {{mode}}': '承認モードを変更しました: {{mode}}', - 'Approval mode changed to: {{mode}} (saved to {{scope}} settings{{location}})': - '承認モードを {{mode}} に変更しました({{scope}} 設定{{location}}に保存)', - 'Usage: /approval-mode [--session|--user|--project]': - '使い方: /approval-mode <モード> [--session|--user|--project]', - 'Scope subcommands do not accept additional arguments.': - 'スコープサブコマンドは追加の引数を受け付けません', - 'Plan mode - Analyze only, do not modify files or execute commands': - 'プランモード - 分析のみ、ファイルの変更やコマンドの実行はしません', - 'Default mode - Require approval for file edits or shell commands': - 'デフォルトモード - ファイル編集やシェルコマンドには承認が必要', - 'Auto-edit mode - Automatically approve file edits': - '自動編集モード - ファイル編集を自動承認', - 'YOLO mode - Automatically approve all tools': - 'YOLOモード - すべてのツールを自動承認', - '{{mode}} mode': '{{mode}}モード', - 'Settings service is not available; unable to persist the approval mode.': - '設定サービスが利用できません。承認モードを保存できません', - 'Failed to save approval mode: {{error}}': - '承認モードの保存に失敗: {{error}}', - 'Failed to change approval mode: {{error}}': - '承認モードの変更に失敗: {{error}}', - 'Apply to current session only (temporary)': - '現在のセッションのみに適用(一時的)', - 'Persist for this project/workspace': 'このプロジェクト/ワークスペースに保存', - 'Persist for this user on this machine': 'このマシンのこのユーザーに保存', - 'Analyze only, do not modify files or execute commands': - '分析のみ、ファイルの変更やコマンドの実行はしません', - 'Require approval for file edits or shell commands': - 'ファイル編集やシェルコマンドには承認が必要', - 'Automatically approve file edits': 'ファイル編集を自動承認', - 'Automatically approve all tools': 'すべてのツールを自動承認', - 'Workspace approval mode exists and takes priority. User-level change will have no effect.': - 'ワークスペースの承認モードが存在し、優先されます。ユーザーレベルの変更は効果がありません', - '(Use Enter to select, Tab to change focus)': - '(Enter で選択、Tab でフォーカス変更)', - 'Apply To': '適用先', - 'Workspace Settings': 'ワークスペース設定', + "Approval Mode": "承認モード", + "Current approval mode: {{mode}}": "現在の承認モード: {{mode}}", + "Available approval modes:": "利用可能な承認モード:", + "Approval mode changed to: {{mode}}": "承認モードを変更しました: {{mode}}", + "Approval mode changed to: {{mode}} (saved to {{scope}} settings{{location}})": + "承認モードを {{mode}} に変更しました({{scope}} 設定{{location}}に保存)", + "Usage: /approval-mode [--session|--user|--project]": + "使い方: /approval-mode <モード> [--session|--user|--project]", + "Scope subcommands do not accept additional arguments.": + "スコープサブコマンドは追加の引数を受け付けません", + "Plan mode - Analyze only, do not modify files or execute commands": + "プランモード - 分析のみ、ファイルの変更やコマンドの実行はしません", + "Default mode - Require approval for file edits or shell commands": + "デフォルトモード - ファイル編集やシェルコマンドには承認が必要", + "Auto-edit mode - Automatically approve file edits": "自動編集モード - ファイル編集を自動承認", + "YOLO mode - Automatically approve all tools": "YOLOモード - すべてのツールを自動承認", + "{{mode}} mode": "{{mode}}モード", + "Settings service is not available; unable to persist the approval mode.": + "設定サービスが利用できません。承認モードを保存できません", + "Failed to save approval mode: {{error}}": "承認モードの保存に失敗: {{error}}", + "Failed to change approval mode: {{error}}": "承認モードの変更に失敗: {{error}}", + "Apply to current session only (temporary)": "現在のセッションのみに適用(一時的)", + "Persist for this project/workspace": "このプロジェクト/ワークスペースに保存", + "Persist for this user on this machine": "このマシンのこのユーザーに保存", + "Analyze only, do not modify files or execute commands": + "分析のみ、ファイルの変更やコマンドの実行はしません", + "Require approval for file edits or shell commands": "ファイル編集やシェルコマンドには承認が必要", + "Automatically approve file edits": "ファイル編集を自動承認", + "Automatically approve all tools": "すべてのツールを自動承認", + "Workspace approval mode exists and takes priority. User-level change will have no effect.": + "ワークスペースの承認モードが存在し、優先されます。ユーザーレベルの変更は効果がありません", + "(Use Enter to select, Tab to change focus)": "(Enter で選択、Tab でフォーカス変更)", + "Apply To": "適用先", + "Workspace Settings": "ワークスペース設定", // Memory - 'Commands for interacting with memory.': 'メモリ操作のコマンド', - 'Show the current memory contents.': '現在のメモリ内容を表示', - 'Show project-level memory contents.': 'プロジェクトレベルのメモリ内容を表示', - 'Show global memory contents.': 'グローバルメモリ内容を表示', - 'Add content to project-level memory.': - 'プロジェクトレベルのメモリにコンテンツを追加', - 'Add content to global memory.': 'グローバルメモリにコンテンツを追加', - 'Refresh the memory from the source.': 'ソースからメモリを更新', - 'Usage: /memory add --project ': - '使い方: /memory add --project <記憶するテキスト>', - 'Usage: /memory add --global ': - '使い方: /memory add --global <記憶するテキスト>', + "Commands for interacting with memory.": "メモリ操作のコマンド", + "Show the current memory contents.": "現在のメモリ内容を表示", + "Show project-level memory contents.": "プロジェクトレベルのメモリ内容を表示", + "Show global memory contents.": "グローバルメモリ内容を表示", + "Add content to project-level memory.": "プロジェクトレベルのメモリにコンテンツを追加", + "Add content to global memory.": "グローバルメモリにコンテンツを追加", + "Refresh the memory from the source.": "ソースからメモリを更新", + "Usage: /memory add --project ": + "使い方: /memory add --project <記憶するテキスト>", + "Usage: /memory add --global ": + "使い方: /memory add --global <記憶するテキスト>", 'Attempting to save to project memory: "{{text}}"': 'プロジェクトメモリへの保存を試行中: "{{text}}"', - 'Attempting to save to global memory: "{{text}}"': - 'グローバルメモリへの保存を試行中: "{{text}}"', - 'Current memory content from {{count}} file(s):': - '{{count}} 個のファイルからの現在のメモリ内容:', - 'Memory is currently empty.': 'メモリは現在空です', - 'Project memory file not found or is currently empty.': - 'プロジェクトメモリファイルが見つからないか、現在空です', - 'Global memory file not found or is currently empty.': - 'グローバルメモリファイルが見つからないか、現在空です', - 'Global memory is currently empty.': 'グローバルメモリは現在空です', - 'Global memory content:\n\n---\n{{content}}\n---': - 'グローバルメモリ内容:\n\n---\n{{content}}\n---', - 'Project memory content from {{path}}:\n\n---\n{{content}}\n---': - '{{path}} からのプロジェクトメモリ内容:\n\n---\n{{content}}\n---', - 'Project memory is currently empty.': 'プロジェクトメモリは現在空です', - 'Refreshing memory from source files...': - 'ソースファイルからメモリを更新中...', - 'Add content to the memory. Use --global for global memory or --project for project memory.': - 'メモリにコンテンツを追加。グローバルメモリには --global、プロジェクトメモリには --project を使用', - 'Usage: /memory add [--global|--project] ': - '使い方: /memory add [--global|--project] <記憶するテキスト>', + 'Attempting to save to global memory: "{{text}}"': 'グローバルメモリへの保存を試行中: "{{text}}"', + "Current memory content from {{count}} file(s):": "{{count}} 個のファイルからの現在のメモリ内容:", + "Memory is currently empty.": "メモリは現在空です", + "Project memory file not found or is currently empty.": + "プロジェクトメモリファイルが見つからないか、現在空です", + "Global memory file not found or is currently empty.": + "グローバルメモリファイルが見つからないか、現在空です", + "Global memory is currently empty.": "グローバルメモリは現在空です", + "Global memory content:\n\n---\n{{content}}\n---": + "グローバルメモリ内容:\n\n---\n{{content}}\n---", + "Project memory content from {{path}}:\n\n---\n{{content}}\n---": + "{{path}} からのプロジェクトメモリ内容:\n\n---\n{{content}}\n---", + "Project memory is currently empty.": "プロジェクトメモリは現在空です", + "Refreshing memory from source files...": "ソースファイルからメモリを更新中...", + "Add content to the memory. Use --global for global memory or --project for project memory.": + "メモリにコンテンツを追加。グローバルメモリには --global、プロジェクトメモリには --project を使用", + "Usage: /memory add [--global|--project] ": + "使い方: /memory add [--global|--project] <記憶するテキスト>", 'Attempting to save to memory {{scope}}: "{{fact}}"': 'メモリ {{scope}} への保存を試行中: "{{fact}}"', // MCP - 'Authenticate with an OAuth-enabled MCP server': - 'OAuth対応のMCPサーバーで認証', - 'List configured MCP servers and tools': - '設定済みのMCPサーバーとツールを一覧表示', - 'No MCP servers configured.': 'MCPサーバーが設定されていません', - 'Restarts MCP servers.': 'MCPサーバーを再起動します', - 'Could not retrieve tool registry.': 'ツールレジストリを取得できませんでした', - 'No MCP servers configured with OAuth authentication.': - 'OAuth認証が設定されたMCPサーバーはありません', - 'MCP servers with OAuth authentication:': 'OAuth認証のMCPサーバー:', - 'Use /mcp auth to authenticate.': - '認証するには /mcp auth <サーバー名> を使用', + "Authenticate with an OAuth-enabled MCP server": "OAuth対応のMCPサーバーで認証", + "List configured MCP servers and tools": "設定済みのMCPサーバーとツールを一覧表示", + "No MCP servers configured.": "MCPサーバーが設定されていません", + "Restarts MCP servers.": "MCPサーバーを再起動します", + "Could not retrieve tool registry.": "ツールレジストリを取得できませんでした", + "No MCP servers configured with OAuth authentication.": + "OAuth認証が設定されたMCPサーバーはありません", + "MCP servers with OAuth authentication:": "OAuth認証のMCPサーバー:", + "Use /mcp auth to authenticate.": "認証するには /mcp auth <サーバー名> を使用", "MCP server '{{name}}' not found.": "MCPサーバー '{{name}}' が見つかりません", "Successfully authenticated and refreshed tools for '{{name}}'.": "'{{name}}' の認証とツール更新に成功しました", "Failed to authenticate with MCP server '{{name}}': {{error}}": "MCPサーバー '{{name}}' での認証に失敗: {{error}}", - "Re-discovering tools from '{{name}}'...": - "'{{name}}' からツールを再検出中...", + "Re-discovering tools from '{{name}}'...": "'{{name}}' からツールを再検出中...", "Discovered {{count}} tool(s) from '{{name}}'.": "'{{name}}' から {{count}} 個のツールを検出しました。", - 'Authentication complete. Returning to server details...': - '認証完了。サーバー詳細に戻ります...', - 'Authentication successful.': '認証成功。', - 'If the browser does not open, copy and paste this URL into your browser:': - 'ブラウザが開かない場合は、このURLをコピーしてブラウザに貼り付けてください:', - 'Make sure to copy the COMPLETE URL - it may wrap across multiple lines.': - '⚠️ URL全体をコピーしてください——複数行にまたがる場合があります。', - 'Configured MCP servers:': '設定済みMCPサーバー:', - Ready: '準備完了', - Disconnected: '切断', - '{{count}} tool': '{{count}} ツール', - '{{count}} tools': '{{count}} ツール', - 'Restarting MCP servers...': 'MCPサーバーを再起動中...', + "Authentication complete. Returning to server details...": "認証完了。サーバー詳細に戻ります...", + "Authentication successful.": "認証成功。", + "If the browser does not open, copy and paste this URL into your browser:": + "ブラウザが開かない場合は、このURLをコピーしてブラウザに貼り付けてください:", + "Make sure to copy the COMPLETE URL - it may wrap across multiple lines.": + "⚠️ URL全体をコピーしてください——複数行にまたがる場合があります。", + "Configured MCP servers:": "設定済みMCPサーバー:", + Ready: "準備完了", + Disconnected: "切断", + "{{count}} tool": "{{count}} ツール", + "{{count}} tools": "{{count}} ツール", + "Restarting MCP servers...": "MCPサーバーを再起動中...", // Chat - 'Manage conversation history.': '会話履歴を管理します', - 'List saved conversation checkpoints': - '保存された会話チェックポイントを一覧表示', - 'No saved conversation checkpoints found.': - '保存された会話チェックポイントが見つかりません', - 'List of saved conversations:': '保存された会話の一覧:', - 'Note: Newest last, oldest first': - '注: 最新のものが下にあり、過去のものが上にあります', - 'Save the current conversation as a checkpoint. Usage: /chat save ': - '現在の会話をチェックポイントとして保存。使い方: /chat save <タグ>', - 'Missing tag. Usage: /chat save ': - 'タグが不足しています。使い方: /chat save <タグ>', - 'Delete a conversation checkpoint. Usage: /chat delete ': - '会話チェックポイントを削除。使い方: /chat delete <タグ>', - 'Missing tag. Usage: /chat delete ': - 'タグが不足しています。使い方: /chat delete <タグ>', + "Manage conversation history.": "会話履歴を管理します", + "List saved conversation checkpoints": "保存された会話チェックポイントを一覧表示", + "No saved conversation checkpoints found.": "保存された会話チェックポイントが見つかりません", + "List of saved conversations:": "保存された会話の一覧:", + "Note: Newest last, oldest first": "注: 最新のものが下にあり、過去のものが上にあります", + "Save the current conversation as a checkpoint. Usage: /chat save ": + "現在の会話をチェックポイントとして保存。使い方: /chat save <タグ>", + "Missing tag. Usage: /chat save ": "タグが不足しています。使い方: /chat save <タグ>", + "Delete a conversation checkpoint. Usage: /chat delete ": + "会話チェックポイントを削除。使い方: /chat delete <タグ>", + "Missing tag. Usage: /chat delete ": "タグが不足しています。使い方: /chat delete <タグ>", "Conversation checkpoint '{{tag}}' has been deleted.": "会話チェックポイント '{{tag}}' を削除しました", "Error: No checkpoint found with tag '{{tag}}'.": "エラー: タグ '{{tag}}' のチェックポイントが見つかりません", - 'Resume a conversation from a checkpoint. Usage: /chat resume ': - 'チェックポイントから会話を再開。使い方: /chat resume <タグ>', - 'Missing tag. Usage: /chat resume ': - 'タグが不足しています。使い方: /chat resume <タグ>', - 'No saved checkpoint found with tag: {{tag}}.': - 'タグ {{tag}} のチェックポイントが見つかりません', - 'A checkpoint with the tag {{tag}} already exists. Do you want to overwrite it?': - 'タグ {{tag}} のチェックポイントは既に存在します。上書きしますか?', - 'No chat client available to save conversation.': - '会話を保存するためのチャットクライアントがありません', - 'Conversation checkpoint saved with tag: {{tag}}.': - 'タグ {{tag}} で会話チェックポイントを保存しました', - 'No conversation found to save.': '保存する会話が見つかりません', - 'No chat client available to share conversation.': - '会話を共有するためのチャットクライアントがありません', - 'Invalid file format. Only .md and .json are supported.': - '無効なファイル形式です。.md と .json のみサポートされています', - 'Error sharing conversation: {{error}}': '会話の共有中にエラー: {{error}}', - 'Conversation shared to {{filePath}}': '会話を {{filePath}} に共有しました', - 'No conversation found to share.': '共有する会話が見つかりません', - 'Share the current conversation to a markdown or json file. Usage: /chat share ': - '現在の会話をmarkdownまたはjsonファイルに共有。使い方: /chat share <ファイル>', + "Resume a conversation from a checkpoint. Usage: /chat resume ": + "チェックポイントから会話を再開。使い方: /chat resume <タグ>", + "Missing tag. Usage: /chat resume ": "タグが不足しています。使い方: /chat resume <タグ>", + "No saved checkpoint found with tag: {{tag}}.": "タグ {{tag}} のチェックポイントが見つかりません", + "A checkpoint with the tag {{tag}} already exists. Do you want to overwrite it?": + "タグ {{tag}} のチェックポイントは既に存在します。上書きしますか?", + "No chat client available to save conversation.": + "会話を保存するためのチャットクライアントがありません", + "Conversation checkpoint saved with tag: {{tag}}.": + "タグ {{tag}} で会話チェックポイントを保存しました", + "No conversation found to save.": "保存する会話が見つかりません", + "No chat client available to share conversation.": + "会話を共有するためのチャットクライアントがありません", + "Invalid file format. Only .md and .json are supported.": + "無効なファイル形式です。.md と .json のみサポートされています", + "Error sharing conversation: {{error}}": "会話の共有中にエラー: {{error}}", + "Conversation shared to {{filePath}}": "会話を {{filePath}} に共有しました", + "No conversation found to share.": "共有する会話が見つかりません", + "Share the current conversation to a markdown or json file. Usage: /chat share ": + "現在の会話をmarkdownまたはjsonファイルに共有。使い方: /chat share <ファイル>", // Summary - 'Generate a project summary and save it to .qwen/PROJECT_SUMMARY.md': - 'プロジェクトサマリーを生成し、.qwen/PROJECT_SUMMARY.md に保存', - 'No chat client available to generate summary.': - 'サマリーを生成するためのチャットクライアントがありません', - 'Already generating summary, wait for previous request to complete': - 'サマリー生成中です。前のリクエストの完了をお待ちください', - 'No conversation found to summarize.': '要約する会話が見つかりません', - 'Failed to generate project context summary: {{error}}': - 'プロジェクトコンテキストサマリーの生成に失敗: {{error}}', - 'Saved project summary to {{filePathForDisplay}}.': - 'プロジェクトサマリーを {{filePathForDisplay}} に保存しました', - 'Saving project summary...': 'プロジェクトサマリーを保存中...', - 'Generating project summary...': 'プロジェクトサマリーを生成中...', - 'Failed to generate summary - no text content received from LLM response': - 'サマリーの生成に失敗 - LLMレスポンスからテキストコンテンツを受信できませんでした', + "Generate a project summary and save it to .qwen/PROJECT_SUMMARY.md": + "プロジェクトサマリーを生成し、.qwen/PROJECT_SUMMARY.md に保存", + "No chat client available to generate summary.": + "サマリーを生成するためのチャットクライアントがありません", + "Already generating summary, wait for previous request to complete": + "サマリー生成中です。前のリクエストの完了をお待ちください", + "No conversation found to summarize.": "要約する会話が見つかりません", + "Failed to generate project context summary: {{error}}": + "プロジェクトコンテキストサマリーの生成に失敗: {{error}}", + "Saved project summary to {{filePathForDisplay}}.": + "プロジェクトサマリーを {{filePathForDisplay}} に保存しました", + "Saving project summary...": "プロジェクトサマリーを保存中...", + "Generating project summary...": "プロジェクトサマリーを生成中...", + "Failed to generate summary - no text content received from LLM response": + "サマリーの生成に失敗 - LLMレスポンスからテキストコンテンツを受信できませんでした", // Model - 'Switch the model for this session': 'このセッションのモデルを切り替え', - 'Set fast model for background tasks': - 'バックグラウンドタスク用の高速モデルを設定', - 'Content generator configuration not available.': - 'コンテンツジェネレーター設定が利用できません', - 'Authentication type not available.': '認証タイプが利用できません', - 'No models available for the current authentication type ({{authType}}).': - '現在の認証タイプ({{authType}})で利用可能なモデルはありません', + "Switch the model for this session": "このセッションのモデルを切り替え", + "Set fast model for background tasks": "バックグラウンドタスク用の高速モデルを設定", + "Content generator configuration not available.": "コンテンツジェネレーター設定が利用できません", + "Authentication type not available.": "認証タイプが利用できません", + "No models available for the current authentication type ({{authType}}).": + "現在の認証タイプ({{authType}})で利用可能なモデルはありません", // Clear - 'Starting a new session, resetting chat, and clearing terminal.': - '新しいセッションを開始し、チャットをリセットし、ターミナルをクリアしています', - 'Starting a new session and clearing.': - '新しいセッションを開始してクリアしています', + "Starting a new session, resetting chat, and clearing terminal.": + "新しいセッションを開始し、チャットをリセットし、ターミナルをクリアしています", + "Starting a new session and clearing.": "新しいセッションを開始してクリアしています", // Compress - 'Already compressing, wait for previous request to complete': - '圧縮中です。前のリクエストの完了をお待ちください', - 'Failed to compress chat history.': 'チャット履歴の圧縮に失敗しました', - 'Failed to compress chat history: {{error}}': - 'チャット履歴の圧縮に失敗: {{error}}', - 'Compressing chat history': 'チャット履歴を圧縮中', - 'Chat history compressed from {{originalTokens}} to {{newTokens}} tokens.': - 'チャット履歴を {{originalTokens}} トークンから {{newTokens}} トークンに圧縮しました', - 'Compression was not beneficial for this history size.': - 'この履歴サイズには圧縮の効果がありませんでした', - 'Chat history compression did not reduce size. This may indicate issues with the compression prompt.': - 'チャット履歴の圧縮でサイズが減少しませんでした。圧縮プロンプトに問題がある可能性があります', - 'Could not compress chat history due to a token counting error.': - 'トークンカウントエラーのため、チャット履歴を圧縮できませんでした', - 'Chat history is already compressed.': 'チャット履歴は既に圧縮されています', + "Already compressing, wait for previous request to complete": + "圧縮中です。前のリクエストの完了をお待ちください", + "Failed to compress chat history.": "チャット履歴の圧縮に失敗しました", + "Failed to compress chat history: {{error}}": "チャット履歴の圧縮に失敗: {{error}}", + "Compressing chat history": "チャット履歴を圧縮中", + "Chat history compressed from {{originalTokens}} to {{newTokens}} tokens.": + "チャット履歴を {{originalTokens}} トークンから {{newTokens}} トークンに圧縮しました", + "Compression was not beneficial for this history size.": + "この履歴サイズには圧縮の効果がありませんでした", + "Chat history compression did not reduce size. This may indicate issues with the compression prompt.": + "チャット履歴の圧縮でサイズが減少しませんでした。圧縮プロンプトに問題がある可能性があります", + "Could not compress chat history due to a token counting error.": + "トークンカウントエラーのため、チャット履歴を圧縮できませんでした", + "Chat history is already compressed.": "チャット履歴は既に圧縮されています", // Directory - 'Configuration is not available.': '設定が利用できません', - 'Please provide at least one path to add.': - '追加するパスを少なくとも1つ指定してください', - 'The /directory add command is not supported in restrictive sandbox profiles. Please use --include-directories when starting the session instead.': - '制限的なサンドボックスプロファイルでは /directory add コマンドはサポートされていません。代わりにセッション開始時に --include-directories を使用してください', - "Error adding '{{path}}': {{error}}": - "'{{path}}' の追加中にエラー: {{error}}", - 'Successfully added QWEN.md files from the following directories if there are:\n- {{directories}}': - '以下のディレクトリから QWEN.md ファイルを追加しました(存在する場合):\n- {{directories}}', - 'Error refreshing memory: {{error}}': 'メモリの更新中にエラー: {{error}}', - 'Successfully added directories:\n- {{directories}}': - 'ディレクトリを正常に追加しました:\n- {{directories}}', - 'Current workspace directories:\n{{directories}}': - '現在のワークスペースディレクトリ:\n{{directories}}', + "Configuration is not available.": "設定が利用できません", + "Please provide at least one path to add.": "追加するパスを少なくとも1つ指定してください", + "The /directory add command is not supported in restrictive sandbox profiles. Please use --include-directories when starting the session instead.": + "制限的なサンドボックスプロファイルでは /directory add コマンドはサポートされていません。代わりにセッション開始時に --include-directories を使用してください", + "Error adding '{{path}}': {{error}}": "'{{path}}' の追加中にエラー: {{error}}", + "Successfully added QWEN.md files from the following directories if there are:\n- {{directories}}": + "以下のディレクトリから QWEN.md ファイルを追加しました(存在する場合):\n- {{directories}}", + "Error refreshing memory: {{error}}": "メモリの更新中にエラー: {{error}}", + "Successfully added directories:\n- {{directories}}": + "ディレクトリを正常に追加しました:\n- {{directories}}", + "Current workspace directories:\n{{directories}}": + "現在のワークスペースディレクトリ:\n{{directories}}", // Docs - 'Please open the following URL in your browser to view the documentation:\n{{url}}': - 'ドキュメントを表示するには、ブラウザで以下のURLを開いてください:\n{{url}}', - 'Opening documentation in your browser: {{url}}': - ' ブラウザでドキュメントを開きました: {{url}}', + "Please open the following URL in your browser to view the documentation:\n{{url}}": + "ドキュメントを表示するには、ブラウザで以下のURLを開いてください:\n{{url}}", + "Opening documentation in your browser: {{url}}": " ブラウザでドキュメントを開きました: {{url}}", // Dialogs - Tool Confirmation - 'Do you want to proceed?': '続行しますか?', - 'Yes, allow once': 'はい(今回のみ許可)', - 'Allow always': '常に許可する', - Yes: 'はい', - No: 'いいえ', - 'No (esc)': 'いいえ (Esc)', - 'Yes, allow always for this session': 'はい、このセッションで常に許可', + "Do you want to proceed?": "続行しますか?", + "Yes, allow once": "はい(今回のみ許可)", + "Allow always": "常に許可する", + Yes: "はい", + No: "いいえ", + "No (esc)": "いいえ (Esc)", + "Yes, allow always for this session": "はい、このセッションで常に許可", // MCP Management - Core translations - 'Manage MCP servers': 'MCPサーバーを管理', - 'Server Detail': 'サーバー詳細', - 'Disable Server': 'サーバーを無効化', - Tools: 'ツール', - 'Tool Detail': 'ツール詳細', - 'MCP Management': 'MCP管理', - 'Loading...': '読み込み中...', - 'Unknown step': '不明なステップ', - 'Esc to back': 'Esc 戻る', - '↑↓ to navigate · Enter to select · Esc to close': - '↑↓ ナビゲート · Enter 選択 · Esc 閉じる', - '↑↓ to navigate · Enter to select · Esc to back': - '↑↓ ナビゲート · Enter 選択 · Esc 戻る', - '↑↓ to navigate · Enter to confirm · Esc to back': - '↑↓ ナビゲート · Enter 確認 · Esc 戻る', - 'User Settings (global)': 'ユーザー設定(グローバル)', - 'Workspace Settings (project-specific)': - 'ワークスペース設定(プロジェクト固有)', - 'Disable server:': 'サーバーを無効化:', - 'Select where to add the server to the exclude list:': - 'サーバーを除外リストに追加する場所を選択してください:', - 'Press Enter to confirm, Esc to cancel': 'Enter で確認、Esc でキャンセル', - Disable: '無効化', - Enable: '有効化', - Authenticate: '認証', - 'Re-authenticate': '再認証', - 'Clear Authentication': '認証をクリア', - disabled: '無効', - 'Server:': 'サーバー:', - Reconnect: '再接続', - 'View tools': 'ツールを表示', - 'Status:': 'ステータス:', - 'Source:': 'ソース:', - 'Command:': 'コマンド:', - 'Working Directory:': '作業ディレクトリ:', - 'Capabilities:': '機能:', - 'No server selected': 'サーバーが選択されていません', - '(disabled)': '(無効)', - 'Error:': 'エラー:', - tool: 'ツール', - tools: 'ツール', - connected: '接続済み', - connecting: '接続中', - disconnected: '切断済み', - error: 'エラー', + "Manage MCP servers": "MCPサーバーを管理", + "Server Detail": "サーバー詳細", + "Disable Server": "サーバーを無効化", + Tools: "ツール", + "Tool Detail": "ツール詳細", + "MCP Management": "MCP管理", + "Loading...": "読み込み中...", + "Unknown step": "不明なステップ", + "Esc to back": "Esc 戻る", + "↑↓ to navigate · Enter to select · Esc to close": "↑↓ ナビゲート · Enter 選択 · Esc 閉じる", + "↑↓ to navigate · Enter to select · Esc to back": "↑↓ ナビゲート · Enter 選択 · Esc 戻る", + "↑↓ to navigate · Enter to confirm · Esc to back": "↑↓ ナビゲート · Enter 確認 · Esc 戻る", + "User Settings (global)": "ユーザー設定(グローバル)", + "Workspace Settings (project-specific)": "ワークスペース設定(プロジェクト固有)", + "Disable server:": "サーバーを無効化:", + "Select where to add the server to the exclude list:": + "サーバーを除外リストに追加する場所を選択してください:", + "Press Enter to confirm, Esc to cancel": "Enter で確認、Esc でキャンセル", + Disable: "無効化", + Enable: "有効化", + Authenticate: "認証", + "Re-authenticate": "再認証", + "Clear Authentication": "認証をクリア", + disabled: "無効", + "Server:": "サーバー:", + Reconnect: "再接続", + "View tools": "ツールを表示", + "Status:": "ステータス:", + "Source:": "ソース:", + "Command:": "コマンド:", + "Working Directory:": "作業ディレクトリ:", + "Capabilities:": "機能:", + "No server selected": "サーバーが選択されていません", + "(disabled)": "(無効)", + "Error:": "エラー:", + tool: "ツール", + tools: "ツール", + connected: "接続済み", + connecting: "接続中", + disconnected: "切断済み", + error: "エラー", // MCP Server List - 'User MCPs': 'ユーザーMCP', - 'Project MCPs': 'プロジェクトMCP', - 'Extension MCPs': '拡張機能MCP', - server: 'サーバー', - servers: 'サーバー', - 'Add MCP servers to your settings to get started.': - '設定にMCPサーバーを追加して開始してください。', - 'Run qwen --debug to see error logs': - 'qwen --debug を実行してエラーログを確認してください', + "User MCPs": "ユーザーMCP", + "Project MCPs": "プロジェクトMCP", + "Extension MCPs": "拡張機能MCP", + server: "サーバー", + servers: "サーバー", + "Add MCP servers to your settings to get started.": + "設定にMCPサーバーを追加して開始してください。", + "Run qwen --debug to see error logs": "qwen --debug を実行してエラーログを確認してください", // MCP OAuth Authentication - 'OAuth Authentication': 'OAuth 認証', - 'Press Enter to start authentication, Esc to go back': - 'Enter で認証開始、Esc で戻る', - 'Authenticating... Please complete the login in your browser.': - '認証中... ブラウザでログインを完了してください。', - 'Press Enter or Esc to go back': 'Enter または Esc で戻る', + "OAuth Authentication": "OAuth 認証", + "Press Enter to start authentication, Esc to go back": "Enter で認証開始、Esc で戻る", + "Authenticating... Please complete the login in your browser.": + "認証中... ブラウザでログインを完了してください。", + "Press Enter or Esc to go back": "Enter または Esc で戻る", // MCP Tool List - 'No tools available for this server.': - 'このサーバーには使用可能なツールがありません。', - destructive: '破壊的', - 'read-only': '読み取り専用', - 'open-world': 'オープンワールド', - idempotent: '冪等', - 'Tools for {{name}}': '{{name}} のツール', - 'Tools for {{serverName}}': '{{serverName}} のツール', - '{{current}}/{{total}}': '{{current}}/{{total}}', + "No tools available for this server.": "このサーバーには使用可能なツールがありません。", + destructive: "破壊的", + "read-only": "読み取り専用", + "open-world": "オープンワールド", + idempotent: "冪等", + "Tools for {{name}}": "{{name}} のツール", + "Tools for {{serverName}}": "{{serverName}} のツール", + "{{current}}/{{total}}": "{{current}}/{{total}}", // MCP Tool Detail - required: '必須', - Type: '型', - Enum: '列挙', - Parameters: 'パラメータ', - 'No tool selected': 'ツールが選択されていません', - Annotations: '注釈', - Title: 'タイトル', - 'Read Only': '読み取り専用', - Destructive: '破壊的', - Idempotent: '冪等', - 'Open World': 'オープンワールド', - Server: 'サーバー', + required: "必須", + Type: "型", + Enum: "列挙", + Parameters: "パラメータ", + "No tool selected": "ツールが選択されていません", + Annotations: "注釈", + Title: "タイトル", + "Read Only": "読み取り専用", + Destructive: "破壊的", + Idempotent: "冪等", + "Open World": "オープンワールド", + Server: "サーバー", // Invalid tool related translations - '{{count}} invalid tools': '{{count}} 個の無効なツール', - invalid: '無効', - 'invalid: {{reason}}': '無効: {{reason}}', - 'missing name': '名前なし', - 'missing description': '説明なし', - '(unnamed)': '(名前なし)', - 'Warning: This tool cannot be called by the LLM': - '警告: このツールはLLMによって呼び出すことができません', - Reason: '理由', - 'Tools must have both name and description to be used by the LLM.': - 'ツールはLLMによって使用されるには名前と説明の両方が必要です。', - 'Modify in progress:': '変更中:', - 'Save and close external editor to continue': - '続行するには外部エディタを保存して閉じてください', - 'Apply this change?': 'この変更を適用しますか?', - 'Yes, allow always': 'はい、常に許可', - 'Modify with external editor': '外部エディタで編集', - 'No, suggest changes (esc)': 'いいえ、変更を提案 (Esc)', + "{{count}} invalid tools": "{{count}} 個の無効なツール", + invalid: "無効", + "invalid: {{reason}}": "無効: {{reason}}", + "missing name": "名前なし", + "missing description": "説明なし", + "(unnamed)": "(名前なし)", + "Warning: This tool cannot be called by the LLM": + "警告: このツールはLLMによって呼び出すことができません", + Reason: "理由", + "Tools must have both name and description to be used by the LLM.": + "ツールはLLMによって使用されるには名前と説明の両方が必要です。", + "Modify in progress:": "変更中:", + "Save and close external editor to continue": "続行するには外部エディタを保存して閉じてください", + "Apply this change?": "この変更を適用しますか?", + "Yes, allow always": "はい、常に許可", + "Modify with external editor": "外部エディタで編集", + "No, suggest changes (esc)": "いいえ、変更を提案 (Esc)", "Allow execution of: '{{command}}'?": "'{{command}}' の実行を許可しますか?", - 'Yes, allow always ...': 'はい、常に許可...', - 'Always allow in this project': 'このプロジェクトで常に許可', - 'Always allow {{action}} in this project': - 'このプロジェクトで{{action}}を常に許可', - 'Always allow for this user': 'このユーザーに常に許可', - 'Always allow {{action}} for this user': 'このユーザーに{{action}}を常に許可', - 'Yes, restore previous mode ({{mode}})': - 'はい、以前のモードに戻す ({{mode}})', - 'Yes, and auto-accept edits': 'はい、編集を自動承認', - 'Yes, and manually approve edits': 'はい、編集を手動承認', - 'No, keep planning (esc)': 'いいえ、計画を続ける (Esc)', - 'URLs to fetch:': '取得するURL:', - 'MCP Server: {{server}}': 'MCPサーバー: {{server}}', - 'Tool: {{tool}}': 'ツール: {{tool}}', + "Yes, allow always ...": "はい、常に許可...", + "Always allow in this project": "このプロジェクトで常に許可", + "Always allow {{action}} in this project": "このプロジェクトで{{action}}を常に許可", + "Always allow for this user": "このユーザーに常に許可", + "Always allow {{action}} for this user": "このユーザーに{{action}}を常に許可", + "Yes, restore previous mode ({{mode}})": "はい、以前のモードに戻す ({{mode}})", + "Yes, and auto-accept edits": "はい、編集を自動承認", + "Yes, and manually approve edits": "はい、編集を手動承認", + "No, keep planning (esc)": "いいえ、計画を続ける (Esc)", + "URLs to fetch:": "取得するURL:", + "MCP Server: {{server}}": "MCPサーバー: {{server}}", + "Tool: {{tool}}": "ツール: {{tool}}", 'Allow execution of MCP tool "{{tool}}" from server "{{server}}"?': 'サーバー "{{server}}" からの MCPツール "{{tool}}" の実行を許可しますか?', 'Yes, always allow tool "{{tool}}" from server "{{server}}"': @@ -933,516 +859,486 @@ export default { 'Yes, always allow all tools from server "{{server}}"': 'はい、サーバー "{{server}}" からのすべてのツールを常に許可', // Dialogs - Shell Confirmation - 'Shell Command Execution': 'シェルコマンド実行', - 'A custom command wants to run the following shell commands:': - 'カスタムコマンドが以下のシェルコマンドを実行しようとしています:', + "Shell Command Execution": "シェルコマンド実行", + "A custom command wants to run the following shell commands:": + "カスタムコマンドが以下のシェルコマンドを実行しようとしています:", // Dialogs - Pro Quota - 'Pro quota limit reached for {{model}}.': - '{{model}} のProクォータ上限に達しました', - 'Change auth (executes the /auth command)': - '認証を変更(/auth コマンドを実行)', - 'Continue with {{model}}': '{{model}} で続行', + "Pro quota limit reached for {{model}}.": "{{model}} のProクォータ上限に達しました", + "Change auth (executes the /auth command)": "認証を変更(/auth コマンドを実行)", + "Continue with {{model}}": "{{model}} で続行", // Dialogs - Welcome Back - 'Current Plan:': '現在のプラン:', - 'Progress: {{done}}/{{total}} tasks completed': - '進捗: {{done}}/{{total}} タスク完了', - ', {{inProgress}} in progress': '、{{inProgress}} 進行中', - 'Pending Tasks:': '保留中のタスク:', - 'What would you like to do?': '何をしますか?', - 'Choose how to proceed with your session:': - 'セッションの続行方法を選択してください:', - 'Start new chat session': '新しいチャットセッションを開始', - 'Continue previous conversation': '前回の会話を続行', - '👋 Welcome back! (Last updated: {{timeAgo}})': - '👋 おかえりなさい!(最終更新: {{timeAgo}})', - '🎯 Overall Goal:': '🎯 全体目標:', + "Current Plan:": "現在のプラン:", + "Progress: {{done}}/{{total}} tasks completed": "進捗: {{done}}/{{total}} タスク完了", + ", {{inProgress}} in progress": "、{{inProgress}} 進行中", + "Pending Tasks:": "保留中のタスク:", + "What would you like to do?": "何をしますか?", + "Choose how to proceed with your session:": "セッションの続行方法を選択してください:", + "Start new chat session": "新しいチャットセッションを開始", + "Continue previous conversation": "前回の会話を続行", + "👋 Welcome back! (Last updated: {{timeAgo}})": "👋 おかえりなさい!(最終更新: {{timeAgo}})", + "🎯 Overall Goal:": "🎯 全体目標:", // Dialogs - Auth - 'Get started': '始める', - 'Select Authentication Method': '認証方法を選択', - 'OpenAI API key is required to use OpenAI authentication.': - 'OpenAI認証を使用するには OpenAI APIキーが必要です', - 'You must select an auth method to proceed. Press Ctrl+C again to exit.': - '続行するには認証方法を選択してください。Ctrl+C をもう一度押すと終了します', - 'Terms of Services and Privacy Notice': '利用規約とプライバシー通知', - 'Paid \u00B7 Up to 6,000 requests/5 hrs \u00B7 All Alibaba Cloud Coding Plan Models': - '有料 \u00B7 5時間最大6,000リクエスト \u00B7 すべての Alibaba Cloud Coding Plan モデル', - 'Alibaba Cloud Coding Plan': 'Alibaba Cloud Coding Plan', - 'Bring your own API key': '自分のAPIキーを使用', - 'API-KEY': 'API-KEY', - 'Use coding plan credentials or your own api-keys/providers.': - 'Coding Planの認証情報またはご自身のAPIキー/プロバイダーをご利用ください。', - OpenAI: 'OpenAI', - 'Failed to login. Message: {{message}}': - 'ログインに失敗しました。メッセージ: {{message}}', - 'Authentication is enforced to be {{enforcedType}}, but you are currently using {{currentType}}.': - '認証は {{enforcedType}} に強制されていますが、現在 {{currentType}} を使用しています', - 'Please visit this URL to authorize:': - '認証するには以下のURLにアクセスしてください:', - 'Or scan the QR code below:': 'または以下のQRコードをスキャン:', - 'Waiting for authorization': '認証を待っています', - 'Time remaining:': '残り時間:', - '(Press ESC or CTRL+C to cancel)': '(ESC または CTRL+C でキャンセル)', - 'OAuth token expired (over {{seconds}} seconds). Please select authentication method again.': - 'OAuthトークンが期限切れです({{seconds}}秒以上)。認証方法を再度選択してください', - 'Press any key to return to authentication type selection.': - '認証タイプ選択に戻るには任意のキーを押してください', - 'Authentication timed out. Please try again.': - '認証がタイムアウトしました。再度お試しください', - 'Waiting for auth... (Press ESC or CTRL+C to cancel)': - '認証を待っています... (ESC または CTRL+C でキャンセル)', - 'Failed to authenticate. Message: {{message}}': - '認証に失敗しました。メッセージ: {{message}}', - 'Authenticated successfully with {{authType}} credentials.': - '{{authType}} 認証情報で正常に認証されました', + "Get started": "始める", + "Select Authentication Method": "認証方法を選択", + "OpenAI API key is required to use OpenAI authentication.": + "OpenAI認証を使用するには OpenAI APIキーが必要です", + "You must select an auth method to proceed. Press Ctrl+C again to exit.": + "続行するには認証方法を選択してください。Ctrl+C をもう一度押すと終了します", + "Terms of Services and Privacy Notice": "利用規約とプライバシー通知", + "Paid \u00B7 Up to 6,000 requests/5 hrs \u00B7 All Alibaba Cloud Coding Plan Models": + "有料 \u00B7 5時間最大6,000リクエスト \u00B7 すべての Alibaba Cloud Coding Plan モデル", + "Alibaba Cloud Coding Plan": "Alibaba Cloud Coding Plan", + "Bring your own API key": "自分のAPIキーを使用", + "API-KEY": "API-KEY", + "Use coding plan credentials or your own api-keys/providers.": + "Coding Planの認証情報またはご自身のAPIキー/プロバイダーをご利用ください。", + OpenAI: "OpenAI", + "Failed to login. Message: {{message}}": "ログインに失敗しました。メッセージ: {{message}}", + "Authentication is enforced to be {{enforcedType}}, but you are currently using {{currentType}}.": + "認証は {{enforcedType}} に強制されていますが、現在 {{currentType}} を使用しています", + "Please visit this URL to authorize:": "認証するには以下のURLにアクセスしてください:", + "Or scan the QR code below:": "または以下のQRコードをスキャン:", + "Waiting for authorization": "認証を待っています", + "Time remaining:": "残り時間:", + "(Press ESC or CTRL+C to cancel)": "(ESC または CTRL+C でキャンセル)", + "OAuth token expired (over {{seconds}} seconds). Please select authentication method again.": + "OAuthトークンが期限切れです({{seconds}}秒以上)。認証方法を再度選択してください", + "Press any key to return to authentication type selection.": + "認証タイプ選択に戻るには任意のキーを押してください", + "Authentication timed out. Please try again.": "認証がタイムアウトしました。再度お試しください", + "Waiting for auth... (Press ESC or CTRL+C to cancel)": + "認証を待っています... (ESC または CTRL+C でキャンセル)", + "Failed to authenticate. Message: {{message}}": "認証に失敗しました。メッセージ: {{message}}", + "Authenticated successfully with {{authType}} credentials.": + "{{authType}} 認証情報で正常に認証されました", 'Invalid QWEN_DEFAULT_AUTH_TYPE value: "{{value}}". Valid values are: {{validValues}}': '無効な QWEN_DEFAULT_AUTH_TYPE 値: "{{value}}"。有効な値: {{validValues}}', - 'OpenAI Configuration Required': 'OpenAI設定が必要です', - 'Please enter your OpenAI configuration. You can get an API key from': - 'OpenAI設定を入力してください。APIキーは以下から取得できます', - 'API Key:': 'APIキー:', - 'Invalid credentials: {{errorMessage}}': '無効な認証情報: {{errorMessage}}', - 'Failed to validate credentials': '認証情報の検証に失敗しました', - 'Press Enter to continue, Tab/↑↓ to navigate, Esc to cancel': - 'Enter で続行、Tab/↑↓ で移動、Esc でキャンセル', + "OpenAI Configuration Required": "OpenAI設定が必要です", + "Please enter your OpenAI configuration. You can get an API key from": + "OpenAI設定を入力してください。APIキーは以下から取得できます", + "API Key:": "APIキー:", + "Invalid credentials: {{errorMessage}}": "無効な認証情報: {{errorMessage}}", + "Failed to validate credentials": "認証情報の検証に失敗しました", + "Press Enter to continue, Tab/↑↓ to navigate, Esc to cancel": + "Enter で続行、Tab/↑↓ で移動、Esc でキャンセル", // Dialogs - Model - 'Select Model': 'モデルを選択', - '(Press Esc to close)': '(Esc で閉じる)', - Modality: 'モダリティ', - 'Context Window': 'コンテキストウィンドウ', - text: 'テキスト', - 'text-only': 'テキストのみ', - image: '画像', - pdf: 'PDF', - audio: '音声', - video: '動画', - 'not set': '未設定', - none: 'なし', - unknown: '不明', - 'Qwen 3.6 Plus — efficient hybrid model with leading coding performance': - 'Qwen 3.6 Plus — 効率的なハイブリッドモデル、業界トップクラスのコーディング性能', - 'The latest Qwen Vision model from Alibaba Cloud ModelStudio (version: qwen3-vl-plus-2025-09-23)': - 'Alibaba Cloud ModelStudioの最新Qwen Visionモデル(バージョン: qwen3-vl-plus-2025-09-23)', + "Select Model": "モデルを選択", + "(Press Esc to close)": "(Esc で閉じる)", + Modality: "モダリティ", + "Context Window": "コンテキストウィンドウ", + text: "テキスト", + "text-only": "テキストのみ", + image: "画像", + pdf: "PDF", + audio: "音声", + video: "動画", + "not set": "未設定", + none: "なし", + unknown: "不明", + "Qwen 3.6 Plus — efficient hybrid model with leading coding performance": + "Qwen 3.6 Plus — 効率的なハイブリッドモデル、業界トップクラスのコーディング性能", + "The latest Qwen Vision model from Alibaba Cloud ModelStudio (version: qwen3-vl-plus-2025-09-23)": + "Alibaba Cloud ModelStudioの最新Qwen Visionモデル(バージョン: qwen3-vl-plus-2025-09-23)", // Dialogs - Permissions - 'Manage folder trust settings': 'フォルダ信頼設定を管理', - 'Manage permission rules': '権限ルールを管理', - Allow: '許可', - Ask: '確認', - Deny: '拒否', - Workspace: 'ワークスペース', + "Manage folder trust settings": "フォルダ信頼設定を管理", + "Manage permission rules": "権限ルールを管理", + Allow: "許可", + Ask: "確認", + Deny: "拒否", + Workspace: "ワークスペース", "Qwen Code won't ask before using allowed tools.": - 'Qwen Code は許可されたツールを使用する前に確認しません。', - 'Qwen Code will ask before using these tools.': - 'Qwen Code はこれらのツールを使用する前に確認します。', - 'Qwen Code is not allowed to use denied tools.': - 'Qwen Code は拒否されたツールを使用できません。', - 'Manage trusted directories for this workspace.': - 'このワークスペースの信頼済みディレクトリを管理します。', - 'Any use of the {{tool}} tool': '{{tool}} ツールのすべての使用', - "{{tool}} commands matching '{{pattern}}'": - "'{{pattern}}' に一致する {{tool}} コマンド", - 'From user settings': 'ユーザー設定から', - 'From project settings': 'プロジェクト設定から', - 'From session': 'セッションから', - 'Project settings (local)': 'プロジェクト設定(ローカル)', - 'Saved in .qwen/settings.local.json': '.qwen/settings.local.json に保存', - 'Project settings': 'プロジェクト設定', - 'Checked in at .qwen/settings.json': '.qwen/settings.json にチェックイン', - 'User settings': 'ユーザー設定', - 'Saved in at ~/.qwen/settings.json': '~/.qwen/settings.json に保存', - 'Add a new rule…': '新しいルールを追加…', - 'Add {{type}} permission rule': '{{type}}権限ルールを追加', - 'Permission rules are a tool name, optionally followed by a specifier in parentheses.': - '権限ルールはツール名で、オプションで括弧内に指定子を付けます。', - 'e.g.,': '例:', - or: 'または', - 'Enter permission rule…': '権限ルールを入力…', - 'Enter to submit · Esc to cancel': 'Enter で送信 · Esc でキャンセル', - 'Where should this rule be saved?': 'このルールをどこに保存しますか?', - 'Enter to confirm · Esc to cancel': 'Enter で確認 · Esc でキャンセル', - 'Delete {{type}} rule?': '{{type}}ルールを削除しますか?', - 'Are you sure you want to delete this permission rule?': - 'この権限ルールを削除してもよろしいですか?', - 'Permissions:': '権限:', - '(←/→ or tab to cycle)': '(←/→ または Tab で切替)', - 'Press ↑↓ to navigate · Enter to select · Type to search · Esc to cancel': - '↑↓ でナビゲート · Enter で選択 · 入力で検索 · Esc でキャンセル', - 'Search…': '検索…', - 'Use /trust to manage folder trust settings for this workspace.': - '/trust を使用してこのワークスペースのフォルダ信頼設定を管理します。', + "Qwen Code は許可されたツールを使用する前に確認しません。", + "Qwen Code will ask before using these tools.": + "Qwen Code はこれらのツールを使用する前に確認します。", + "Qwen Code is not allowed to use denied tools.": "Qwen Code は拒否されたツールを使用できません。", + "Manage trusted directories for this workspace.": + "このワークスペースの信頼済みディレクトリを管理します。", + "Any use of the {{tool}} tool": "{{tool}} ツールのすべての使用", + "{{tool}} commands matching '{{pattern}}'": "'{{pattern}}' に一致する {{tool}} コマンド", + "From user settings": "ユーザー設定から", + "From project settings": "プロジェクト設定から", + "From session": "セッションから", + "Project settings (local)": "プロジェクト設定(ローカル)", + "Saved in .qwen/settings.local.json": ".qwen/settings.local.json に保存", + "Project settings": "プロジェクト設定", + "Checked in at .qwen/settings.json": ".qwen/settings.json にチェックイン", + "User settings": "ユーザー設定", + "Saved in at ~/.qwen/settings.json": "~/.qwen/settings.json に保存", + "Add a new rule…": "新しいルールを追加…", + "Add {{type}} permission rule": "{{type}}権限ルールを追加", + "Permission rules are a tool name, optionally followed by a specifier in parentheses.": + "権限ルールはツール名で、オプションで括弧内に指定子を付けます。", + "e.g.,": "例:", + or: "または", + "Enter permission rule…": "権限ルールを入力…", + "Enter to submit · Esc to cancel": "Enter で送信 · Esc でキャンセル", + "Where should this rule be saved?": "このルールをどこに保存しますか?", + "Enter to confirm · Esc to cancel": "Enter で確認 · Esc でキャンセル", + "Delete {{type}} rule?": "{{type}}ルールを削除しますか?", + "Are you sure you want to delete this permission rule?": + "この権限ルールを削除してもよろしいですか?", + "Permissions:": "権限:", + "(←/→ or tab to cycle)": "(←/→ または Tab で切替)", + "Press ↑↓ to navigate · Enter to select · Type to search · Esc to cancel": + "↑↓ でナビゲート · Enter で選択 · 入力で検索 · Esc でキャンセル", + "Search…": "検索…", + "Use /trust to manage folder trust settings for this workspace.": + "/trust を使用してこのワークスペースのフォルダ信頼設定を管理します。", // Workspace directory management - 'Add directory…': 'ディレクトリを追加…', - 'Add directory to workspace': 'ワークスペースにディレクトリを追加', - 'Qwen Code can read files in the workspace, and make edits when auto-accept edits is on.': - 'Qwen Code はワークスペース内のファイルを読み取り、自動編集承認が有効な場合は編集を行えます。', - 'Qwen Code will be able to read files in this directory and make edits when auto-accept edits is on.': - 'Qwen Code はこのディレクトリ内のファイルを読み取り、自動編集承認が有効な場合は編集を行えます。', - 'Enter the path to the directory:': 'ディレクトリのパスを入力してください:', - 'Enter directory path…': 'ディレクトリパスを入力…', - 'Tab to complete · Enter to add · Esc to cancel': - 'Tab で補完 · Enter で追加 · Esc でキャンセル', - 'Remove directory?': 'ディレクトリを削除しますか?', - 'Are you sure you want to remove this directory from the workspace?': - 'このディレクトリをワークスペースから削除してもよろしいですか?', - ' (Original working directory)': ' (元の作業ディレクトリ)', - ' (from settings)': ' (設定より)', - 'Directory does not exist.': 'ディレクトリが存在しません。', - 'Path is not a directory.': 'パスはディレクトリではありません。', - 'This directory is already in the workspace.': - 'このディレクトリはすでにワークスペースに含まれています。', - 'Already covered by existing directory: {{dir}}': - '既存のディレクトリによって既にカバーされています: {{dir}}', + "Add directory…": "ディレクトリを追加…", + "Add directory to workspace": "ワークスペースにディレクトリを追加", + "Qwen Code can read files in the workspace, and make edits when auto-accept edits is on.": + "Qwen Code はワークスペース内のファイルを読み取り、自動編集承認が有効な場合は編集を行えます。", + "Qwen Code will be able to read files in this directory and make edits when auto-accept edits is on.": + "Qwen Code はこのディレクトリ内のファイルを読み取り、自動編集承認が有効な場合は編集を行えます。", + "Enter the path to the directory:": "ディレクトリのパスを入力してください:", + "Enter directory path…": "ディレクトリパスを入力…", + "Tab to complete · Enter to add · Esc to cancel": "Tab で補完 · Enter で追加 · Esc でキャンセル", + "Remove directory?": "ディレクトリを削除しますか?", + "Are you sure you want to remove this directory from the workspace?": + "このディレクトリをワークスペースから削除してもよろしいですか?", + " (Original working directory)": " (元の作業ディレクトリ)", + " (from settings)": " (設定より)", + "Directory does not exist.": "ディレクトリが存在しません。", + "Path is not a directory.": "パスはディレクトリではありません。", + "This directory is already in the workspace.": + "このディレクトリはすでにワークスペースに含まれています。", + "Already covered by existing directory: {{dir}}": + "既存のディレクトリによって既にカバーされています: {{dir}}", // Status Bar - 'Using:': '使用中:', - '{{count}} open file': '{{count}} 個のファイルを開いています', - '{{count}} open files': '{{count}} 個のファイルを開いています', - '(ctrl+g to view)': '(Ctrl+G で表示)', - '{{count}} {{name}} file': '{{count}} {{name}} ファイル', - '{{count}} {{name}} files': '{{count}} {{name}} ファイル', - '{{count}} MCP server': '{{count}} MCPサーバー', - '{{count}} MCP servers': '{{count}} MCPサーバー', - '{{count}} Blocked': '{{count}} ブロック', - '(ctrl+t to view)': '(Ctrl+T で表示)', - '(ctrl+t to toggle)': '(Ctrl+T で切り替え)', - 'Press Ctrl+C again to exit.': 'Ctrl+C をもう一度押すと終了します', - 'Press Ctrl+D again to exit.': 'Ctrl+D をもう一度押すと終了します', - 'Press Esc again to clear.': 'Esc をもう一度押すとクリアします', + "Using:": "使用中:", + "{{count}} open file": "{{count}} 個のファイルを開いています", + "{{count}} open files": "{{count}} 個のファイルを開いています", + "(ctrl+g to view)": "(Ctrl+G で表示)", + "{{count}} {{name}} file": "{{count}} {{name}} ファイル", + "{{count}} {{name}} files": "{{count}} {{name}} ファイル", + "{{count}} MCP server": "{{count}} MCPサーバー", + "{{count}} MCP servers": "{{count}} MCPサーバー", + "{{count}} Blocked": "{{count}} ブロック", + "(ctrl+t to view)": "(Ctrl+T で表示)", + "(ctrl+t to toggle)": "(Ctrl+T で切り替え)", + "Press Ctrl+C again to exit.": "Ctrl+C をもう一度押すと終了します", + "Press Ctrl+D again to exit.": "Ctrl+D をもう一度押すと終了します", + "Press Esc again to clear.": "Esc をもう一度押すとクリアします", // MCP Status - '⏳ MCP servers are starting up ({{count}} initializing)...': - '⏳ MCPサーバーを起動中({{count}} 初期化中)...', - 'Note: First startup may take longer. Tool availability will update automatically.': - '注: 初回起動には時間がかかる場合があります。ツールの利用可能状況は自動的に更新されます', - 'Starting... (first startup may take longer)': - '起動中...(初回起動には時間がかかる場合があります)', - '{{count}} prompt': '{{count}} プロンプト', - '{{count}} prompts': '{{count}} プロンプト', - '(from {{extensionName}})': '({{extensionName}} から)', - OAuth: 'OAuth', - 'OAuth expired': 'OAuth 期限切れ', - 'OAuth not authenticated': 'OAuth 未認証', - 'tools and prompts will appear when ready': - 'ツールとプロンプトは準備完了後に表示されます', - '{{count}} tools cached': '{{count}} ツール(キャッシュ済み)', - 'Tools:': 'ツール:', - 'Parameters:': 'パラメータ:', - 'Prompts:': 'プロンプト:', - Blocked: 'ブロック', - '💡 Tips:': '💡 ヒント:', - Use: '使用', - 'to show server and tool descriptions': 'サーバーとツールの説明を表示', - 'to show tool parameter schemas': 'ツールパラメータスキーマを表示', - 'to hide descriptions': '説明を非表示', - 'to authenticate with OAuth-enabled servers': 'OAuth対応サーバーで認証', - Press: '押す', - 'to toggle tool descriptions on/off': 'ツール説明の表示/非表示を切り替え', + "⏳ MCP servers are starting up ({{count}} initializing)...": + "⏳ MCPサーバーを起動中({{count}} 初期化中)...", + "Note: First startup may take longer. Tool availability will update automatically.": + "注: 初回起動には時間がかかる場合があります。ツールの利用可能状況は自動的に更新されます", + "Starting... (first startup may take longer)": + "起動中...(初回起動には時間がかかる場合があります)", + "{{count}} prompt": "{{count}} プロンプト", + "{{count}} prompts": "{{count}} プロンプト", + "(from {{extensionName}})": "({{extensionName}} から)", + OAuth: "OAuth", + "OAuth expired": "OAuth 期限切れ", + "OAuth not authenticated": "OAuth 未認証", + "tools and prompts will appear when ready": "ツールとプロンプトは準備完了後に表示されます", + "{{count}} tools cached": "{{count}} ツール(キャッシュ済み)", + "Tools:": "ツール:", + "Parameters:": "パラメータ:", + "Prompts:": "プロンプト:", + Blocked: "ブロック", + "💡 Tips:": "💡 ヒント:", + Use: "使用", + "to show server and tool descriptions": "サーバーとツールの説明を表示", + "to show tool parameter schemas": "ツールパラメータスキーマを表示", + "to hide descriptions": "説明を非表示", + "to authenticate with OAuth-enabled servers": "OAuth対応サーバーで認証", + Press: "押す", + "to toggle tool descriptions on/off": "ツール説明の表示/非表示を切り替え", "Starting OAuth authentication for MCP server '{{name}}'...": "MCPサーバー '{{name}}' のOAuth認証を開始中...", // Startup Tips - 'Tips:': 'ヒント:', - 'Use /compress when the conversation gets long to summarize history and free up context.': - '会話が長くなったら /compress で履歴を要約し、コンテキストを解放できます。', - 'Start a fresh idea with /clear or /new; the previous session stays available in history.': - '/clear または /new で新しいアイデアを始められます。前のセッションは履歴に残ります。', - 'Use /bug to submit issues to the maintainers when something goes off.': - '問題が発生したら /bug でメンテナーに報告できます。', - 'Switch auth type quickly with /auth.': - '/auth で認証タイプをすばやく切り替えられます。', - 'You can run any shell commands from Qwen Code using ! (e.g. !ls).': - 'Qwen Code から ! を使って任意のシェルコマンドを実行できます(例: !ls)。', - 'Type / to open the command popup; Tab autocompletes slash commands and saved prompts.': - '/ を入力してコマンドポップアップを開きます。Tab でスラッシュコマンドと保存済みプロンプトを補完できます。', - 'You can resume a previous conversation by running qwen --continue or qwen --resume.': - 'qwen --continue または qwen --resume で前の会話を再開できます。', - 'You can switch permission mode quickly with Shift+Tab or /approval-mode.': - 'Shift+Tab または /approval-mode で権限モードをすばやく切り替えられます。', - 'You can switch permission mode quickly with Tab or /approval-mode.': - 'Tab または /approval-mode で権限モードをすばやく切り替えられます。', - 'Try /insight to generate personalized insights from your chat history.': - '/insight でチャット履歴からパーソナライズされたインサイトを生成できます。', - 'Tips for getting started:': '始めるためのヒント:', - '1. Ask questions, edit files, or run commands.': - '1. 質問したり、ファイルを編集したり、コマンドを実行したりできます', - '2. Be specific for the best results.': - '2. 具体的に指示すると最良の結果が得られます', - 'files to customize your interactions with Qwen Code.': - 'Qwen Code との対話をカスタマイズするためのファイル', - 'for more information.': '詳細情報を確認できます', + "Tips:": "ヒント:", + "Use /compress when the conversation gets long to summarize history and free up context.": + "会話が長くなったら /compress で履歴を要約し、コンテキストを解放できます。", + "Start a fresh idea with /clear or /new; the previous session stays available in history.": + "/clear または /new で新しいアイデアを始められます。前のセッションは履歴に残ります。", + "Use /bug to submit issues to the maintainers when something goes off.": + "問題が発生したら /bug でメンテナーに報告できます。", + "Switch auth type quickly with /auth.": "/auth で認証タイプをすばやく切り替えられます。", + "You can run any shell commands from Qwen Code using ! (e.g. !ls).": + "Qwen Code から ! を使って任意のシェルコマンドを実行できます(例: !ls)。", + "Type / to open the command popup; Tab autocompletes slash commands and saved prompts.": + "/ を入力してコマンドポップアップを開きます。Tab でスラッシュコマンドと保存済みプロンプトを補完できます。", + "You can resume a previous conversation by running qwen --continue or qwen --resume.": + "qwen --continue または qwen --resume で前の会話を再開できます。", + "You can switch permission mode quickly with Shift+Tab or /approval-mode.": + "Shift+Tab または /approval-mode で権限モードをすばやく切り替えられます。", + "You can switch permission mode quickly with Tab or /approval-mode.": + "Tab または /approval-mode で権限モードをすばやく切り替えられます。", + "Try /insight to generate personalized insights from your chat history.": + "/insight でチャット履歴からパーソナライズされたインサイトを生成できます。", + "Tips for getting started:": "始めるためのヒント:", + "1. Ask questions, edit files, or run commands.": + "1. 質問したり、ファイルを編集したり、コマンドを実行したりできます", + "2. Be specific for the best results.": "2. 具体的に指示すると最良の結果が得られます", + "files to customize your interactions with Qwen Code.": + "Qwen Code との対話をカスタマイズするためのファイル", + "for more information.": "詳細情報を確認できます", // Exit Screen / Stats - 'Agent powering down. Goodbye!': 'エージェントを終了します。さようなら!', - 'To continue this session, run': 'このセッションを続行するには、次を実行:', - 'Interaction Summary': 'インタラクション概要', - 'Session ID:': 'セッションID:', - 'Tool Calls:': 'ツール呼び出し:', - 'Success Rate:': '成功率:', - 'User Agreement:': 'ユーザー同意:', - reviewed: 'レビュー済み', - 'Code Changes:': 'コード変更:', - Performance: 'パフォーマンス', - 'Wall Time:': '経過時間:', - 'Agent Active:': 'エージェント稼働時間:', - 'API Time:': 'API時間:', - 'Tool Time:': 'ツール時間:', - 'Session Stats': 'セッション統計', - 'Model Usage': 'モデル使用量', - Reqs: 'リクエスト', - 'Input Tokens': '入力トークン', - 'Output Tokens': '出力トークン', - 'Savings Highlight:': '節約ハイライト:', - 'of input tokens were served from the cache, reducing costs.': - '入力トークンがキャッシュから提供され、コストを削減しました', - 'Tip: For a full token breakdown, run `/stats model`.': - 'ヒント: トークンの詳細な内訳は `/stats model` を実行してください', - 'Model Stats For Nerds': 'マニア向けモデル統計', - 'Tool Stats For Nerds': 'マニア向けツール統計', - Metric: 'メトリック', - API: 'API', - Requests: 'リクエスト', - Errors: 'エラー', - 'Avg Latency': '平均レイテンシ', - Tokens: 'トークン', - Total: '合計', - Prompt: 'プロンプト', - Cached: 'キャッシュ', - Thoughts: '思考', - Tool: 'ツール', - Output: '出力', - 'No API calls have been made in this session.': - 'このセッションではAPI呼び出しが行われていません', - 'Tool Name': 'ツール名', - Calls: '呼び出し', - 'Success Rate': '成功率', - 'Avg Duration': '平均時間', - 'User Decision Summary': 'ユーザー決定サマリー', - 'Total Reviewed Suggestions:': '総レビュー提案数:', - ' » Accepted:': ' » 承認:', - ' » Rejected:': ' » 却下:', - ' » Modified:': ' » 変更:', - ' Overall Agreement Rate:': ' 全体承認率:', - 'No tool calls have been made in this session.': - 'このセッションではツール呼び出しが行われていません', - 'Session start time is unavailable, cannot calculate stats.': - 'セッション開始時刻が利用できないため、統計を計算できません', + "Agent powering down. Goodbye!": "エージェントを終了します。さようなら!", + "To continue this session, run": "このセッションを続行するには、次を実行:", + "Interaction Summary": "インタラクション概要", + "Session ID:": "セッションID:", + "Tool Calls:": "ツール呼び出し:", + "Success Rate:": "成功率:", + "User Agreement:": "ユーザー同意:", + reviewed: "レビュー済み", + "Code Changes:": "コード変更:", + Performance: "パフォーマンス", + "Wall Time:": "経過時間:", + "Agent Active:": "エージェント稼働時間:", + "API Time:": "API時間:", + "Tool Time:": "ツール時間:", + "Session Stats": "セッション統計", + "Model Usage": "モデル使用量", + Reqs: "リクエスト", + "Input Tokens": "入力トークン", + "Output Tokens": "出力トークン", + "Savings Highlight:": "節約ハイライト:", + "of input tokens were served from the cache, reducing costs.": + "入力トークンがキャッシュから提供され、コストを削減しました", + "Tip: For a full token breakdown, run `/stats model`.": + "ヒント: トークンの詳細な内訳は `/stats model` を実行してください", + "Model Stats For Nerds": "マニア向けモデル統計", + "Tool Stats For Nerds": "マニア向けツール統計", + Metric: "メトリック", + API: "API", + Requests: "リクエスト", + Errors: "エラー", + "Avg Latency": "平均レイテンシ", + Tokens: "トークン", + Total: "合計", + Prompt: "プロンプト", + Cached: "キャッシュ", + Thoughts: "思考", + Tool: "ツール", + Output: "出力", + "No API calls have been made in this session.": "このセッションではAPI呼び出しが行われていません", + "Tool Name": "ツール名", + Calls: "呼び出し", + "Success Rate": "成功率", + "Avg Duration": "平均時間", + "User Decision Summary": "ユーザー決定サマリー", + "Total Reviewed Suggestions:": "総レビュー提案数:", + " » Accepted:": " » 承認:", + " » Rejected:": " » 却下:", + " » Modified:": " » 変更:", + " Overall Agreement Rate:": " 全体承認率:", + "No tool calls have been made in this session.": + "このセッションではツール呼び出しが行われていません", + "Session start time is unavailable, cannot calculate stats.": + "セッション開始時刻が利用できないため、統計を計算できません", // Loading - 'Waiting for user confirmation...': 'ユーザーの確認を待っています...', - '(esc to cancel, {{time}})': '(Esc でキャンセル、{{time}})', + "Waiting for user confirmation...": "ユーザーの確認を待っています...", + "(esc to cancel, {{time}})": "(Esc でキャンセル、{{time}})", // Witty Loading Phrases WITTY_LOADING_PHRASES: [ - '運任せで検索中...', - '中の人がタイピング中...', - 'ロジックを最適化中...', - '電子の数を確認中...', - '宇宙のバグをチェック中...', - '大量の0と1をコンパイル中...', - 'HDDと思い出をデフラグ中...', - 'ビットをこっそり入れ替え中...', - 'ニューロンの接続を再構築中...', - 'どこかに行ったセミコロンを捜索中...', - 'フラックスキャパシタを調整中...', - 'フォースと交感中...', - 'アルゴリズムをチューニング中...', - '白いウサギを追跡中...', - 'カセットフーフー中...', - 'ローディングメッセージを考え中...', - 'ほぼ完了...多分...', - '最新のミームについて調査中...', - 'この表示を改善するアイデアを思索中...', - 'この問題を考え中...', - 'それはバグでなく誰も知らない新機能だよ', - 'ダイヤルアップ接続音が終わるのを待機中...', - 'コードに油を追加中...', + "運任せで検索中...", + "中の人がタイピング中...", + "ロジックを最適化中...", + "電子の数を確認中...", + "宇宙のバグをチェック中...", + "大量の0と1をコンパイル中...", + "HDDと思い出をデフラグ中...", + "ビットをこっそり入れ替え中...", + "ニューロンの接続を再構築中...", + "どこかに行ったセミコロンを捜索中...", + "フラックスキャパシタを調整中...", + "フォースと交感中...", + "アルゴリズムをチューニング中...", + "白いウサギを追跡中...", + "カセットフーフー中...", + "ローディングメッセージを考え中...", + "ほぼ完了...多分...", + "最新のミームについて調査中...", + "この表示を改善するアイデアを思索中...", + "この問題を考え中...", + "それはバグでなく誰も知らない新機能だよ", + "ダイヤルアップ接続音が終わるのを待機中...", + "コードに油を追加中...", // かなり意訳が入ってるもの - 'イヤホンをほどき中...', - 'カフェインをコードに変換中...', - '天動説を地動説に書き換え中...', - 'プールで時計の完成を待機中...', - '笑撃的な回答を用意中...', - '適切なミームを記述中...', - 'Aボタンを押して次へ...', - 'コードにリックロールを仕込み中...', - 'プログラマーが貧乏なのはキャッシュを使いすぎるから...', - 'プログラマーがダークモードなのはバグを見たくないから...', - 'コードが壊れた?叩けば治るさ', - 'USBの差し込みに挑戦中...', + "イヤホンをほどき中...", + "カフェインをコードに変換中...", + "天動説を地動説に書き換え中...", + "プールで時計の完成を待機中...", + "笑撃的な回答を用意中...", + "適切なミームを記述中...", + "Aボタンを押して次へ...", + "コードにリックロールを仕込み中...", + "プログラマーが貧乏なのはキャッシュを使いすぎるから...", + "プログラマーがダークモードなのはバグを見たくないから...", + "コードが壊れた?叩けば治るさ", + "USBの差し込みに挑戦中...", ], // ============================================================================ // Custom API Key Configuration // ============================================================================ - 'You can configure your API key and models in settings.json': - 'settings.json で API キーとモデルを設定できます', - 'Refer to the documentation for setup instructions': - 'セットアップ手順はドキュメントを参照してください', + "You can configure your API key and models in settings.json": + "settings.json で API キーとモデルを設定できます", + "Refer to the documentation for setup instructions": + "セットアップ手順はドキュメントを参照してください", // ============================================================================ // Coding Plan Authentication // ============================================================================ - 'API key cannot be empty.': 'APIキーは空にできません。', - 'You can get your Coding Plan API key here': - 'Coding Plan APIキーはこちらで取得できます', - 'Coding Plan configuration updated successfully. New models are now available.': - 'Coding Plan の設定が正常に更新されました。新しいモデルが利用可能になりました。', - 'Coding Plan API key not found. Please re-authenticate with Coding Plan.': - 'Coding Plan の API キーが見つかりません。Coding Plan で再認証してください。', - 'Failed to update Coding Plan configuration: {{message}}': - 'Coding Plan の設定更新に失敗しました: {{message}}', + "API key cannot be empty.": "APIキーは空にできません。", + "You can get your Coding Plan API key here": "Coding Plan APIキーはこちらで取得できます", + "Coding Plan configuration updated successfully. New models are now available.": + "Coding Plan の設定が正常に更新されました。新しいモデルが利用可能になりました。", + "Coding Plan API key not found. Please re-authenticate with Coding Plan.": + "Coding Plan の API キーが見つかりません。Coding Plan で再認証してください。", + "Failed to update Coding Plan configuration: {{message}}": + "Coding Plan の設定更新に失敗しました: {{message}}", // ============================================================================ // Auth Dialog - View Titles and Labels // ============================================================================ - 'Coding Plan': 'Coding Plan', + "Coding Plan": "Coding Plan", "Paste your api key of ModelStudio Coding Plan and you're all set!": - 'ModelStudio Coding PlanのAPIキーを貼り付けるだけで準備完了です!', - Custom: 'カスタム', - 'More instructions about configuring `modelProviders` manually.': - '`modelProviders`を手動で設定する方法の詳細はこちら。', - 'Select API-KEY configuration mode:': 'API-KEY設定モードを選択してください:', - '(Press Escape to go back)': '(Escapeキーで戻る)', - '(Press Enter to submit, Escape to cancel)': - '(Enterで送信、Escapeでキャンセル)', - 'More instructions please check:': '詳細な手順はこちらをご確認ください:', - 'Select Region for Coding Plan': 'Coding Planのリージョンを選択', - 'Choose based on where your account is registered': - 'アカウントの登録先に応じて選択してください', - 'Enter Coding Plan API Key': 'Coding Plan APIキーを入力', + "ModelStudio Coding PlanのAPIキーを貼り付けるだけで準備完了です!", + Custom: "カスタム", + "More instructions about configuring `modelProviders` manually.": + "`modelProviders`を手動で設定する方法の詳細はこちら。", + "Select API-KEY configuration mode:": "API-KEY設定モードを選択してください:", + "(Press Escape to go back)": "(Escapeキーで戻る)", + "(Press Enter to submit, Escape to cancel)": "(Enterで送信、Escapeでキャンセル)", + "More instructions please check:": "詳細な手順はこちらをご確認ください:", + "Select Region for Coding Plan": "Coding Planのリージョンを選択", + "Choose based on where your account is registered": "アカウントの登録先に応じて選択してください", + "Enter Coding Plan API Key": "Coding Plan APIキーを入力", // ============================================================================ // Coding Plan International Updates // ============================================================================ - 'New model configurations are available for {{region}}. Update now?': - '{{region}} の新しいモデル設定が利用可能です。今すぐ更新しますか?', + "New model configurations are available for {{region}}. Update now?": + "{{region}} の新しいモデル設定が利用可能です。今すぐ更新しますか?", '{{region}} configuration updated successfully. Model switched to "{{model}}".': '{{region}} の設定が正常に更新されました。モデルが "{{model}}" に切り替わりました。', - 'Authenticated successfully with {{region}}. API key and model configs saved to settings.json (backed up).': - '{{region}} での認証に成功しました。API キーとモデル設定が settings.json に保存されました(バックアップ済み)。', + "Authenticated successfully with {{region}}. API key and model configs saved to settings.json (backed up).": + "{{region}} での認証に成功しました。API キーとモデル設定が settings.json に保存されました(バックアップ済み)。", // ============================================================================ // Context Usage Component // ============================================================================ - 'Context Usage': 'コンテキスト使用量', - 'No API response yet. Send a message to see actual usage.': - 'API応答はありません。メッセージを送信して実際の使用量を確認してください。', - 'Estimated pre-conversation overhead': '推定事前会話オーバーヘッド', - 'Context window': 'コンテキストウィンドウ', - tokens: 'トークン', - Used: '使用済み', - Free: '空き', - 'Autocompact buffer': '自動圧縮バッファ', - 'Usage by category': 'カテゴリ別の使用量', - 'System prompt': 'システムプロンプト', - 'Built-in tools': '組み込みツール', - 'MCP tools': 'MCPツール', - 'Memory files': 'メモリファイル', - Skills: 'スキル', - Messages: 'メッセージ', - 'Show context window usage breakdown.': - 'コンテキストウィンドウの使用状況を表示します。', - 'Run /context detail for per-item breakdown.': - '/context detail を実行すると項目ごとの内訳を表示します。', - active: '有効', - 'body loaded': '本文読み込み済み', - memory: 'メモリ', - '{{region}} configuration updated successfully.': - '{{region}} の設定が正常に更新されました。', - 'Authenticated successfully with {{region}}. API key and model configs saved to settings.json.': - '{{region}} での認証に成功しました。APIキーとモデル設定が settings.json に保存されました。', - 'Tip: Use /model to switch between available Coding Plan models.': - 'ヒント: /model で利用可能な Coding Plan モデルを切り替えられます。', + "Context Usage": "コンテキスト使用量", + "No API response yet. Send a message to see actual usage.": + "API応答はありません。メッセージを送信して実際の使用量を確認してください。", + "Estimated pre-conversation overhead": "推定事前会話オーバーヘッド", + "Context window": "コンテキストウィンドウ", + tokens: "トークン", + Used: "使用済み", + Free: "空き", + "Autocompact buffer": "自動圧縮バッファ", + "Usage by category": "カテゴリ別の使用量", + "System prompt": "システムプロンプト", + "Built-in tools": "組み込みツール", + "MCP tools": "MCPツール", + "Memory files": "メモリファイル", + Skills: "スキル", + Messages: "メッセージ", + "Show context window usage breakdown.": "コンテキストウィンドウの使用状況を表示します。", + "Run /context detail for per-item breakdown.": + "/context detail を実行すると項目ごとの内訳を表示します。", + active: "有効", + "body loaded": "本文読み込み済み", + memory: "メモリ", + "{{region}} configuration updated successfully.": "{{region}} の設定が正常に更新されました。", + "Authenticated successfully with {{region}}. API key and model configs saved to settings.json.": + "{{region}} での認証に成功しました。APIキーとモデル設定が settings.json に保存されました。", + "Tip: Use /model to switch between available Coding Plan models.": + "ヒント: /model で利用可能な Coding Plan モデルを切り替えられます。", // ============================================================================ // Ask User Question Tool // ============================================================================ - 'Please answer the following question(s):': '以下の質問に答えてください:', - 'Cannot ask user questions in non-interactive mode. Please run in interactive mode to use this tool.': - '非対話モードではユーザーに質問できません。このツールを使用するには対話モードで実行してください。', - 'User declined to answer the questions.': - 'ユーザーは質問への回答を拒否しました。', - 'User has provided the following answers:': - 'ユーザーは以下の回答を提供しました:', - 'Failed to process user answers:': 'ユーザー回答の処理に失敗しました:', - 'Type something...': '何か入力...', - Submit: '送信', - 'Submit answers': '回答を送信', - Cancel: 'キャンセル', - 'Your answers:': 'あなたの回答:', - '(not answered)': '(未回答)', - 'Ready to submit your answers?': '回答を送信しますか?', - '↑/↓: Navigate | ←/→: Switch tabs | Enter: Select': - '↑/↓: ナビゲート | ←/→: タブ切り替え | Enter: 選択', - '↑/↓: Navigate | ←/→: Switch tabs | Space/Enter: Toggle | Esc: Cancel': - '↑/↓: ナビゲート | ←/→: タブ切り替え | Space/Enter: 切り替え | Esc: キャンセル', - '↑/↓: Navigate | Space/Enter: Toggle | Esc: Cancel': - '↑/↓: ナビゲート | Space/Enter: 切り替え | Esc: キャンセル', - '↑/↓: Navigate | Enter: Select | Esc: Cancel': - '↑/↓: ナビゲート | Enter: 選択 | Esc: キャンセル', + "Please answer the following question(s):": "以下の質問に答えてください:", + "Cannot ask user questions in non-interactive mode. Please run in interactive mode to use this tool.": + "非対話モードではユーザーに質問できません。このツールを使用するには対話モードで実行してください。", + "User declined to answer the questions.": "ユーザーは質問への回答を拒否しました。", + "User has provided the following answers:": "ユーザーは以下の回答を提供しました:", + "Failed to process user answers:": "ユーザー回答の処理に失敗しました:", + "Type something...": "何か入力...", + Submit: "送信", + "Submit answers": "回答を送信", + Cancel: "キャンセル", + "Your answers:": "あなたの回答:", + "(not answered)": "(未回答)", + "Ready to submit your answers?": "回答を送信しますか?", + "↑/↓: Navigate | ←/→: Switch tabs | Enter: Select": + "↑/↓: ナビゲート | ←/→: タブ切り替え | Enter: 選択", + "↑/↓: Navigate | ←/→: Switch tabs | Space/Enter: Toggle | Esc: Cancel": + "↑/↓: ナビゲート | ←/→: タブ切り替え | Space/Enter: 切り替え | Esc: キャンセル", + "↑/↓: Navigate | Space/Enter: Toggle | Esc: Cancel": + "↑/↓: ナビゲート | Space/Enter: 切り替え | Esc: キャンセル", + "↑/↓: Navigate | Enter: Select | Esc: Cancel": "↑/↓: ナビゲート | Enter: 選択 | Esc: キャンセル", // ============================================================================ // Commands - Auth // ============================================================================ - 'Authenticate using Alibaba Cloud Coding Plan': - 'Alibaba Cloud Coding Plan で認証する', - 'Region for Coding Plan (china/global)': - 'Coding Plan のリージョン (china/global)', - 'API key for Coding Plan': 'Coding Plan の API キー', - 'Show current authentication status': '現在の認証ステータスを表示', - 'Authentication completed successfully.': '認証が正常に完了しました。', - 'Processing Alibaba Cloud Coding Plan authentication...': - 'Alibaba Cloud Coding Plan 認証を処理しています...', - 'Successfully authenticated with Alibaba Cloud Coding Plan.': - 'Alibaba Cloud Coding Plan での認証に成功しました。', - 'Failed to authenticate with Coding Plan: {{error}}': - 'Coding Plan での認証に失敗しました: {{error}}', - '中国 (China)': '中国 (China)', - '阿里云百炼 (aliyun.com)': '阿里云百炼 (aliyun.com)', - Global: 'グローバル', - 'Alibaba Cloud (alibabacloud.com)': 'Alibaba Cloud (alibabacloud.com)', - 'Select region for Coding Plan:': 'Coding Plan のリージョンを選択:', - 'Enter your Coding Plan API key: ': - 'Coding Plan の API キーを入力してください: ', - 'Select authentication method:': '認証方法を選択:', - '\n=== Authentication Status ===\n': '\n=== 認証ステータス ===\n', - '⚠️ No authentication method configured.\n': - '⚠️ 認証方法が設定されていません。\n', - 'Run one of the following commands to get started:\n': - '以下のコマンドのいずれかを実行して開始してください:\n', - ' qwen auth coding-plan - Authenticate with Alibaba Cloud Coding Plan\n': - ' qwen auth coding-plan - Alibaba Cloud Coding Plan で認証\n', - 'Or simply run:': 'または以下を実行:', - ' qwen auth - Interactive authentication setup\n': - ' qwen auth - インタラクティブ認証セットアップ\n', - '✓ Authentication Method: Alibaba Cloud Coding Plan': - '✓ 認証方法: Alibaba Cloud Coding Plan', - '中国 (China) - 阿里云百炼': '中国 (China) - 阿里云百炼', - 'Global - Alibaba Cloud': 'グローバル - Alibaba Cloud', - ' Region: {{region}}': ' リージョン: {{region}}', - ' Current Model: {{model}}': ' 現在のモデル: {{model}}', - ' Config Version: {{version}}': ' 設定バージョン: {{version}}', - ' Status: API key configured\n': ' ステータス: APIキー設定済み\n', - '⚠️ Authentication Method: Alibaba Cloud Coding Plan (Incomplete)': - '⚠️ 認証方法: Alibaba Cloud Coding Plan(不完全)', - ' Issue: API key not found in environment or settings\n': - ' 問題: 環境変数または設定にAPIキーが見つかりません\n', - ' Run `qwen auth coding-plan` to re-configure.\n': - ' `qwen auth coding-plan` を実行して再設定してください。\n', - '✓ Authentication Method: {{type}}': '✓ 認証方法: {{type}}', - ' Status: Configured\n': ' ステータス: 設定済み\n', - 'Failed to check authentication status: {{error}}': - '認証ステータスの確認に失敗しました: {{error}}', - 'Select an option:': 'オプションを選択:', - 'Raw mode not available. Please run in an interactive terminal.': - 'Rawモードが利用できません。インタラクティブターミナルで実行してください。', - '(Use ↑ ↓ arrows to navigate, Enter to select, Ctrl+C to exit)\n': - '(↑ ↓ 矢印キーで移動、Enter で選択、Ctrl+C で終了)\n', - verbose: '詳細', - 'Show full tool output and thinking in verbose mode (toggle with Ctrl+O).': - '詳細モードで完全なツール出力と思考を表示します(Ctrl+O で切り替え)。', - 'Press Ctrl+O to show full tool output': 'Ctrl+O で完全なツール出力を表示', + "Authenticate using Alibaba Cloud Coding Plan": "Alibaba Cloud Coding Plan で認証する", + "Region for Coding Plan (china/global)": "Coding Plan のリージョン (china/global)", + "API key for Coding Plan": "Coding Plan の API キー", + "Show current authentication status": "現在の認証ステータスを表示", + "Authentication completed successfully.": "認証が正常に完了しました。", + "Processing Alibaba Cloud Coding Plan authentication...": + "Alibaba Cloud Coding Plan 認証を処理しています...", + "Successfully authenticated with Alibaba Cloud Coding Plan.": + "Alibaba Cloud Coding Plan での認証に成功しました。", + "Failed to authenticate with Coding Plan: {{error}}": + "Coding Plan での認証に失敗しました: {{error}}", + "中国 (China)": "中国 (China)", + "阿里云百炼 (aliyun.com)": "阿里云百炼 (aliyun.com)", + Global: "グローバル", + "Alibaba Cloud (alibabacloud.com)": "Alibaba Cloud (alibabacloud.com)", + "Select region for Coding Plan:": "Coding Plan のリージョンを選択:", + "Enter your Coding Plan API key: ": "Coding Plan の API キーを入力してください: ", + "Select authentication method:": "認証方法を選択:", + "\n=== Authentication Status ===\n": "\n=== 認証ステータス ===\n", + "⚠️ No authentication method configured.\n": "⚠️ 認証方法が設定されていません。\n", + "Run one of the following commands to get started:\n": + "以下のコマンドのいずれかを実行して開始してください:\n", + " qwen auth coding-plan - Authenticate with Alibaba Cloud Coding Plan\n": + " qwen auth coding-plan - Alibaba Cloud Coding Plan で認証\n", + "Or simply run:": "または以下を実行:", + " qwen auth - Interactive authentication setup\n": + " qwen auth - インタラクティブ認証セットアップ\n", + "✓ Authentication Method: Alibaba Cloud Coding Plan": "✓ 認証方法: Alibaba Cloud Coding Plan", + "中国 (China) - 阿里云百炼": "中国 (China) - 阿里云百炼", + "Global - Alibaba Cloud": "グローバル - Alibaba Cloud", + " Region: {{region}}": " リージョン: {{region}}", + " Current Model: {{model}}": " 現在のモデル: {{model}}", + " Config Version: {{version}}": " 設定バージョン: {{version}}", + " Status: API key configured\n": " ステータス: APIキー設定済み\n", + "⚠️ Authentication Method: Alibaba Cloud Coding Plan (Incomplete)": + "⚠️ 認証方法: Alibaba Cloud Coding Plan(不完全)", + " Issue: API key not found in environment or settings\n": + " 問題: 環境変数または設定にAPIキーが見つかりません\n", + " Run `qwen auth coding-plan` to re-configure.\n": + " `qwen auth coding-plan` を実行して再設定してください。\n", + "✓ Authentication Method: {{type}}": "✓ 認証方法: {{type}}", + " Status: Configured\n": " ステータス: 設定済み\n", + "Failed to check authentication status: {{error}}": + "認証ステータスの確認に失敗しました: {{error}}", + "Select an option:": "オプションを選択:", + "Raw mode not available. Please run in an interactive terminal.": + "Rawモードが利用できません。インタラクティブターミナルで実行してください。", + "(Use ↑ ↓ arrows to navigate, Enter to select, Ctrl+C to exit)\n": + "(↑ ↓ 矢印キーで移動、Enter で選択、Ctrl+C で終了)\n", + verbose: "詳細", + "Show full tool output and thinking in verbose mode (toggle with Ctrl+O).": + "詳細モードで完全なツール出力と思考を表示します(Ctrl+O で切り替え)。", + "Press Ctrl+O to show full tool output": "Ctrl+O で完全なツール出力を表示", - 'Switch to plan mode or exit plan mode': - 'Switch to plan mode or exit plan mode', - 'Exited plan mode. Previous approval mode restored.': - 'Exited plan mode. Previous approval mode restored.', - 'Enabled plan mode. The agent will analyze and plan without executing tools.': - 'Enabled plan mode. The agent will analyze and plan without executing tools.', + "Switch to plan mode or exit plan mode": "Switch to plan mode or exit plan mode", + "Exited plan mode. Previous approval mode restored.": + "Exited plan mode. Previous approval mode restored.", + "Enabled plan mode. The agent will analyze and plan without executing tools.": + "Enabled plan mode. The agent will analyze and plan without executing tools.", 'Already in plan mode. Use "/plan exit" to exit plan mode.': 'Already in plan mode. Use "/plan exit" to exit plan mode.', 'Not in plan mode. Use "/plan" to enter plan mode first.': diff --git a/apps/airiscode-cli/src/i18n/locales/pt.js b/apps/airiscode-cli/src/i18n/locales/pt.js index 3f72b072b..c523ae839 100644 --- a/apps/airiscode-cli/src/i18n/locales/pt.js +++ b/apps/airiscode-cli/src/i18n/locales/pt.js @@ -10,1194 +10,1103 @@ export default { // ============================================================================ // Help / UI Components // ============================================================================ - 'Basics:': 'Noções básicas:', - 'Add context': 'Adicionar contexto', - 'Use {{symbol}} to specify files for context (e.g., {{example}}) to target specific files or folders.': - 'Use {{symbol}} para especificar arquivos para o contexto (ex: {{example}}) para atingir arquivos ou pastas específicos.', - '@': '@', - '@src/myFile.ts': '@src/myFile.ts', - 'Shell mode': 'Modo shell', - 'YOLO mode': 'Modo YOLO', - 'plan mode': 'modo planejamento', - 'auto-accept edits': 'aceitar edições automaticamente', - 'Accepting edits': 'Aceitando edições', - '(shift + tab to cycle)': '(shift + tab para alternar)', - 'Execute shell commands via {{symbol}} (e.g., {{example1}}) or use natural language (e.g., {{example2}}).': - 'Execute comandos shell via {{symbol}} (ex: {{example1}}) ou use linguagem natural (ex: {{example2}}).', - '!': '!', - '!npm run start': '!npm run start', - 'start server': 'iniciar servidor', - 'Commands:': 'Comandos:', - 'shell command': 'comando shell', - 'Model Context Protocol command (from external servers)': - 'Comando Model Context Protocol (de servidores externos)', - 'Keyboard Shortcuts:': 'Atalhos de teclado:', - 'Toggle this help display': 'Alternar exibição desta ajuda', - 'Toggle shell mode': 'Alternar modo shell', - 'Open command menu': 'Abrir menu de comandos', - 'Add file context': 'Adicionar contexto de arquivo', - 'Accept suggestion / Autocomplete': 'Aceitar sugestão / Autocompletar', - 'Reverse search history': 'Pesquisa reversa no histórico', - 'Press ? again to close': 'Pressione ? novamente para fechar', + "Basics:": "Noções básicas:", + "Add context": "Adicionar contexto", + "Use {{symbol}} to specify files for context (e.g., {{example}}) to target specific files or folders.": + "Use {{symbol}} para especificar arquivos para o contexto (ex: {{example}}) para atingir arquivos ou pastas específicos.", + "@": "@", + "@src/myFile.ts": "@src/myFile.ts", + "Shell mode": "Modo shell", + "YOLO mode": "Modo YOLO", + "plan mode": "modo planejamento", + "auto-accept edits": "aceitar edições automaticamente", + "Accepting edits": "Aceitando edições", + "(shift + tab to cycle)": "(shift + tab para alternar)", + "Execute shell commands via {{symbol}} (e.g., {{example1}}) or use natural language (e.g., {{example2}}).": + "Execute comandos shell via {{symbol}} (ex: {{example1}}) ou use linguagem natural (ex: {{example2}}).", + "!": "!", + "!npm run start": "!npm run start", + "start server": "iniciar servidor", + "Commands:": "Comandos:", + "shell command": "comando shell", + "Model Context Protocol command (from external servers)": + "Comando Model Context Protocol (de servidores externos)", + "Keyboard Shortcuts:": "Atalhos de teclado:", + "Toggle this help display": "Alternar exibição desta ajuda", + "Toggle shell mode": "Alternar modo shell", + "Open command menu": "Abrir menu de comandos", + "Add file context": "Adicionar contexto de arquivo", + "Accept suggestion / Autocomplete": "Aceitar sugestão / Autocompletar", + "Reverse search history": "Pesquisa reversa no histórico", + "Press ? again to close": "Pressione ? novamente para fechar", // Keyboard shortcuts panel descriptions - 'for shell mode': 'para modo shell', - 'for commands': 'para comandos', - 'for file paths': 'para caminhos de arquivo', - 'to clear input': 'para limpar entrada', - 'to cycle approvals': 'para alternar aprovações', - 'to quit': 'para sair', - 'for newline': 'para nova linha', - 'to clear screen': 'para limpar a tela', - 'to search history': 'para pesquisar no histórico', - 'to paste images': 'para colar imagens', - 'for external editor': 'para editor externo', - 'Jump through words in the input': 'Pular palavras na entrada', - 'Close dialogs, cancel requests, or quit application': - 'Fechar diálogos, cancelar solicitações ou sair do aplicativo', - 'New line': 'Nova linha', - 'New line (Alt+Enter works for certain linux distros)': - 'Nova linha (Alt+Enter funciona em certas distros linux)', - 'Clear the screen': 'Limpar a tela', - 'Open input in external editor': 'Abrir entrada no editor externo', - 'Send message': 'Enviar mensagem', - 'Initializing...': 'Inicializando...', - 'Connecting to MCP servers... ({{connected}}/{{total}})': - 'Conectando aos servidores MCP... ({{connected}}/{{total}})', - 'Type your message or @path/to/file': - 'Digite sua mensagem ou @caminho/do/arquivo', - '? for shortcuts': '? para atalhos', + "for shell mode": "para modo shell", + "for commands": "para comandos", + "for file paths": "para caminhos de arquivo", + "to clear input": "para limpar entrada", + "to cycle approvals": "para alternar aprovações", + "to quit": "para sair", + "for newline": "para nova linha", + "to clear screen": "para limpar a tela", + "to search history": "para pesquisar no histórico", + "to paste images": "para colar imagens", + "for external editor": "para editor externo", + "Jump through words in the input": "Pular palavras na entrada", + "Close dialogs, cancel requests, or quit application": + "Fechar diálogos, cancelar solicitações ou sair do aplicativo", + "New line": "Nova linha", + "New line (Alt+Enter works for certain linux distros)": + "Nova linha (Alt+Enter funciona em certas distros linux)", + "Clear the screen": "Limpar a tela", + "Open input in external editor": "Abrir entrada no editor externo", + "Send message": "Enviar mensagem", + "Initializing...": "Inicializando...", + "Connecting to MCP servers... ({{connected}}/{{total}})": + "Conectando aos servidores MCP... ({{connected}}/{{total}})", + "Type your message or @path/to/file": "Digite sua mensagem ou @caminho/do/arquivo", + "? for shortcuts": "? para atalhos", "Press 'i' for INSERT mode and 'Esc' for NORMAL mode.": "Pressione 'i' para modo INSERÇÃO e 'Esc' para modo NORMAL.", - 'Cancel operation / Clear input (double press)': - 'Cancelar operação / Limpar entrada (pressionar duas vezes)', - 'Cycle approval modes': 'Alternar modos de aprovação', - 'Cycle through your prompt history': 'Alternar histórico de prompts', - 'For a full list of shortcuts, see {{docPath}}': - 'Para uma lista completa de atalhos, consulte {{docPath}}', - 'docs/keyboard-shortcuts.md': 'docs/keyboard-shortcuts.md', - 'for help on Qwen Code': 'para ajuda sobre o Qwen Code', - 'show version info': 'mostrar informações de versão', - 'submit a bug report': 'enviar um relatório de erro', - 'About Qwen Code': 'Sobre o Qwen Code', - Status: 'Status', + "Cancel operation / Clear input (double press)": + "Cancelar operação / Limpar entrada (pressionar duas vezes)", + "Cycle approval modes": "Alternar modos de aprovação", + "Cycle through your prompt history": "Alternar histórico de prompts", + "For a full list of shortcuts, see {{docPath}}": + "Para uma lista completa de atalhos, consulte {{docPath}}", + "docs/keyboard-shortcuts.md": "docs/keyboard-shortcuts.md", + "for help on Qwen Code": "para ajuda sobre o Qwen Code", + "show version info": "mostrar informações de versão", + "submit a bug report": "enviar um relatório de erro", + "About Qwen Code": "Sobre o Qwen Code", + Status: "Status", // ============================================================================ // System Information Fields // ============================================================================ - 'Qwen Code': 'Qwen Code', - Runtime: 'Runtime', - OS: 'SO', - Auth: 'Autenticação', - 'CLI Version': 'Versão da CLI', - 'Git Commit': 'Commit do Git', - Model: 'Modelo', - 'Fast Model': 'Modelo Rápido', - Sandbox: 'Sandbox', - 'OS Platform': 'Plataforma do SO', - 'OS Arch': 'Arquitetura do SO', - 'OS Release': 'Versão do SO', - 'Node.js Version': 'Versão do Node.js', - 'NPM Version': 'Versão do NPM', - 'Session ID': 'ID da Sessão', - 'Auth Method': 'Método de Autenticação', - 'Base URL': 'URL Base', - Proxy: 'Proxy', - 'Memory Usage': 'Uso de Memória', - 'IDE Client': 'Cliente IDE', + "Qwen Code": "Qwen Code", + Runtime: "Runtime", + OS: "SO", + Auth: "Autenticação", + "CLI Version": "Versão da CLI", + "Git Commit": "Commit do Git", + Model: "Modelo", + "Fast Model": "Modelo Rápido", + Sandbox: "Sandbox", + "OS Platform": "Plataforma do SO", + "OS Arch": "Arquitetura do SO", + "OS Release": "Versão do SO", + "Node.js Version": "Versão do Node.js", + "NPM Version": "Versão do NPM", + "Session ID": "ID da Sessão", + "Auth Method": "Método de Autenticação", + "Base URL": "URL Base", + Proxy: "Proxy", + "Memory Usage": "Uso de Memória", + "IDE Client": "Cliente IDE", // ============================================================================ // Commands - General // ============================================================================ - 'Analyzes the project and creates a tailored QWEN.md file.': - 'Analisa o projeto e cria um arquivo QWEN.md personalizado.', - 'List available Qwen Code tools. Usage: /tools [desc]': - 'Listar ferramentas Qwen Code disponíveis. Uso: /tools [desc]', - 'List available skills.': 'Listar habilidades disponíveis.', - 'Available Qwen Code CLI tools:': 'Ferramentas CLI do Qwen Code disponíveis:', - 'No tools available': 'Nenhuma ferramenta disponível', - 'View or change the approval mode for tool usage': - 'Ver ou alterar o modo de aprovação para uso de ferramentas', + "Analyzes the project and creates a tailored QWEN.md file.": + "Analisa o projeto e cria um arquivo QWEN.md personalizado.", + "List available Qwen Code tools. Usage: /tools [desc]": + "Listar ferramentas Qwen Code disponíveis. Uso: /tools [desc]", + "List available skills.": "Listar habilidades disponíveis.", + "Available Qwen Code CLI tools:": "Ferramentas CLI do Qwen Code disponíveis:", + "No tools available": "Nenhuma ferramenta disponível", + "View or change the approval mode for tool usage": + "Ver ou alterar o modo de aprovação para uso de ferramentas", 'Invalid approval mode "{{arg}}". Valid modes: {{modes}}': 'Modo de aprovação inválido "{{arg}}". Modos válidos: {{modes}}', - 'Approval mode set to "{{mode}}"': - 'Modo de aprovação definido como "{{mode}}"', - 'View or change the language setting': - 'Ver ou alterar a configuração de idioma', - 'change the theme': 'alterar o tema', - 'Select Theme': 'Selecionar Tema', - Preview: 'Visualizar', - '(Use Enter to select, Tab to configure scope)': - '(Use Enter para selecionar, Tab para configurar o escopo)', - '(Use Enter to apply scope, Tab to go back)': - '(Use Enter para aplicar o escopo, Tab para voltar)', - 'Theme configuration unavailable due to NO_COLOR env variable.': - 'Configuração de tema indisponível devido à variável de ambiente NO_COLOR.', + 'Approval mode set to "{{mode}}"': 'Modo de aprovação definido como "{{mode}}"', + "View or change the language setting": "Ver ou alterar a configuração de idioma", + "change the theme": "alterar o tema", + "Select Theme": "Selecionar Tema", + Preview: "Visualizar", + "(Use Enter to select, Tab to configure scope)": + "(Use Enter para selecionar, Tab para configurar o escopo)", + "(Use Enter to apply scope, Tab to go back)": + "(Use Enter para aplicar o escopo, Tab para voltar)", + "Theme configuration unavailable due to NO_COLOR env variable.": + "Configuração de tema indisponível devido à variável de ambiente NO_COLOR.", 'Theme "{{themeName}}" not found.': 'Tema "{{themeName}}" não encontrado.', 'Theme "{{themeName}}" not found in selected scope.': 'Tema "{{themeName}}" não encontrado no escopo selecionado.', - 'Clear conversation history and free up context': - 'Limpar histórico de conversa e liberar contexto', - 'Compresses the context by replacing it with a summary.': - 'Comprime o contexto substituindo-o por um resumo.', - 'open full Qwen Code documentation in your browser': - 'abrir documentação completa do Qwen Code no seu navegador', - 'Configuration not available.': 'Configuração não disponível.', - 'change the auth method': 'alterar o método de autenticação', - 'Configure authentication information for login': - 'Configurar informações de autenticação para login', - 'Copy the last result or code snippet to clipboard': - 'Copiar o último resultado ou trecho de código para a área de transferência', + "Clear conversation history and free up context": + "Limpar histórico de conversa e liberar contexto", + "Compresses the context by replacing it with a summary.": + "Comprime o contexto substituindo-o por um resumo.", + "open full Qwen Code documentation in your browser": + "abrir documentação completa do Qwen Code no seu navegador", + "Configuration not available.": "Configuração não disponível.", + "change the auth method": "alterar o método de autenticação", + "Configure authentication information for login": + "Configurar informações de autenticação para login", + "Copy the last result or code snippet to clipboard": + "Copiar o último resultado ou trecho de código para a área de transferência", // ============================================================================ // Commands - Agents // ============================================================================ - 'Manage subagents for specialized task delegation.': - 'Gerenciar subagentes para delegação de tarefas especializadas.', - 'Manage existing subagents (view, edit, delete).': - 'Gerenciar subagentes existentes (ver, editar, excluir).', - 'Create a new subagent with guided setup.': - 'Criar um novo subagente com configuração guiada.', + "Manage subagents for specialized task delegation.": + "Gerenciar subagentes para delegação de tarefas especializadas.", + "Manage existing subagents (view, edit, delete).": + "Gerenciar subagentes existentes (ver, editar, excluir).", + "Create a new subagent with guided setup.": "Criar um novo subagente com configuração guiada.", // ============================================================================ // Agents - Management Dialog // ============================================================================ - Agents: 'Agentes', - 'Choose Action': 'Escolher Ação', - 'Edit {{name}}': 'Editar {{name}}', - 'Edit Tools: {{name}}': 'Editar Ferramentas: {{name}}', - 'Edit Color: {{name}}': 'Editar Cor: {{name}}', - 'Delete {{name}}': 'Excluir {{name}}', - 'Unknown Step': 'Etapa Desconhecida', - 'Esc to close': 'Esc para fechar', - 'Enter to select, ↑↓ to navigate, Esc to close': - 'Enter para selecionar, ↑↓ para navegar, Esc para fechar', - 'Esc to go back': 'Esc para voltar', - 'Enter to confirm, Esc to cancel': 'Enter para confirmar, Esc para cancelar', - 'Enter to select, ↑↓ to navigate, Esc to go back': - 'Enter para selecionar, ↑↓ para navegar, Esc para voltar', - 'Enter to submit, Esc to go back': 'Enter para enviar, Esc para voltar', - 'Invalid step: {{step}}': 'Etapa inválida: {{step}}', - 'No subagents found.': 'Nenhum subagente encontrado.', + Agents: "Agentes", + "Choose Action": "Escolher Ação", + "Edit {{name}}": "Editar {{name}}", + "Edit Tools: {{name}}": "Editar Ferramentas: {{name}}", + "Edit Color: {{name}}": "Editar Cor: {{name}}", + "Delete {{name}}": "Excluir {{name}}", + "Unknown Step": "Etapa Desconhecida", + "Esc to close": "Esc para fechar", + "Enter to select, ↑↓ to navigate, Esc to close": + "Enter para selecionar, ↑↓ para navegar, Esc para fechar", + "Esc to go back": "Esc para voltar", + "Enter to confirm, Esc to cancel": "Enter para confirmar, Esc para cancelar", + "Enter to select, ↑↓ to navigate, Esc to go back": + "Enter para selecionar, ↑↓ para navegar, Esc para voltar", + "Enter to submit, Esc to go back": "Enter para enviar, Esc para voltar", + "Invalid step: {{step}}": "Etapa inválida: {{step}}", + "No subagents found.": "Nenhum subagente encontrado.", "Use '/agents create' to create your first subagent.": "Use '/agents create' para criar seu primeiro subagente.", - '(built-in)': '(integrado)', - '(overridden by project level agent)': - '(substituído por agente de nível de projeto)', - 'Project Level ({{path}})': 'Nível de Projeto ({{path}})', - 'User Level ({{path}})': 'Nível de Usuário ({{path}})', - 'Built-in Agents': 'Agentes Integrados', - 'Extension Agents': 'Agentes de Extensão', - 'Using: {{count}} agents': 'Usando: {{count}} agentes', - 'View Agent': 'Ver Agente', - 'Edit Agent': 'Editar Agente', - 'Delete Agent': 'Excluir Agente', - Back: 'Voltar', - 'No agent selected': 'Nenhum agente selecionado', - 'File Path: ': 'Caminho do Arquivo: ', - 'Tools: ': 'Ferramentas: ', - 'Color: ': 'Cor: ', - 'Description:': 'Descrição:', - 'System Prompt:': 'Prompt do Sistema:', - 'Open in editor': 'Abrir no editor', - 'Edit tools': 'Editar ferramentas', - 'Edit color': 'Editar cor', - '❌ Error:': '❌ Erro:', + "(built-in)": "(integrado)", + "(overridden by project level agent)": "(substituído por agente de nível de projeto)", + "Project Level ({{path}})": "Nível de Projeto ({{path}})", + "User Level ({{path}})": "Nível de Usuário ({{path}})", + "Built-in Agents": "Agentes Integrados", + "Extension Agents": "Agentes de Extensão", + "Using: {{count}} agents": "Usando: {{count}} agentes", + "View Agent": "Ver Agente", + "Edit Agent": "Editar Agente", + "Delete Agent": "Excluir Agente", + Back: "Voltar", + "No agent selected": "Nenhum agente selecionado", + "File Path: ": "Caminho do Arquivo: ", + "Tools: ": "Ferramentas: ", + "Color: ": "Cor: ", + "Description:": "Descrição:", + "System Prompt:": "Prompt do Sistema:", + "Open in editor": "Abrir no editor", + "Edit tools": "Editar ferramentas", + "Edit color": "Editar cor", + "❌ Error:": "❌ Erro:", 'Are you sure you want to delete agent "{{name}}"?': 'Tem certeza que deseja excluir o agente "{{name}}"?', // ============================================================================ // Agents - Creation Wizard // ============================================================================ - 'Project Level (.qwen/agents/)': 'Nível de Projeto (.qwen/agents/)', - 'User Level (~/.qwen/agents/)': 'Nível de Usuário (~/.qwen/agents/)', - '✅ Subagent Created Successfully!': '✅ Subagente criado com sucesso!', + "Project Level (.qwen/agents/)": "Nível de Projeto (.qwen/agents/)", + "User Level (~/.qwen/agents/)": "Nível de Usuário (~/.qwen/agents/)", + "✅ Subagent Created Successfully!": "✅ Subagente criado com sucesso!", 'Subagent "{{name}}" has been saved to {{level}} level.': 'O subagente "{{name}}" foi salvo no nível {{level}}.', - 'Name: ': 'Nome: ', - 'Location: ': 'Localização: ', - '❌ Error saving subagent:': '❌ Erro ao salvar subagente:', - 'Warnings:': 'Avisos:', + "Name: ": "Nome: ", + "Location: ": "Localização: ", + "❌ Error saving subagent:": "❌ Erro ao salvar subagente:", + "Warnings:": "Avisos:", 'Name "{{name}}" already exists at {{level}} level - will overwrite existing subagent': 'O nome "{{name}}" já existe no nível {{level}} - o subagente existente será substituído', 'Name "{{name}}" exists at user level - project level will take precedence': 'O nome "{{name}}" existe no nível de usuário - o nível de projeto terá precedência', 'Name "{{name}}" exists at project level - existing subagent will take precedence': 'O nome "{{name}}" existe no nível de projeto - o subagente existente terá precedência', - 'Description is over {{length}} characters': - 'A descrição tem mais de {{length}} caracteres', - 'System prompt is over {{length}} characters': - 'O prompt do sistema tem mais de {{length}} caracteres', + "Description is over {{length}} characters": "A descrição tem mais de {{length}} caracteres", + "System prompt is over {{length}} characters": + "O prompt do sistema tem mais de {{length}} caracteres", // ============================================================================ // Agents - Creation Wizard Steps // ============================================================================ - 'Step {{n}}: Choose Location': 'Etapa {{n}}: Escolher Localização', - 'Step {{n}}: Choose Generation Method': - 'Etapa {{n}}: Escolher Método de Geração', - 'Generate with Qwen Code (Recommended)': 'Gerar com Qwen Code (Recomendado)', - 'Manual Creation': 'Criação Manual', - 'Describe what this subagent should do and when it should be used. (Be comprehensive for best results)': - 'Descreva o que este subagente deve fazer e quando deve ser usado. (Seja abrangente para melhores resultados)', - 'e.g., Expert code reviewer that reviews code based on best practices...': - 'ex: Revisor de código especialista que revisa código com base em melhores práticas...', - 'Generating subagent configuration...': - 'Gerando configuração do subagente...', - 'Failed to generate subagent: {{error}}': - 'Falha ao gerar subagente: {{error}}', - 'Step {{n}}: Describe Your Subagent': 'Etapa {{n}}: Descreva Seu Subagente', - 'Step {{n}}: Enter Subagent Name': 'Etapa {{n}}: Digite o Nome do Subagente', - 'Step {{n}}: Enter System Prompt': 'Etapa {{n}}: Digite o Prompt do Sistema', - 'Step {{n}}: Enter Description': 'Etapa {{n}}: Digite a Descrição', + "Step {{n}}: Choose Location": "Etapa {{n}}: Escolher Localização", + "Step {{n}}: Choose Generation Method": "Etapa {{n}}: Escolher Método de Geração", + "Generate with Qwen Code (Recommended)": "Gerar com Qwen Code (Recomendado)", + "Manual Creation": "Criação Manual", + "Describe what this subagent should do and when it should be used. (Be comprehensive for best results)": + "Descreva o que este subagente deve fazer e quando deve ser usado. (Seja abrangente para melhores resultados)", + "e.g., Expert code reviewer that reviews code based on best practices...": + "ex: Revisor de código especialista que revisa código com base em melhores práticas...", + "Generating subagent configuration...": "Gerando configuração do subagente...", + "Failed to generate subagent: {{error}}": "Falha ao gerar subagente: {{error}}", + "Step {{n}}: Describe Your Subagent": "Etapa {{n}}: Descreva Seu Subagente", + "Step {{n}}: Enter Subagent Name": "Etapa {{n}}: Digite o Nome do Subagente", + "Step {{n}}: Enter System Prompt": "Etapa {{n}}: Digite o Prompt do Sistema", + "Step {{n}}: Enter Description": "Etapa {{n}}: Digite a Descrição", // ============================================================================ // Agents - Tool Selection // ============================================================================ - 'Step {{n}}: Select Tools': 'Etapa {{n}}: Selecionar Ferramentas', - 'All Tools (Default)': 'Todas as Ferramentas (Padrão)', - 'All Tools': 'Todas as Ferramentas', - 'Read-only Tools': 'Ferramentas de Somente Leitura', - 'Read & Edit Tools': 'Ferramentas de Leitura e Edição', - 'Read & Edit & Execution Tools': 'Ferramentas de Leitura, Edição e Execução', - 'All tools selected, including MCP tools': - 'Todas as ferramentas selecionadas, incluindo ferramentas MCP', - 'Selected tools:': 'Ferramentas selecionadas:', - 'Read-only tools:': 'Ferramentas de somente leitura:', - 'Edit tools:': 'Ferramentas de edição:', - 'Execution tools:': 'Ferramentas de execução:', - 'Step {{n}}: Choose Background Color': 'Etapa {{n}}: Escolher Cor de Fundo', - 'Step {{n}}: Confirm and Save': 'Etapa {{n}}: Confirmar e Salvar', + "Step {{n}}: Select Tools": "Etapa {{n}}: Selecionar Ferramentas", + "All Tools (Default)": "Todas as Ferramentas (Padrão)", + "All Tools": "Todas as Ferramentas", + "Read-only Tools": "Ferramentas de Somente Leitura", + "Read & Edit Tools": "Ferramentas de Leitura e Edição", + "Read & Edit & Execution Tools": "Ferramentas de Leitura, Edição e Execução", + "All tools selected, including MCP tools": + "Todas as ferramentas selecionadas, incluindo ferramentas MCP", + "Selected tools:": "Ferramentas selecionadas:", + "Read-only tools:": "Ferramentas de somente leitura:", + "Edit tools:": "Ferramentas de edição:", + "Execution tools:": "Ferramentas de execução:", + "Step {{n}}: Choose Background Color": "Etapa {{n}}: Escolher Cor de Fundo", + "Step {{n}}: Confirm and Save": "Etapa {{n}}: Confirmar e Salvar", // ============================================================================ // Agents - Navigation & Instructions // ============================================================================ - 'Esc to cancel': 'Esc para cancelar', - 'Press Enter to save, e to save and edit, Esc to go back': - 'Pressione Enter para salvar, e para salvar e editar, Esc para voltar', - 'Press Enter to continue, {{navigation}}Esc to {{action}}': - 'Pressione Enter para continuar, {{navigation}}Esc para {{action}}', - cancel: 'cancelar', - 'go back': 'voltar', - '↑↓ to navigate, ': '↑↓ para navegar, ', - 'Enter a clear, unique name for this subagent.': - 'Digite um nome claro e único para este subagente.', - 'e.g., Code Reviewer': 'ex: Revisor de Código', - 'Name cannot be empty.': 'O nome não pode estar vazio.', + "Esc to cancel": "Esc para cancelar", + "Press Enter to save, e to save and edit, Esc to go back": + "Pressione Enter para salvar, e para salvar e editar, Esc para voltar", + "Press Enter to continue, {{navigation}}Esc to {{action}}": + "Pressione Enter para continuar, {{navigation}}Esc para {{action}}", + cancel: "cancelar", + "go back": "voltar", + "↑↓ to navigate, ": "↑↓ para navegar, ", + "Enter a clear, unique name for this subagent.": + "Digite um nome claro e único para este subagente.", + "e.g., Code Reviewer": "ex: Revisor de Código", + "Name cannot be empty.": "O nome não pode estar vazio.", "Write the system prompt that defines this subagent's behavior. Be comprehensive for best results.": - 'Escreva o prompt do sistema que define o comportamento deste subagente. Seja abrangente para melhores resultados.', - 'e.g., You are an expert code reviewer...': - 'ex: Você é um revisor de código especialista...', - 'System prompt cannot be empty.': 'O prompt do sistema não pode estar vazio.', - 'Describe when and how this subagent should be used.': - 'Descreva quando e como este subagente deve ser usado.', - 'e.g., Reviews code for best practices and potential bugs.': - 'ex: Revisa o código em busca de melhores práticas e erros potenciais.', - 'Description cannot be empty.': 'A descrição não pode estar vazia.', - 'Failed to launch editor: {{error}}': 'Falha ao iniciar editor: {{error}}', - 'Failed to save and edit subagent: {{error}}': - 'Falha ao salvar e editar subagente: {{error}}', + "Escreva o prompt do sistema que define o comportamento deste subagente. Seja abrangente para melhores resultados.", + "e.g., You are an expert code reviewer...": "ex: Você é um revisor de código especialista...", + "System prompt cannot be empty.": "O prompt do sistema não pode estar vazio.", + "Describe when and how this subagent should be used.": + "Descreva quando e como este subagente deve ser usado.", + "e.g., Reviews code for best practices and potential bugs.": + "ex: Revisa o código em busca de melhores práticas e erros potenciais.", + "Description cannot be empty.": "A descrição não pode estar vazia.", + "Failed to launch editor: {{error}}": "Falha ao iniciar editor: {{error}}", + "Failed to save and edit subagent: {{error}}": "Falha ao salvar e editar subagente: {{error}}", // ============================================================================ // Commands - General (continued) // ============================================================================ - 'View and edit Qwen Code settings': 'Ver e editar configurações do Qwen Code', - Settings: 'Configurações', - 'To see changes, Qwen Code must be restarted. Press r to exit and apply changes now.': - 'Para ver as alterações, o Qwen Code deve ser reiniciado. Pressione r para sair e aplicar as alterações agora.', + "View and edit Qwen Code settings": "Ver e editar configurações do Qwen Code", + Settings: "Configurações", + "To see changes, Qwen Code must be restarted. Press r to exit and apply changes now.": + "Para ver as alterações, o Qwen Code deve ser reiniciado. Pressione r para sair e aplicar as alterações agora.", 'The command "/{{command}}" is not supported in non-interactive mode.': 'O comando "/{{command}}" não é suportado no modo não interativo.', // ============================================================================ // Settings Labels // ============================================================================ - 'Vim Mode': 'Modo Vim', - 'Disable Auto Update': 'Desativar Atualização Automática', - 'Attribution: commit': 'Atribuição: commit', - 'Terminal Bell Notification': 'Notificação Sonora do Terminal', - 'Enable Usage Statistics': 'Ativar Estatísticas de Uso', - Theme: 'Tema', - 'Preferred Editor': 'Editor Preferido', - 'Auto-connect to IDE': 'Conexão Automática com IDE', - 'Enable Prompt Completion': 'Ativar Autocompletar de Prompts', - 'Debug Keystroke Logging': 'Log de Depuração de Teclas', - 'Language: UI': 'Idioma: Interface', - 'Language: Model': 'Idioma: Modelo', - 'Output Format': 'Formato de Saída', - 'Hide Window Title': 'Ocultar Título da Janela', - 'Show Status in Title': 'Mostrar Status no Título', - 'Hide Tips': 'Ocultar Dicas', - 'Show Line Numbers in Code': 'Mostrar Números de Linhas no Código', - 'Show Citations': 'Mostrar Citações', - 'Custom Witty Phrases': 'Frases de Efeito Personalizadas', - 'Show Welcome Back Dialog': 'Mostrar Diálogo de Bem-vindo de Volta', - 'Enable User Feedback': 'Ativar Feedback do Usuário', - 'How is Qwen doing this session? (optional)': - 'Como o Qwen está se saindo nesta sessão? (opcional)', - Bad: 'Ruim', - Fine: 'Bom', - Good: 'Ótimo', - Dismiss: 'Ignorar', - 'Not Sure Yet': 'Não tenho certeza ainda', - 'Any other key': 'Qualquer outra tecla', - 'Disable Loading Phrases': 'Desativar Frases de Carregamento', - 'Screen Reader Mode': 'Modo de Leitor de Tela', - 'IDE Mode': 'Modo IDE', - 'Max Session Turns': 'Máximo de Turnos da Sessão', - 'Skip Next Speaker Check': 'Pular Verificação do Próximo Falante', - 'Skip Loop Detection': 'Pular Detecção de Loop', - 'Skip Startup Context': 'Pular Contexto de Inicialização', - 'Enable OpenAI Logging': 'Ativar Log do OpenAI', - 'OpenAI Logging Directory': 'Diretório de Log do OpenAI', - Timeout: 'Tempo Limite', - 'Max Retries': 'Máximo de Tentativas', - 'Disable Cache Control': 'Desativar Controle de Cache', - 'Memory Discovery Max Dirs': 'Descoberta de Memória Máx. Diretorios', - 'Load Memory From Include Directories': - 'Carregar Memória de Diretórios Incluídos', - 'Respect .gitignore': 'Respeitar .gitignore', - 'Respect .qwenignore': 'Respeitar .qwenignore', - 'Enable Recursive File Search': 'Ativar Pesquisa Recursiva de Arquivos', - 'Disable Fuzzy Search': 'Desativar Pesquisa Difusa', - 'Interactive Shell (PTY)': 'Shell Interativo (PTY)', - 'Show Color': 'Mostrar Cores', - 'Auto Accept': 'Aceitar Automaticamente', - 'Use Ripgrep': 'Usar Ripgrep', - 'Use Builtin Ripgrep': 'Usar Ripgrep Integrado', - 'Enable Tool Output Truncation': 'Ativar Truncamento de Saída de Ferramenta', - 'Tool Output Truncation Threshold': - 'Limite de Truncamento de Saída de Ferramenta', - 'Tool Output Truncation Lines': - 'Linhas de Truncamento de Saída de Ferramenta', - 'Folder Trust': 'Confiança de Pasta', - 'Vision Model Preview': 'Visualização de Modelo de Visão', - 'Tool Schema Compliance': 'Conformidade de Esquema de Ferramenta', + "Vim Mode": "Modo Vim", + "Disable Auto Update": "Desativar Atualização Automática", + "Attribution: commit": "Atribuição: commit", + "Terminal Bell Notification": "Notificação Sonora do Terminal", + "Enable Usage Statistics": "Ativar Estatísticas de Uso", + Theme: "Tema", + "Preferred Editor": "Editor Preferido", + "Auto-connect to IDE": "Conexão Automática com IDE", + "Enable Prompt Completion": "Ativar Autocompletar de Prompts", + "Debug Keystroke Logging": "Log de Depuração de Teclas", + "Language: UI": "Idioma: Interface", + "Language: Model": "Idioma: Modelo", + "Output Format": "Formato de Saída", + "Hide Window Title": "Ocultar Título da Janela", + "Show Status in Title": "Mostrar Status no Título", + "Hide Tips": "Ocultar Dicas", + "Show Line Numbers in Code": "Mostrar Números de Linhas no Código", + "Show Citations": "Mostrar Citações", + "Custom Witty Phrases": "Frases de Efeito Personalizadas", + "Show Welcome Back Dialog": "Mostrar Diálogo de Bem-vindo de Volta", + "Enable User Feedback": "Ativar Feedback do Usuário", + "How is Qwen doing this session? (optional)": + "Como o Qwen está se saindo nesta sessão? (opcional)", + Bad: "Ruim", + Fine: "Bom", + Good: "Ótimo", + Dismiss: "Ignorar", + "Not Sure Yet": "Não tenho certeza ainda", + "Any other key": "Qualquer outra tecla", + "Disable Loading Phrases": "Desativar Frases de Carregamento", + "Screen Reader Mode": "Modo de Leitor de Tela", + "IDE Mode": "Modo IDE", + "Max Session Turns": "Máximo de Turnos da Sessão", + "Skip Next Speaker Check": "Pular Verificação do Próximo Falante", + "Skip Loop Detection": "Pular Detecção de Loop", + "Skip Startup Context": "Pular Contexto de Inicialização", + "Enable OpenAI Logging": "Ativar Log do OpenAI", + "OpenAI Logging Directory": "Diretório de Log do OpenAI", + Timeout: "Tempo Limite", + "Max Retries": "Máximo de Tentativas", + "Disable Cache Control": "Desativar Controle de Cache", + "Memory Discovery Max Dirs": "Descoberta de Memória Máx. Diretorios", + "Load Memory From Include Directories": "Carregar Memória de Diretórios Incluídos", + "Respect .gitignore": "Respeitar .gitignore", + "Respect .qwenignore": "Respeitar .qwenignore", + "Enable Recursive File Search": "Ativar Pesquisa Recursiva de Arquivos", + "Disable Fuzzy Search": "Desativar Pesquisa Difusa", + "Interactive Shell (PTY)": "Shell Interativo (PTY)", + "Show Color": "Mostrar Cores", + "Auto Accept": "Aceitar Automaticamente", + "Use Ripgrep": "Usar Ripgrep", + "Use Builtin Ripgrep": "Usar Ripgrep Integrado", + "Enable Tool Output Truncation": "Ativar Truncamento de Saída de Ferramenta", + "Tool Output Truncation Threshold": "Limite de Truncamento de Saída de Ferramenta", + "Tool Output Truncation Lines": "Linhas de Truncamento de Saída de Ferramenta", + "Folder Trust": "Confiança de Pasta", + "Vision Model Preview": "Visualização de Modelo de Visão", + "Tool Schema Compliance": "Conformidade de Esquema de Ferramenta", // Settings enum options - 'Auto (detect from system)': 'Automático (detectar do sistema)', - Text: 'Texto', - JSON: 'JSON', - Plan: 'Planejamento', - Default: 'Padrão', - 'Auto Edit': 'Edição Automática', - YOLO: 'YOLO', - 'toggle vim mode on/off': 'alternar modo vim ligado/desligado', - 'check session stats. Usage: /stats [model|tools]': - 'verificar estatísticas da sessão. Uso: /stats [model|tools]', - 'Show model-specific usage statistics.': - 'Mostrar estatísticas de uso específicas do modelo.', - 'Show tool-specific usage statistics.': - 'Mostrar estatísticas de uso específicas da ferramenta.', - 'exit the cli': 'sair da cli', - 'Open MCP management dialog, or authenticate with OAuth-enabled servers': - 'Abrir diálogo de gerenciamento MCP ou autenticar com servidor habilitado para OAuth', - 'List configured MCP servers and tools, or authenticate with OAuth-enabled servers': - 'Listar servidores e ferramentas MCP configurados, ou autenticar com servidores habilitados para OAuth', - 'Manage workspace directories': 'Gerenciar diretórios do workspace', - 'Add directories to the workspace. Use comma to separate multiple paths': - 'Adicionar diretórios ao workspace. Use vírgula para separar vários caminhos', - 'Show all directories in the workspace': - 'Mostrar todos os diretórios no workspace', - 'set external editor preference': 'definir preferência de editor externo', - 'Select Editor': 'Selecionar Editor', - 'Editor Preference': 'Preferência de Editor', - 'These editors are currently supported. Please note that some editors cannot be used in sandbox mode.': - 'Estes editores são suportados atualmente. Note que alguns editores não podem ser usados no modo sandbox.', - 'Your preferred editor is:': 'Seu editor preferido é:', - 'Manage extensions': 'Gerenciar extensões', - 'Manage installed extensions': 'Gerenciar extensões instaladas', - 'List active extensions': 'Listar extensões ativas', - 'Update extensions. Usage: update |--all': - 'Atualizar extensões. Uso: update |--all', - 'Disable an extension': 'Desativar uma extensão', - 'Enable an extension': 'Ativar uma extensão', - 'Install an extension from a git repo or local path': - 'Instalar uma extensão de um repositório git ou caminho local', - 'Uninstall an extension': 'Desinstalar uma extensão', - 'No extensions installed.': 'Nenhuma extensão instalada.', - 'Usage: /extensions update |--all': - 'Uso: /extensions update |--all', + "Auto (detect from system)": "Automático (detectar do sistema)", + Text: "Texto", + JSON: "JSON", + Plan: "Planejamento", + Default: "Padrão", + "Auto Edit": "Edição Automática", + YOLO: "YOLO", + "toggle vim mode on/off": "alternar modo vim ligado/desligado", + "check session stats. Usage: /stats [model|tools]": + "verificar estatísticas da sessão. Uso: /stats [model|tools]", + "Show model-specific usage statistics.": "Mostrar estatísticas de uso específicas do modelo.", + "Show tool-specific usage statistics.": "Mostrar estatísticas de uso específicas da ferramenta.", + "exit the cli": "sair da cli", + "Open MCP management dialog, or authenticate with OAuth-enabled servers": + "Abrir diálogo de gerenciamento MCP ou autenticar com servidor habilitado para OAuth", + "List configured MCP servers and tools, or authenticate with OAuth-enabled servers": + "Listar servidores e ferramentas MCP configurados, ou autenticar com servidores habilitados para OAuth", + "Manage workspace directories": "Gerenciar diretórios do workspace", + "Add directories to the workspace. Use comma to separate multiple paths": + "Adicionar diretórios ao workspace. Use vírgula para separar vários caminhos", + "Show all directories in the workspace": "Mostrar todos os diretórios no workspace", + "set external editor preference": "definir preferência de editor externo", + "Select Editor": "Selecionar Editor", + "Editor Preference": "Preferência de Editor", + "These editors are currently supported. Please note that some editors cannot be used in sandbox mode.": + "Estes editores são suportados atualmente. Note que alguns editores não podem ser usados no modo sandbox.", + "Your preferred editor is:": "Seu editor preferido é:", + "Manage extensions": "Gerenciar extensões", + "Manage installed extensions": "Gerenciar extensões instaladas", + "List active extensions": "Listar extensões ativas", + "Update extensions. Usage: update |--all": + "Atualizar extensões. Uso: update |--all", + "Disable an extension": "Desativar uma extensão", + "Enable an extension": "Ativar uma extensão", + "Install an extension from a git repo or local path": + "Instalar uma extensão de um repositório git ou caminho local", + "Uninstall an extension": "Desinstalar uma extensão", + "No extensions installed.": "Nenhuma extensão instalada.", + "Usage: /extensions update |--all": + "Uso: /extensions update |--all", 'Extension "{{name}}" not found.': 'Extensão "{{name}}" não encontrada.', - 'No extensions to update.': 'Nenhuma extensão para atualizar.', - 'Usage: /extensions install ': 'Uso: /extensions install ', - 'Installing extension from "{{source}}"...': - 'Instalando extensão de "{{source}}"...', - 'Extension "{{name}}" installed successfully.': - 'Extensão "{{name}}" instalada com sucesso.', + "No extensions to update.": "Nenhuma extensão para atualizar.", + "Usage: /extensions install ": "Uso: /extensions install ", + 'Installing extension from "{{source}}"...': 'Instalando extensão de "{{source}}"...', + 'Extension "{{name}}" installed successfully.': 'Extensão "{{name}}" instalada com sucesso.', 'Failed to install extension from "{{source}}": {{error}}': 'Falha ao instalar extensão de "{{source}}": {{error}}', - 'Usage: /extensions uninstall ': - 'Uso: /extensions uninstall ', - 'Uninstalling extension "{{name}}"...': - 'Desinstalando extensão "{{name}}"...', - 'Extension "{{name}}" uninstalled successfully.': - 'Extensão "{{name}}" desinstalada com sucesso.', + "Usage: /extensions uninstall ": "Uso: /extensions uninstall ", + 'Uninstalling extension "{{name}}"...': 'Desinstalando extensão "{{name}}"...', + 'Extension "{{name}}" uninstalled successfully.': 'Extensão "{{name}}" desinstalada com sucesso.', 'Failed to uninstall extension "{{name}}": {{error}}': 'Falha ao desinstalar extensão "{{name}}": {{error}}', - 'Usage: /extensions {{command}} [--scope=]': - 'Uso: /extensions {{command}} [--scope=]', + "Usage: /extensions {{command}} [--scope=]": + "Uso: /extensions {{command}} [--scope=]", 'Unsupported scope "{{scope}}", deve ser um de "user" ou "workspace"': 'Escopo não suportado "{{scope}}", deve ser um de "user" ou "workspace"', 'Extension "{{name}}" disabled for scope "{{scope}}"': 'Extensão "{{name}}" desativada para o escopo "{{scope}}"', 'Extension "{{name}}" enabled for scope "{{scope}}"': 'Extensão "{{name}}" ativada para o escopo "{{scope}}"', - 'Do you want to continue? [Y/n]: ': 'Você deseja continuar? [Y/n]: ', - 'Do you want to continue?': 'Você deseja continuar?', + "Do you want to continue? [Y/n]: ": "Você deseja continuar? [Y/n]: ", + "Do you want to continue?": "Você deseja continuar?", 'Installing extension "{{name}}".': 'Instalando extensão "{{name}}".', - '**Extensions may introduce unexpected behavior. Ensure you have investigated the extension source and trust the author.**': - '**As extensões podem introduzir comportamentos inesperados. Certifique-se de ter investigado a fonte da extensão e confie no autor.**', - 'This extension will run the following MCP servers:': - 'Esta extensão executará os seguintes servidores MCP:', - local: 'local', - remote: 'remoto', - 'This extension will add the following commands: {{commands}}.': - 'Esta extensão adicionará os seguintes comandos: {{commands}}.', - 'This extension will append info to your QWEN.md context using {{fileName}}': - 'Esta extensão anexará informações ao seu contexto QWEN.md usando {{fileName}}', - 'This extension will exclude the following core tools: {{tools}}': - 'Esta extensão excluirá as seguintes ferramentas principais: {{tools}}', - 'This extension will install the following skills:': - 'Esta extensão instalará as seguintes habilidades:', - 'This extension will install the following subagents:': - 'Esta extensão instalará os seguintes subagentes:', - 'Installation cancelled for "{{name}}".': - 'Instalação cancelada para "{{name}}".', - 'You are installing an extension from {{originSource}}. Some features may not work perfectly with Qwen Code.': - 'Você está instalando uma extensão de {{originSource}}. Alguns recursos podem não funcionar perfeitamente com o Qwen Code.', - '--ref and --auto-update are not applicable for marketplace extensions.': - '--ref e --auto-update não são aplicáveis para extensões de marketplace.', + "**Extensions may introduce unexpected behavior. Ensure you have investigated the extension source and trust the author.**": + "**As extensões podem introduzir comportamentos inesperados. Certifique-se de ter investigado a fonte da extensão e confie no autor.**", + "This extension will run the following MCP servers:": + "Esta extensão executará os seguintes servidores MCP:", + local: "local", + remote: "remoto", + "This extension will add the following commands: {{commands}}.": + "Esta extensão adicionará os seguintes comandos: {{commands}}.", + "This extension will append info to your QWEN.md context using {{fileName}}": + "Esta extensão anexará informações ao seu contexto QWEN.md usando {{fileName}}", + "This extension will exclude the following core tools: {{tools}}": + "Esta extensão excluirá as seguintes ferramentas principais: {{tools}}", + "This extension will install the following skills:": + "Esta extensão instalará as seguintes habilidades:", + "This extension will install the following subagents:": + "Esta extensão instalará os seguintes subagentes:", + 'Installation cancelled for "{{name}}".': 'Instalação cancelada para "{{name}}".', + "You are installing an extension from {{originSource}}. Some features may not work perfectly with Qwen Code.": + "Você está instalando uma extensão de {{originSource}}. Alguns recursos podem não funcionar perfeitamente com o Qwen Code.", + "--ref and --auto-update are not applicable for marketplace extensions.": + "--ref e --auto-update não são aplicáveis para extensões de marketplace.", 'Extension "{{name}}" installed successfully and enabled.': 'Extensão "{{name}}" instalada com sucesso e ativada.', - 'Installs an extension from a git repository URL, local path, or claude marketplace (marketplace-url:plugin-name).': - 'Instala uma extensão de uma URL de repositório git, caminho local ou marketplace do claude (marketplace-url:plugin-name).', - 'The github URL, local path, or marketplace source (marketplace-url:plugin-name) of the extension to install.': - 'A URL do github, caminho local ou fonte do marketplace (marketplace-url:plugin-name) da extensão para instalar.', - 'The git ref to install from.': 'A referência git para instalar.', - 'Enable auto-update for this extension.': - 'Ativar atualização automática para esta extensão.', - 'Enable pre-release versions for this extension.': - 'Ativar versões de pré-lançamento para esta extensão.', - 'Acknowledge the security risks of installing an extension and skip the confirmation prompt.': - 'Reconhecer os riscos de segurança de instalar uma extensão e pular o prompt de confirmação.', - 'The source argument must be provided.': - 'O argumento fonte deve ser fornecido.', - 'Extension "{{name}}" successfully uninstalled.': - 'Extensão "{{name}}" desinstalada com sucesso.', - 'Uninstalls an extension.': 'Desinstala uma extensão.', - 'The name or source path of the extension to uninstall.': - 'O nome ou caminho da fonte da extensão para desinstalar.', - 'Please include the name of the extension to uninstall as a positional argument.': - 'Inclua o nome da extensão para desinstalar como um argumento posicional.', - 'Enables an extension.': 'Ativa uma extensão.', - 'The name of the extension to enable.': 'O nome da extensão para ativar.', - 'The scope to enable the extenison in. If not set, will be enabled in all scopes.': - 'O escopo para ativar a extensão. Se não definido, será ativada em todos os escopos.', + "Installs an extension from a git repository URL, local path, or claude marketplace (marketplace-url:plugin-name).": + "Instala uma extensão de uma URL de repositório git, caminho local ou marketplace do claude (marketplace-url:plugin-name).", + "The github URL, local path, or marketplace source (marketplace-url:plugin-name) of the extension to install.": + "A URL do github, caminho local ou fonte do marketplace (marketplace-url:plugin-name) da extensão para instalar.", + "The git ref to install from.": "A referência git para instalar.", + "Enable auto-update for this extension.": "Ativar atualização automática para esta extensão.", + "Enable pre-release versions for this extension.": + "Ativar versões de pré-lançamento para esta extensão.", + "Acknowledge the security risks of installing an extension and skip the confirmation prompt.": + "Reconhecer os riscos de segurança de instalar uma extensão e pular o prompt de confirmação.", + "The source argument must be provided.": "O argumento fonte deve ser fornecido.", + 'Extension "{{name}}" successfully uninstalled.': 'Extensão "{{name}}" desinstalada com sucesso.', + "Uninstalls an extension.": "Desinstala uma extensão.", + "The name or source path of the extension to uninstall.": + "O nome ou caminho da fonte da extensão para desinstalar.", + "Please include the name of the extension to uninstall as a positional argument.": + "Inclua o nome da extensão para desinstalar como um argumento posicional.", + "Enables an extension.": "Ativa uma extensão.", + "The name of the extension to enable.": "O nome da extensão para ativar.", + "The scope to enable the extenison in. If not set, will be enabled in all scopes.": + "O escopo para ativar a extensão. Se não definido, será ativada em todos os escopos.", 'Extension "{{name}}" successfully enabled for scope "{{scope}}".': 'Extensão "{{name}}" ativada com sucesso para o escopo "{{scope}}".', 'Extension "{{name}}" successfully enabled in all scopes.': 'Extensão "{{name}}" ativada com sucesso em todos os escopos.', - 'Invalid scope: {{scope}}. Please use one of {{scopes}}.': - 'Escopo inválido: {{scope}}. Use um de {{scopes}}.', - 'Disables an extension.': 'Desativa uma extensão.', - 'The name of the extension to disable.': 'O nome da extensão para desativar.', - 'The scope to disable the extenison in.': - 'O escopo para desativar a extensão.', + "Invalid scope: {{scope}}. Please use one of {{scopes}}.": + "Escopo inválido: {{scope}}. Use um de {{scopes}}.", + "Disables an extension.": "Desativa uma extensão.", + "The name of the extension to disable.": "O nome da extensão para desativar.", + "The scope to disable the extenison in.": "O escopo para desativar a extensão.", 'Extension "{{name}}" successfully disabled for scope "{{scope}}".': 'Extensão "{{name}}" desativada com sucesso para o escopo "{{scope}}".', 'Extension "{{name}}" successfully updated: {{oldVersion}} → {{newVersion}}.': 'Extensão "{{name}}" atualizada com sucesso: {{oldVersion}} → {{newVersion}}.', 'Unable to install extension "{{name}}" due to missing install metadata': 'Não foi possível instalar a extensão "{{name}}" devido à falta de metadados de instalação', - 'Extension "{{name}}" is already up to date.': - 'A extensão "{{name}}" já está atualizada.', - 'Updates all extensions or a named extension to the latest version.': - 'Atualiza todas as extensões ou uma extensão nomeada para a última versão.', - 'Update all extensions.': 'Atualizar todas as extensões.', - 'Either an extension name or --all must be provided': - 'Um nome de extensão ou --all deve ser fornecido', - 'Lists installed extensions.': 'Lista as extensões instaladas.', - 'Link extension failed to install.': 'Falha ao instalar link da extensão.', + 'Extension "{{name}}" is already up to date.': 'A extensão "{{name}}" já está atualizada.', + "Updates all extensions or a named extension to the latest version.": + "Atualiza todas as extensões ou uma extensão nomeada para a última versão.", + "Update all extensions.": "Atualizar todas as extensões.", + "Either an extension name or --all must be provided": + "Um nome de extensão ou --all deve ser fornecido", + "Lists installed extensions.": "Lista as extensões instaladas.", + "Link extension failed to install.": "Falha ao instalar link da extensão.", 'Extension "{{name}}" linked successfully and enabled.': 'Extensão "{{name}}" vinculada com sucesso e ativada.', - 'Links an extension from a local path. Updates made to the local path will always be reflected.': - 'Vincula uma extensão de um caminho local. Atualizações feitas no caminho local sempre serão refletidas.', - 'The name of the extension to link.': 'O nome da extensão para vincular.', - 'Set a specific setting for an extension.': - 'Define uma configuração específica para uma extensão.', - 'Name of the extension to configure.': 'Nome da extensão para configurar.', - 'The setting to configure (name or env var).': - 'A configuração para configurar (nome ou var env).', - 'The scope to set the setting in.': 'O escopo para definir a configuração.', - 'List all settings for an extension.': - 'Listar todas as configurações de uma extensão.', - 'Name of the extension.': 'Nome da extensão.', + "Links an extension from a local path. Updates made to the local path will always be reflected.": + "Vincula uma extensão de um caminho local. Atualizações feitas no caminho local sempre serão refletidas.", + "The name of the extension to link.": "O nome da extensão para vincular.", + "Set a specific setting for an extension.": + "Define uma configuração específica para uma extensão.", + "Name of the extension to configure.": "Nome da extensão para configurar.", + "The setting to configure (name or env var).": + "A configuração para configurar (nome ou var env).", + "The scope to set the setting in.": "O escopo para definir a configuração.", + "List all settings for an extension.": "Listar todas as configurações de uma extensão.", + "Name of the extension.": "Nome da extensão.", 'Extension "{{name}}" has no settings to configure.': 'A extensão "{{name}}" não tem configurações para configurar.', 'Settings for "{{name}}":': 'Configurações para "{{name}}":', - '(workspace)': '(workspace)', - '(user)': '(usuário)', - '[not set]': '[não definido]', - '[value stored in keychain]': '[valor armazenado no chaveiro]', - 'Value:': 'Valor:', - 'Manage extension settings.': 'Gerenciar configurações de extensão.', - 'You need to specify a command (set or list).': - 'Você precisa especificar um comando (set ou list).', + "(workspace)": "(workspace)", + "(user)": "(usuário)", + "[not set]": "[não definido]", + "[value stored in keychain]": "[valor armazenado no chaveiro]", + "Value:": "Valor:", + "Manage extension settings.": "Gerenciar configurações de extensão.", + "You need to specify a command (set or list).": + "Você precisa especificar um comando (set ou list).", // ============================================================================ // Plugin Choice / Marketplace // ============================================================================ - 'No plugins available in this marketplace.': - 'Nenhum plugin disponível neste marketplace.', + "No plugins available in this marketplace.": "Nenhum plugin disponível neste marketplace.", 'Select a plugin to install from marketplace "{{name}}":': 'Selecione um plugin para instalar do marketplace "{{name}}":', - 'Plugin selection cancelled.': 'Seleção de plugin cancelada.', + "Plugin selection cancelled.": "Seleção de plugin cancelada.", 'Select a plugin from "{{name}}"': 'Selecione um plugin de "{{name}}"', - 'Use ↑↓ or j/k to navigate, Enter to select, Escape to cancel': - 'Use ↑↓ ou j/k para navegar, Enter para selecionar, Escape para cancelar', - '{{count}} more above': '{{count}} mais acima', - '{{count}} more below': '{{count}} mais abaixo', - 'manage IDE integration': 'gerenciar integração com IDE', - 'check status of IDE integration': 'verificar status da integração com IDE', - 'install required IDE companion for {{ideName}}': - 'instalar companion IDE necessário para {{ideName}}', - 'enable IDE integration': 'ativar integração com IDE', - 'disable IDE integration': 'desativar integração com IDE', - '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.': - 'A integração com IDE não é suportada no seu ambiente atual. Para usar este recurso, execute o Qwen Code em um destes IDEs suportados: VS Code ou forks do VS Code.', - 'Set up GitHub Actions': 'Configurar GitHub Actions', - 'Configure terminal keybindings for multiline input (VS Code, Cursor, Windsurf, Trae)': - 'Configurar atalhos de terminal para entrada multilinhas (VS Code, Cursor, Windsurf, Trae)', - 'Please restart your terminal for the changes to take effect.': - 'Reinicie seu terminal para que as alterações tenham efeito.', - 'Failed to configure terminal: {{error}}': - 'Falha ao configurar terminal: {{error}}', - 'Could not determine {{terminalName}} config path on Windows: APPDATA environment variable is not set.': - 'Não foi possível determinar o caminho de configuração de {{terminalName}} no Windows: variável de ambiente APPDATA não está definida.', - '{{terminalName}} keybindings.json exists but is not a valid JSON array. Please fix the file manually or delete it to allow automatic configuration.': - '{{terminalName}} keybindings.json existe mas não é um array JSON válido. Corrija o arquivo manualmente ou exclua-o para permitir a configuração automática.', - 'File: {{file}}': 'Arquivo: {{file}}', - 'Failed to parse {{terminalName}} keybindings.json. The file contains invalid JSON. Please fix the file manually or delete it to allow automatic configuration.': - 'Falha ao analisar {{terminalName}} keybindings.json. O arquivo contém JSON inválido. Corrija o arquivo manualmente ou exclua-o para permitir a configuração automática.', - 'Error: {{error}}': 'Erro: {{error}}', - 'Shift+Enter binding already exists': 'Atalho Shift+Enter já existe', - 'Ctrl+Enter binding already exists': 'Atalho Ctrl+Enter já existe', - 'Existing keybindings detected. Will not modify to avoid conflicts.': - 'Atalhos existentes detectados. Não serão modificados para evitar conflitos.', - 'Please check and modify manually if needed: {{file}}': - 'Verifique e modifique manualmente se necessário: {{file}}', - 'Added Shift+Enter and Ctrl+Enter keybindings to {{terminalName}}.': - 'Adicionados atalhos Shift+Enter e Ctrl+Enter para {{terminalName}}.', - 'Modified: {{file}}': 'Modificado: {{file}}', - '{{terminalName}} keybindings already configured.': - 'Atalhos de {{terminalName}} já configurados.', - 'Failed to configure {{terminalName}}.': - 'Falha ao configurar {{terminalName}}.', - 'Your terminal is already configured for an optimal experience with multiline input (Shift+Enter and Ctrl+Enter).': - 'Seu terminal já está configurado para uma experiência ideal com entrada multilinhas (Shift+Enter e Ctrl+Enter).', + "Use ↑↓ or j/k to navigate, Enter to select, Escape to cancel": + "Use ↑↓ ou j/k para navegar, Enter para selecionar, Escape para cancelar", + "{{count}} more above": "{{count}} mais acima", + "{{count}} more below": "{{count}} mais abaixo", + "manage IDE integration": "gerenciar integração com IDE", + "check status of IDE integration": "verificar status da integração com IDE", + "install required IDE companion for {{ideName}}": + "instalar companion IDE necessário para {{ideName}}", + "enable IDE integration": "ativar integração com IDE", + "disable IDE integration": "desativar integração com IDE", + "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.": + "A integração com IDE não é suportada no seu ambiente atual. Para usar este recurso, execute o Qwen Code em um destes IDEs suportados: VS Code ou forks do VS Code.", + "Set up GitHub Actions": "Configurar GitHub Actions", + "Configure terminal keybindings for multiline input (VS Code, Cursor, Windsurf, Trae)": + "Configurar atalhos de terminal para entrada multilinhas (VS Code, Cursor, Windsurf, Trae)", + "Please restart your terminal for the changes to take effect.": + "Reinicie seu terminal para que as alterações tenham efeito.", + "Failed to configure terminal: {{error}}": "Falha ao configurar terminal: {{error}}", + "Could not determine {{terminalName}} config path on Windows: APPDATA environment variable is not set.": + "Não foi possível determinar o caminho de configuração de {{terminalName}} no Windows: variável de ambiente APPDATA não está definida.", + "{{terminalName}} keybindings.json exists but is not a valid JSON array. Please fix the file manually or delete it to allow automatic configuration.": + "{{terminalName}} keybindings.json existe mas não é um array JSON válido. Corrija o arquivo manualmente ou exclua-o para permitir a configuração automática.", + "File: {{file}}": "Arquivo: {{file}}", + "Failed to parse {{terminalName}} keybindings.json. The file contains invalid JSON. Please fix the file manually or delete it to allow automatic configuration.": + "Falha ao analisar {{terminalName}} keybindings.json. O arquivo contém JSON inválido. Corrija o arquivo manualmente ou exclua-o para permitir a configuração automática.", + "Error: {{error}}": "Erro: {{error}}", + "Shift+Enter binding already exists": "Atalho Shift+Enter já existe", + "Ctrl+Enter binding already exists": "Atalho Ctrl+Enter já existe", + "Existing keybindings detected. Will not modify to avoid conflicts.": + "Atalhos existentes detectados. Não serão modificados para evitar conflitos.", + "Please check and modify manually if needed: {{file}}": + "Verifique e modifique manualmente se necessário: {{file}}", + "Added Shift+Enter and Ctrl+Enter keybindings to {{terminalName}}.": + "Adicionados atalhos Shift+Enter e Ctrl+Enter para {{terminalName}}.", + "Modified: {{file}}": "Modificado: {{file}}", + "{{terminalName}} keybindings already configured.": + "Atalhos de {{terminalName}} já configurados.", + "Failed to configure {{terminalName}}.": "Falha ao configurar {{terminalName}}.", + "Your terminal is already configured for an optimal experience with multiline input (Shift+Enter and Ctrl+Enter).": + "Seu terminal já está configurado para uma experiência ideal com entrada multilinhas (Shift+Enter e Ctrl+Enter).", // ============================================================================ // Commands - Hooks // ============================================================================ - 'Manage Qwen Code hooks': 'Gerenciar hooks do Qwen Code', - 'List all configured hooks': 'Listar todos os hooks configurados', - 'Enable a disabled hook': 'Ativar um hook desativado', - 'Disable an active hook': 'Desativar um hook ativo', + "Manage Qwen Code hooks": "Gerenciar hooks do Qwen Code", + "List all configured hooks": "Listar todos os hooks configurados", + "Enable a disabled hook": "Ativar um hook desativado", + "Disable an active hook": "Desativar um hook ativo", // Hooks - Dialog - Hooks: 'Hooks', - 'Loading hooks...': 'Carregando hooks...', - 'Error loading hooks:': 'Erro ao carregar hooks:', - 'Press Escape to close': 'Pressione Escape para fechar', - 'Press Escape, Ctrl+C, or Ctrl+D to cancel': - 'Pressione Escape, Ctrl+C ou Ctrl+D para cancelar', - 'Press Space, Enter, or Escape to dismiss': - 'Pressione Espaço, Enter ou Escape para dispensar', - 'No hook selected': 'Nenhum hook selecionado', + Hooks: "Hooks", + "Loading hooks...": "Carregando hooks...", + "Error loading hooks:": "Erro ao carregar hooks:", + "Press Escape to close": "Pressione Escape para fechar", + "Press Escape, Ctrl+C, or Ctrl+D to cancel": "Pressione Escape, Ctrl+C ou Ctrl+D para cancelar", + "Press Space, Enter, or Escape to dismiss": "Pressione Espaço, Enter ou Escape para dispensar", + "No hook selected": "Nenhum hook selecionado", // Hooks - List Step - 'No hook events found.': 'Nenhum evento de hook encontrado.', - '{{count}} hook configured': '{{count}} hook configurado', - '{{count}} hooks configured': '{{count}} hooks configurados', - 'This menu is read-only. To add or modify hooks, edit settings.json directly or ask Qwen Code.': - 'Este menu é somente leitura. Para adicionar ou modificar hooks, edite settings.json diretamente ou pergunte ao Qwen Code.', - 'Enter to select · Esc to cancel': - 'Enter para selecionar · Esc para cancelar', + "No hook events found.": "Nenhum evento de hook encontrado.", + "{{count}} hook configured": "{{count}} hook configurado", + "{{count}} hooks configured": "{{count}} hooks configurados", + "This menu is read-only. To add or modify hooks, edit settings.json directly or ask Qwen Code.": + "Este menu é somente leitura. Para adicionar ou modificar hooks, edite settings.json diretamente ou pergunte ao Qwen Code.", + "Enter to select · Esc to cancel": "Enter para selecionar · Esc para cancelar", // Hooks - Detail Step - 'Exit codes:': 'Códigos de saída:', - 'Configured hooks:': 'Hooks configurados:', - 'No hooks configured for this event.': - 'Nenhum hook configurado para este evento.', - 'To add hooks, edit settings.json directly or ask Qwen.': - 'Para adicionar hooks, edite settings.json diretamente ou pergunte ao Qwen.', - 'Enter to select · Esc to go back': 'Enter para selecionar · Esc para voltar', + "Exit codes:": "Códigos de saída:", + "Configured hooks:": "Hooks configurados:", + "No hooks configured for this event.": "Nenhum hook configurado para este evento.", + "To add hooks, edit settings.json directly or ask Qwen.": + "Para adicionar hooks, edite settings.json diretamente ou pergunte ao Qwen.", + "Enter to select · Esc to go back": "Enter para selecionar · Esc para voltar", // Hooks - Config Detail Step - 'Hook details': 'Detalhes do Hook', - 'Event:': 'Evento:', - 'Extension:': 'Extensão:', - 'Desc:': 'Descrição:', - 'No hook config selected': 'Nenhuma configuração de hook selecionada', - 'To modify or remove this hook, edit settings.json directly or ask Qwen to help.': - 'Para modificar ou remover este hook, edite settings.json diretamente ou pergunte ao Qwen.', + "Hook details": "Detalhes do Hook", + "Event:": "Evento:", + "Extension:": "Extensão:", + "Desc:": "Descrição:", + "No hook config selected": "Nenhuma configuração de hook selecionada", + "To modify or remove this hook, edit settings.json directly or ask Qwen to help.": + "Para modificar ou remover este hook, edite settings.json diretamente ou pergunte ao Qwen.", // Hooks - Disabled Step - 'Hook Configuration - Disabled': 'Configuração de Hook - Desativado', - 'All hooks are currently disabled. You have {{count}} that are not running.': - 'Todos os hooks estão desativados. Você tem {{count}} que não estão em execução.', - '{{count}} configured hook': '{{count}} hook configurado', - '{{count}} configured hooks': '{{count}} hooks configurados', - 'When hooks are disabled:': 'Quando os hooks estão desativados:', - 'No hook commands will execute': 'Nenhum comando de hook será executado', - 'StatusLine will not be displayed': 'StatusLine não será exibido', - 'Tool operations will proceed without hook validation': - 'As operações de ferramentas prosseguirão sem validação de hook', + "Hook Configuration - Disabled": "Configuração de Hook - Desativado", + "All hooks are currently disabled. You have {{count}} that are not running.": + "Todos os hooks estão desativados. Você tem {{count}} que não estão em execução.", + "{{count}} configured hook": "{{count}} hook configurado", + "{{count}} configured hooks": "{{count}} hooks configurados", + "When hooks are disabled:": "Quando os hooks estão desativados:", + "No hook commands will execute": "Nenhum comando de hook será executado", + "StatusLine will not be displayed": "StatusLine não será exibido", + "Tool operations will proceed without hook validation": + "As operações de ferramentas prosseguirão sem validação de hook", 'To re-enable hooks, remove "disableAllHooks" from settings.json or ask Qwen Code.': 'Para reativar os hooks, remova "disableAllHooks" do settings.json ou pergunte ao Qwen Code.', // Hooks - Source - Project: 'Projeto', - User: 'Usuário', - System: 'Sistema', - Extension: 'Extensão', - 'Local Settings': 'Configurações Locais', - 'User Settings': 'Configurações do Usuário', - 'System Settings': 'Configurações do Sistema', - Extensions: 'Extensões', + Project: "Projeto", + User: "Usuário", + System: "Sistema", + Extension: "Extensão", + "Local Settings": "Configurações Locais", + "User Settings": "Configurações do Usuário", + "System Settings": "Configurações do Sistema", + Extensions: "Extensões", // Hooks - Status - '✓ Enabled': '✓ Ativado', - '✗ Disabled': '✗ Desativado', + "✓ Enabled": "✓ Ativado", + "✗ Disabled": "✗ Desativado", // Hooks - Event Descriptions (short) - 'Before tool execution': 'Antes da execução da ferramenta', - 'After tool execution': 'Após a execução da ferramenta', - 'After tool execution fails': 'Após a falha da execução da ferramenta', - 'When notifications are sent': 'Quando notificações são enviadas', - 'When the user submits a prompt': 'Quando o usuário envia um prompt', - 'When a new session is started': 'Quando uma nova sessão é iniciada', - 'Right before Qwen Code concludes its response': - 'Logo antes do Qwen Code concluir sua resposta', - 'When a subagent (Agent tool call) is started': - 'Quando um subagente (chamada de ferramenta Agent) é iniciado', - 'Right before a subagent concludes its response': - 'Logo antes de um subagente concluir sua resposta', - 'Before conversation compaction': 'Antes da compactação da conversa', - 'When a session is ending': 'Quando uma sessão está terminando', - 'When a permission dialog is displayed': - 'Quando um diálogo de permissão é exibido', + "Before tool execution": "Antes da execução da ferramenta", + "After tool execution": "Após a execução da ferramenta", + "After tool execution fails": "Após a falha da execução da ferramenta", + "When notifications are sent": "Quando notificações são enviadas", + "When the user submits a prompt": "Quando o usuário envia um prompt", + "When a new session is started": "Quando uma nova sessão é iniciada", + "Right before Qwen Code concludes its response": "Logo antes do Qwen Code concluir sua resposta", + "When a subagent (Agent tool call) is started": + "Quando um subagente (chamada de ferramenta Agent) é iniciado", + "Right before a subagent concludes its response": + "Logo antes de um subagente concluir sua resposta", + "Before conversation compaction": "Antes da compactação da conversa", + "When a session is ending": "Quando uma sessão está terminando", + "When a permission dialog is displayed": "Quando um diálogo de permissão é exibido", // Hooks - Event Descriptions (detailed) - 'Input to command is JSON of tool call arguments.': - 'A entrada para o comando é JSON dos argumentos da chamada da ferramenta.', + "Input to command is JSON of tool call arguments.": + "A entrada para o comando é JSON dos argumentos da chamada da ferramenta.", 'Input to command is JSON with fields "inputs" (tool call arguments) and "response" (tool call response).': 'A entrada para o comando é JSON com campos "inputs" (argumentos da chamada da ferramenta) e "response" (resposta da chamada da ferramenta).', - 'Input to command is JSON with tool_name, tool_input, tool_use_id, error, error_type, is_interrupt, and is_timeout.': - 'A entrada para o comando é JSON com tool_name, tool_input, tool_use_id, error, error_type, is_interrupt e is_timeout.', - 'Input to command is JSON with notification message and type.': - 'A entrada para o comando é JSON com mensagem e tipo de notificação.', - 'Input to command is JSON with original user prompt text.': - 'A entrada para o comando é JSON com o texto original do prompt do usuário.', - 'Input to command is JSON with session start source.': - 'A entrada para o comando é JSON com a fonte de início da sessão.', - 'Input to command is JSON with session end reason.': - 'A entrada para o comando é JSON com o motivo do fim da sessão.', - 'Input to command is JSON with agent_id and agent_type.': - 'A entrada para o comando é JSON com agent_id e agent_type.', - 'Input to command is JSON with agent_id, agent_type, and agent_transcript_path.': - 'A entrada para o comando é JSON com agent_id, agent_type e agent_transcript_path.', - 'Input to command is JSON with compaction details.': - 'A entrada para o comando é JSON com detalhes da compactação.', - 'Input to command is JSON with tool_name, tool_input, and tool_use_id. Output JSON with hookSpecificOutput containing decision to allow or deny.': - 'A entrada para o comando é JSON com tool_name, tool_input e tool_use_id. Saída é JSON com hookSpecificOutput contendo decisão de permitir ou negar.', + "Input to command is JSON with tool_name, tool_input, tool_use_id, error, error_type, is_interrupt, and is_timeout.": + "A entrada para o comando é JSON com tool_name, tool_input, tool_use_id, error, error_type, is_interrupt e is_timeout.", + "Input to command is JSON with notification message and type.": + "A entrada para o comando é JSON com mensagem e tipo de notificação.", + "Input to command is JSON with original user prompt text.": + "A entrada para o comando é JSON com o texto original do prompt do usuário.", + "Input to command is JSON with session start source.": + "A entrada para o comando é JSON com a fonte de início da sessão.", + "Input to command is JSON with session end reason.": + "A entrada para o comando é JSON com o motivo do fim da sessão.", + "Input to command is JSON with agent_id and agent_type.": + "A entrada para o comando é JSON com agent_id e agent_type.", + "Input to command is JSON with agent_id, agent_type, and agent_transcript_path.": + "A entrada para o comando é JSON com agent_id, agent_type e agent_transcript_path.", + "Input to command is JSON with compaction details.": + "A entrada para o comando é JSON com detalhes da compactação.", + "Input to command is JSON with tool_name, tool_input, and tool_use_id. Output JSON with hookSpecificOutput containing decision to allow or deny.": + "A entrada para o comando é JSON com tool_name, tool_input e tool_use_id. Saída é JSON com hookSpecificOutput contendo decisão de permitir ou negar.", // Hooks - Exit Code Descriptions - 'stdout/stderr not shown': 'stdout/stderr não exibido', - 'show stderr to model and continue conversation': - 'mostrar stderr ao modelo e continuar conversa', - 'show stderr to user only': 'mostrar stderr apenas ao usuário', - 'stdout shown in transcript mode (ctrl+o)': - 'stdout exibido no modo transcrição (ctrl+o)', - 'show stderr to model immediately': 'mostrar stderr ao modelo imediatamente', - 'show stderr to user only but continue with tool call': - 'mostrar stderr apenas ao usuário mas continuar com chamada de ferramenta', - 'block processing, erase original prompt, and show stderr to user only': - 'bloquear processamento, apagar prompt original e mostrar stderr apenas ao usuário', - 'stdout shown to Qwen': 'stdout mostrado ao Qwen', - 'show stderr to user only (blocking errors ignored)': - 'mostrar stderr apenas ao usuário (erros de bloqueio ignorados)', - 'command completes successfully': 'comando concluído com sucesso', - 'stdout shown to subagent': 'stdout mostrado ao subagente', - 'show stderr to subagent and continue having it run': - 'mostrar stderr ao subagente e continuar executando', - 'stdout appended as custom compact instructions': - 'stdout anexado como instruções de compactação personalizadas', - 'block compaction': 'bloquear compactação', - 'show stderr to user only but continue with compaction': - 'mostrar stderr apenas ao usuário mas continuar com compactação', - 'use hook decision if provided': 'usar decisão do hook se fornecida', + "stdout/stderr not shown": "stdout/stderr não exibido", + "show stderr to model and continue conversation": "mostrar stderr ao modelo e continuar conversa", + "show stderr to user only": "mostrar stderr apenas ao usuário", + "stdout shown in transcript mode (ctrl+o)": "stdout exibido no modo transcrição (ctrl+o)", + "show stderr to model immediately": "mostrar stderr ao modelo imediatamente", + "show stderr to user only but continue with tool call": + "mostrar stderr apenas ao usuário mas continuar com chamada de ferramenta", + "block processing, erase original prompt, and show stderr to user only": + "bloquear processamento, apagar prompt original e mostrar stderr apenas ao usuário", + "stdout shown to Qwen": "stdout mostrado ao Qwen", + "show stderr to user only (blocking errors ignored)": + "mostrar stderr apenas ao usuário (erros de bloqueio ignorados)", + "command completes successfully": "comando concluído com sucesso", + "stdout shown to subagent": "stdout mostrado ao subagente", + "show stderr to subagent and continue having it run": + "mostrar stderr ao subagente e continuar executando", + "stdout appended as custom compact instructions": + "stdout anexado como instruções de compactação personalizadas", + "block compaction": "bloquear compactação", + "show stderr to user only but continue with compaction": + "mostrar stderr apenas ao usuário mas continuar com compactação", + "use hook decision if provided": "usar decisão do hook se fornecida", // Hooks - Messages - 'Config not loaded.': 'Configuração não carregada.', - 'Hooks are not enabled. Enable hooks in settings to use this feature.': - 'Hooks não estão ativados. Ative hooks nas configurações para usar este recurso.', - 'No hooks configured. Add hooks in your settings.json file.': - 'Nenhum hook configurado. Adicione hooks no seu arquivo settings.json.', - 'Configured Hooks ({{count}} total)': 'Hooks Configurados ({{count}} total)', + "Config not loaded.": "Configuração não carregada.", + "Hooks are not enabled. Enable hooks in settings to use this feature.": + "Hooks não estão ativados. Ative hooks nas configurações para usar este recurso.", + "No hooks configured. Add hooks in your settings.json file.": + "Nenhum hook configurado. Adicione hooks no seu arquivo settings.json.", + "Configured Hooks ({{count}} total)": "Hooks Configurados ({{count}} total)", // ============================================================================ // Commands - Session Export // ============================================================================ - 'Export current session message history to a file': - 'Exportar o histórico de mensagens da sessão atual para um arquivo', - 'Export session to HTML format': 'Exportar a sessão para o formato HTML', - 'Export session to JSON format': 'Exportar a sessão para o formato JSON', - 'Export session to JSONL format (one message per line)': - 'Exportar a sessão para o formato JSONL (uma mensagem por linha)', - 'Export session to markdown format': - 'Exportar a sessão para o formato Markdown', + "Export current session message history to a file": + "Exportar o histórico de mensagens da sessão atual para um arquivo", + "Export session to HTML format": "Exportar a sessão para o formato HTML", + "Export session to JSON format": "Exportar a sessão para o formato JSON", + "Export session to JSONL format (one message per line)": + "Exportar a sessão para o formato JSONL (uma mensagem por linha)", + "Export session to markdown format": "Exportar a sessão para o formato Markdown", // ============================================================================ // Commands - Insights // ============================================================================ - 'generate personalized programming insights from your chat history': - 'Gerar insights personalizados de programação a partir do seu histórico de chat', + "generate personalized programming insights from your chat history": + "Gerar insights personalizados de programação a partir do seu histórico de chat", // ============================================================================ // Commands - Session History // ============================================================================ - 'Resume a previous session': 'Retomar uma sessão anterior', - 'Restore a tool call. This will reset the conversation and file history to the state it was in when the tool call was suggested': - 'Restaurar uma chamada de ferramenta. Isso redefinirá o histórico da conversa e dos arquivos para o estado em que a chamada da ferramenta foi sugerida', - 'Could not detect terminal type. Supported terminals: VS Code, Cursor, Windsurf, and Trae.': - 'Não foi possível detectar o tipo de terminal. Terminais suportados: VS Code, Cursor, Windsurf e Trae.', + "Resume a previous session": "Retomar uma sessão anterior", + "Restore a tool call. This will reset the conversation and file history to the state it was in when the tool call was suggested": + "Restaurar uma chamada de ferramenta. Isso redefinirá o histórico da conversa e dos arquivos para o estado em que a chamada da ferramenta foi sugerida", + "Could not detect terminal type. Supported terminals: VS Code, Cursor, Windsurf, and Trae.": + "Não foi possível detectar o tipo de terminal. Terminais suportados: VS Code, Cursor, Windsurf e Trae.", 'Terminal "{{terminal}}" is not supported yet.': 'O terminal "{{terminal}}" ainda não é suportado.', // ============================================================================ // Commands - Language // ============================================================================ - 'Invalid language. Available: {{options}}': - 'Idioma inválido. Disponíveis: {{options}}', - 'Language subcommands do not accept additional arguments.': - 'Subcomandos de idioma não aceitam argumentos adicionais.', - 'Current UI language: {{lang}}': 'Idioma atual da interface: {{lang}}', - 'Current LLM output language: {{lang}}': - 'Idioma atual da saída do LLM: {{lang}}', - 'LLM output language not set': 'Idioma de saída do LLM não definido', - 'Set UI language': 'Definir idioma da interface', - 'Set LLM output language': 'Definir idioma de saída do LLM', - 'Usage: /language ui [{{options}}]': 'Uso: /language ui [{{options}}]', - 'Usage: /language output ': 'Uso: /language output ', - 'Example: /language output 中文': 'Exemplo: /language output Português', - 'Example: /language output English': 'Exemplo: /language output Inglês', - 'Example: /language output 日本語': 'Exemplo: /language output Japonês', - 'Example: /language output Português': 'Exemplo: /language output Português', - 'UI language changed to {{lang}}': - 'Idioma da interface alterado para {{lang}}', - 'LLM output language set to {{lang}}': - 'Idioma de saída do LLM definido para {{lang}}', - 'LLM output language rule file generated at {{path}}': - 'Arquivo de regra de idioma de saída do LLM gerado em {{path}}', - 'Please restart the application for the changes to take effect.': - 'Reinicie o aplicativo para que as alterações tenham efeito.', - 'Failed to generate LLM output language rule file: {{error}}': - 'Falha ao gerar arquivo de regra de idioma de saída do LLM: {{error}}', - 'Invalid command. Available subcommands:': - 'Comando inválido. Subcomandos disponíveis:', - 'Available subcommands:': 'Subcomandos disponíveis:', - 'To request additional UI language packs, please open an issue on GitHub.': - 'Para solicitar pacotes de idiomas de interface adicionais, abra um problema no GitHub.', - 'Available options:': 'Opções disponíveis:', - 'Set UI language to {{name}}': 'Definir idioma da interface para {{name}}', + "Invalid language. Available: {{options}}": "Idioma inválido. Disponíveis: {{options}}", + "Language subcommands do not accept additional arguments.": + "Subcomandos de idioma não aceitam argumentos adicionais.", + "Current UI language: {{lang}}": "Idioma atual da interface: {{lang}}", + "Current LLM output language: {{lang}}": "Idioma atual da saída do LLM: {{lang}}", + "LLM output language not set": "Idioma de saída do LLM não definido", + "Set UI language": "Definir idioma da interface", + "Set LLM output language": "Definir idioma de saída do LLM", + "Usage: /language ui [{{options}}]": "Uso: /language ui [{{options}}]", + "Usage: /language output ": "Uso: /language output ", + "Example: /language output 中文": "Exemplo: /language output Português", + "Example: /language output English": "Exemplo: /language output Inglês", + "Example: /language output 日本語": "Exemplo: /language output Japonês", + "Example: /language output Português": "Exemplo: /language output Português", + "UI language changed to {{lang}}": "Idioma da interface alterado para {{lang}}", + "LLM output language set to {{lang}}": "Idioma de saída do LLM definido para {{lang}}", + "LLM output language rule file generated at {{path}}": + "Arquivo de regra de idioma de saída do LLM gerado em {{path}}", + "Please restart the application for the changes to take effect.": + "Reinicie o aplicativo para que as alterações tenham efeito.", + "Failed to generate LLM output language rule file: {{error}}": + "Falha ao gerar arquivo de regra de idioma de saída do LLM: {{error}}", + "Invalid command. Available subcommands:": "Comando inválido. Subcomandos disponíveis:", + "Available subcommands:": "Subcomandos disponíveis:", + "To request additional UI language packs, please open an issue on GitHub.": + "Para solicitar pacotes de idiomas de interface adicionais, abra um problema no GitHub.", + "Available options:": "Opções disponíveis:", + "Set UI language to {{name}}": "Definir idioma da interface para {{name}}", // ============================================================================ // Commands - Approval Mode // ============================================================================ - 'Tool Approval Mode': 'Modo de Aprovação de Ferramenta', - 'Current approval mode: {{mode}}': 'Modo de aprovação atual: {{mode}}', - 'Available approval modes:': 'Modos de aprovação disponíveis:', - 'Approval mode changed to: {{mode}}': - 'Modo de aprovação alterado para: {{mode}}', - 'Approval mode changed to: {{mode}} (saved to {{scope}} settings{{location}})': - 'Modo de aprovação alterado para: {{mode}} (salvo nas configurações de {{scope}}{{location}})', - 'Usage: /approval-mode [--session|--user|--project]': - 'Uso: /approval-mode [--session|--user|--project]', + "Tool Approval Mode": "Modo de Aprovação de Ferramenta", + "Current approval mode: {{mode}}": "Modo de aprovação atual: {{mode}}", + "Available approval modes:": "Modos de aprovação disponíveis:", + "Approval mode changed to: {{mode}}": "Modo de aprovação alterado para: {{mode}}", + "Approval mode changed to: {{mode}} (saved to {{scope}} settings{{location}})": + "Modo de aprovação alterado para: {{mode}} (salvo nas configurações de {{scope}}{{location}})", + "Usage: /approval-mode [--session|--user|--project]": + "Uso: /approval-mode [--session|--user|--project]", - 'Scope subcommands do not accept additional arguments.': - 'Subcomandos de escopo não aceitam argumentos adicionais.', - 'Plan mode - Analyze only, do not modify files or execute commands': - 'Modo planejamento - Apenas analisa, não modifica arquivos nem executa comandos', - 'Default mode - Require approval for file edits or shell commands': - 'Modo padrão - Exige aprovação para edições de arquivos ou comandos shell', - 'Auto-edit mode - Automatically approve file edits': - 'Modo auto-edição - Aprova automaticamente edições de arquivos', - 'YOLO mode - Automatically approve all tools': - 'Modo YOLO - Aprova automaticamente todas as ferramentas', - '{{mode}} mode': 'Modo {{mode}}', - 'Settings service is not available; unable to persist the approval mode.': - 'Serviço de configurações não disponível; não foi possível persistir o modo de aprovação.', - 'Failed to save approval mode: {{error}}': - 'Falha ao salvar modo de aprovação: {{error}}', - 'Failed to change approval mode: {{error}}': - 'Falha ao alterar modo de aprovação: {{error}}', - 'Apply to current session only (temporary)': - 'Aplicar apenas à sessão atual (temporário)', - 'Persist for this project/workspace': 'Persistir para este projeto/workspace', - 'Persist for this user on this machine': - 'Persistir para este usuário nesta máquina', - 'Analyze only, do not modify files or execute commands': - 'Apenas analisar, não modificar arquivos nem executar comandos', - 'Require approval for file edits or shell commands': - 'Exigir aprovação para edições de arquivos ou comandos shell', - 'Automatically approve file edits': - 'Aprovar automaticamente edições de arquivos', - 'Automatically approve all tools': - 'Aprovar automaticamente todas as ferramentas', - 'Workspace approval mode exists and takes priority. User-level change will have no effect.': - 'O modo de aprovação do workspace existe e tem prioridade. A alteração no nível do usuário não terá efeito.', - 'Apply To': 'Aplicar A', - 'Workspace Settings': 'Configurações do Workspace', + "Scope subcommands do not accept additional arguments.": + "Subcomandos de escopo não aceitam argumentos adicionais.", + "Plan mode - Analyze only, do not modify files or execute commands": + "Modo planejamento - Apenas analisa, não modifica arquivos nem executa comandos", + "Default mode - Require approval for file edits or shell commands": + "Modo padrão - Exige aprovação para edições de arquivos ou comandos shell", + "Auto-edit mode - Automatically approve file edits": + "Modo auto-edição - Aprova automaticamente edições de arquivos", + "YOLO mode - Automatically approve all tools": + "Modo YOLO - Aprova automaticamente todas as ferramentas", + "{{mode}} mode": "Modo {{mode}}", + "Settings service is not available; unable to persist the approval mode.": + "Serviço de configurações não disponível; não foi possível persistir o modo de aprovação.", + "Failed to save approval mode: {{error}}": "Falha ao salvar modo de aprovação: {{error}}", + "Failed to change approval mode: {{error}}": "Falha ao alterar modo de aprovação: {{error}}", + "Apply to current session only (temporary)": "Aplicar apenas à sessão atual (temporário)", + "Persist for this project/workspace": "Persistir para este projeto/workspace", + "Persist for this user on this machine": "Persistir para este usuário nesta máquina", + "Analyze only, do not modify files or execute commands": + "Apenas analisar, não modificar arquivos nem executar comandos", + "Require approval for file edits or shell commands": + "Exigir aprovação para edições de arquivos ou comandos shell", + "Automatically approve file edits": "Aprovar automaticamente edições de arquivos", + "Automatically approve all tools": "Aprovar automaticamente todas as ferramentas", + "Workspace approval mode exists and takes priority. User-level change will have no effect.": + "O modo de aprovação do workspace existe e tem prioridade. A alteração no nível do usuário não terá efeito.", + "Apply To": "Aplicar A", + "Workspace Settings": "Configurações do Workspace", // ============================================================================ // Commands - Memory // ============================================================================ - 'Commands for interacting with memory.': - 'Comandos para interagir com a memória.', - 'Show the current memory contents.': - 'Mostrar os conteúdos atuais da memória.', - 'Show project-level memory contents.': - 'Mostrar conteúdos da memória de nível de projeto.', - 'Show global memory contents.': 'Mostrar conteúdos da memória global.', - 'Add content to project-level memory.': - 'Adicionar conteúdo à memória de nível de projeto.', - 'Add content to global memory.': 'Adicionar conteúdo à memória global.', - 'Refresh the memory from the source.': 'Atualizar a memória da fonte.', - 'Usage: /memory add --project ': - 'Uso: /memory add --project ', - 'Usage: /memory add --global ': - 'Uso: /memory add --global ', + "Commands for interacting with memory.": "Comandos para interagir com a memória.", + "Show the current memory contents.": "Mostrar os conteúdos atuais da memória.", + "Show project-level memory contents.": "Mostrar conteúdos da memória de nível de projeto.", + "Show global memory contents.": "Mostrar conteúdos da memória global.", + "Add content to project-level memory.": "Adicionar conteúdo à memória de nível de projeto.", + "Add content to global memory.": "Adicionar conteúdo à memória global.", + "Refresh the memory from the source.": "Atualizar a memória da fonte.", + "Usage: /memory add --project ": + "Uso: /memory add --project ", + "Usage: /memory add --global ": + "Uso: /memory add --global ", 'Attempting to save to project memory: "{{text}}"': 'Tentando salvar na memória do projeto: "{{text}}"', 'Attempting to save to global memory: "{{text}}"': 'Tentando salvar na memória global: "{{text}}"', - 'Current memory content from {{count}} file(s):': - 'Conteúdo da memória atual de {{count}} arquivo(s):', - 'Memory is currently empty.': 'A memória está vazia no momento.', - 'Project memory file not found or is currently empty.': - 'Arquivo de memória do projeto não encontrado ou está vazio.', - 'Global memory file not found or is currently empty.': - 'Arquivo de memória global não encontrado ou está vazio.', - 'Global memory is currently empty.': - 'A memória global está vazia no momento.', - 'Global memory content:\n\n---\n{{content}}\n---': - 'Conteúdo da memória global:\n\n---\n{{content}}\n---', - 'Project memory content from {{path}}:\n\n---\n{{content}}\n---': - 'Conteúdo da memória do projeto de {{path}}:\n\n---\n{{content}}\n---', - 'Project memory is currently empty.': - 'A memória do projeto está vazia no momento.', - 'Refreshing memory from source files...': - 'Atualizando memória dos arquivos fonte...', - 'Add content to the memory. Use --global for global memory or --project for project memory.': - 'Adicionar conteúdo à memória. Use --global para memória global ou --project para memória do projeto.', - 'Usage: /memory add [--global|--project] ': - 'Uso: /memory add [--global|--project] ', + "Current memory content from {{count}} file(s):": + "Conteúdo da memória atual de {{count}} arquivo(s):", + "Memory is currently empty.": "A memória está vazia no momento.", + "Project memory file not found or is currently empty.": + "Arquivo de memória do projeto não encontrado ou está vazio.", + "Global memory file not found or is currently empty.": + "Arquivo de memória global não encontrado ou está vazio.", + "Global memory is currently empty.": "A memória global está vazia no momento.", + "Global memory content:\n\n---\n{{content}}\n---": + "Conteúdo da memória global:\n\n---\n{{content}}\n---", + "Project memory content from {{path}}:\n\n---\n{{content}}\n---": + "Conteúdo da memória do projeto de {{path}}:\n\n---\n{{content}}\n---", + "Project memory is currently empty.": "A memória do projeto está vazia no momento.", + "Refreshing memory from source files...": "Atualizando memória dos arquivos fonte...", + "Add content to the memory. Use --global for global memory or --project for project memory.": + "Adicionar conteúdo à memória. Use --global para memória global ou --project para memória do projeto.", + "Usage: /memory add [--global|--project] ": + "Uso: /memory add [--global|--project] ", 'Attempting to save to memory {{scope}}: "{{fact}}"': 'Tentando salvar na memória {{scope}}: "{{fact}}"', // ============================================================================ // Commands - MCP // ============================================================================ - 'Authenticate with an OAuth-enabled MCP server': - 'Autenticar com um servidor MCP habilitado para OAuth', - 'List configured MCP servers and tools': - 'Listar servidores e ferramentas MCP configurados', - 'Restarts MCP servers.': 'Reinicia os servidores MCP.', - 'Could not retrieve tool registry.': - 'Não foi possível recuperar o registro de ferramentas.', - 'No MCP servers configured with OAuth authentication.': - 'Nenhum servidor MCP configurado com autenticação OAuth.', - 'MCP servers with OAuth authentication:': - 'Servidores MCP com autenticação OAuth:', - 'Use /mcp auth to authenticate.': - 'Use /mcp auth para autenticar.', + "Authenticate with an OAuth-enabled MCP server": + "Autenticar com um servidor MCP habilitado para OAuth", + "List configured MCP servers and tools": "Listar servidores e ferramentas MCP configurados", + "Restarts MCP servers.": "Reinicia os servidores MCP.", + "Could not retrieve tool registry.": "Não foi possível recuperar o registro de ferramentas.", + "No MCP servers configured with OAuth authentication.": + "Nenhum servidor MCP configurado com autenticação OAuth.", + "MCP servers with OAuth authentication:": "Servidores MCP com autenticação OAuth:", + "Use /mcp auth to authenticate.": + "Use /mcp auth para autenticar.", "MCP server '{{name}}' not found.": "Servidor MCP '{{name}}' não encontrado.", "Successfully authenticated and refreshed tools for '{{name}}'.": "Autenticado com sucesso e ferramentas atualizadas para '{{name}}'.", "Failed to authenticate with MCP server '{{name}}': {{error}}": "Falha ao autenticar com o servidor MCP '{{name}}': {{error}}", - "Re-discovering tools from '{{name}}'...": - "Redescobrindo ferramentas de '{{name}}'...", + "Re-discovering tools from '{{name}}'...": "Redescobrindo ferramentas de '{{name}}'...", "Discovered {{count}} tool(s) from '{{name}}'.": "{{count}} ferramenta(s) descoberta(s) de '{{name}}'.", - 'Authentication complete. Returning to server details...': - 'Autenticação concluída. Retornando aos detalhes do servidor...', - 'Authentication successful.': 'Autenticação bem-sucedida.', - 'If the browser does not open, copy and paste this URL into your browser:': - 'Se o navegador não abrir, copie e cole esta URL no seu navegador:', - 'Make sure to copy the COMPLETE URL - it may wrap across multiple lines.': - '⚠️ Certifique-se de copiar a URL COMPLETA – ela pode ocupar várias linhas.', + "Authentication complete. Returning to server details...": + "Autenticação concluída. Retornando aos detalhes do servidor...", + "Authentication successful.": "Autenticação bem-sucedida.", + "If the browser does not open, copy and paste this URL into your browser:": + "Se o navegador não abrir, copie e cole esta URL no seu navegador:", + "Make sure to copy the COMPLETE URL - it may wrap across multiple lines.": + "⚠️ Certifique-se de copiar a URL COMPLETA – ela pode ocupar várias linhas.", // ============================================================================ // Commands - Chat // ============================================================================ - 'Manage conversation history.': 'Gerenciar histórico de conversas.', - 'List saved conversation checkpoints': - 'Listar checkpoints de conversa salvos', - 'No saved conversation checkpoints found.': - 'Nenhum checkpoint de conversa salvo encontrado.', - 'List of saved conversations:': 'Lista de conversas salvas:', - 'Note: Newest last, oldest first': - 'Nota: Mais novos por último, mais antigos primeiro', - 'Save the current conversation as a checkpoint. Usage: /chat save ': - 'Salvar a conversa atual como um checkpoint. Uso: /chat save ', - 'Missing tag. Usage: /chat save ': 'Tag ausente. Uso: /chat save ', - 'Delete a conversation checkpoint. Usage: /chat delete ': - 'Excluir um checkpoint de conversa. Uso: /chat delete ', - 'Missing tag. Usage: /chat delete ': - 'Tag ausente. Uso: /chat delete ', + "Manage conversation history.": "Gerenciar histórico de conversas.", + "List saved conversation checkpoints": "Listar checkpoints de conversa salvos", + "No saved conversation checkpoints found.": "Nenhum checkpoint de conversa salvo encontrado.", + "List of saved conversations:": "Lista de conversas salvas:", + "Note: Newest last, oldest first": "Nota: Mais novos por último, mais antigos primeiro", + "Save the current conversation as a checkpoint. Usage: /chat save ": + "Salvar a conversa atual como um checkpoint. Uso: /chat save ", + "Missing tag. Usage: /chat save ": "Tag ausente. Uso: /chat save ", + "Delete a conversation checkpoint. Usage: /chat delete ": + "Excluir um checkpoint de conversa. Uso: /chat delete ", + "Missing tag. Usage: /chat delete ": "Tag ausente. Uso: /chat delete ", "Conversation checkpoint '{{tag}}' has been deleted.": "O checkpoint de conversa '{{tag}}' foi excluído.", "Error: No checkpoint found with tag '{{tag}}'.": "Erro: Nenhum checkpoint encontrado com a tag '{{tag}}'.", - 'Resume a conversation from a checkpoint. Usage: /chat resume ': - 'Retomar uma conversa de um checkpoint. Uso: /chat resume ', - 'Missing tag. Usage: /chat resume ': - 'Tag ausente. Uso: /chat resume ', - 'No saved checkpoint found with tag: {{tag}}.': - 'Nenhum checkpoint salvo encontrado com a tag: {{tag}}.', - 'A checkpoint with the tag {{tag}} already exists. Do you want to overwrite it?': - 'Um checkpoint com a tag {{tag}} já existe. Você deseja substituí-lo?', - 'No chat client available to save conversation.': - 'Nenhum cliente de chat disponível para salvar a conversa.', - 'Conversation checkpoint saved with tag: {{tag}}.': - 'Checkpoint de conversa salvo com a tag: {{tag}}.', - 'No conversation found to save.': 'Nenhuma conversa encontrada para salvar.', - 'No chat client available to share conversation.': - 'Nenhum cliente de chat disponível para compartilhar a conversa.', - 'Invalid file format. Only .md and .json are supported.': - 'Formato de arquivo inválido. Apenas .md e .json são suportados.', - 'Error sharing conversation: {{error}}': - 'Erro ao compartilhar conversa: {{error}}', - 'Conversation shared to {{filePath}}': - 'Conversa compartilhada em {{filePath}}', - 'No conversation found to share.': - 'Nenhuma conversa encontrada para compartilhar.', - 'Share the current conversation to a markdown or json file. Usage: /chat share ': - 'Compartilhar a conversa atual para um arquivo markdown ou json. Uso: /chat share ', + "Resume a conversation from a checkpoint. Usage: /chat resume ": + "Retomar uma conversa de um checkpoint. Uso: /chat resume ", + "Missing tag. Usage: /chat resume ": "Tag ausente. Uso: /chat resume ", + "No saved checkpoint found with tag: {{tag}}.": + "Nenhum checkpoint salvo encontrado com a tag: {{tag}}.", + "A checkpoint with the tag {{tag}} already exists. Do you want to overwrite it?": + "Um checkpoint com a tag {{tag}} já existe. Você deseja substituí-lo?", + "No chat client available to save conversation.": + "Nenhum cliente de chat disponível para salvar a conversa.", + "Conversation checkpoint saved with tag: {{tag}}.": + "Checkpoint de conversa salvo com a tag: {{tag}}.", + "No conversation found to save.": "Nenhuma conversa encontrada para salvar.", + "No chat client available to share conversation.": + "Nenhum cliente de chat disponível para compartilhar a conversa.", + "Invalid file format. Only .md and .json are supported.": + "Formato de arquivo inválido. Apenas .md e .json são suportados.", + "Error sharing conversation: {{error}}": "Erro ao compartilhar conversa: {{error}}", + "Conversation shared to {{filePath}}": "Conversa compartilhada em {{filePath}}", + "No conversation found to share.": "Nenhuma conversa encontrada para compartilhar.", + "Share the current conversation to a markdown or json file. Usage: /chat share ": + "Compartilhar a conversa atual para um arquivo markdown ou json. Uso: /chat share ", // ============================================================================ // Commands - Summary // ============================================================================ - 'Generate a project summary and save it to .qwen/PROJECT_SUMMARY.md': - 'Gerar um resumo do projeto e salvá-lo em .qwen/PROJECT_SUMMARY.md', - 'No chat client available to generate summary.': - 'Nenhum cliente de chat disponível para gerar o resumo.', - 'Already generating summary, wait for previous request to complete': - 'Já gerando resumo, aguarde a conclusão da solicitação anterior', - 'No conversation found to summarize.': - 'Nenhuma conversa encontrada para resumir.', - 'Failed to generate project context summary: {{error}}': - 'Falha ao gerar resumo do contexto do projeto: {{error}}', - 'Saved project summary to {{filePathForDisplay}}.': - 'Resumo do projeto salvo em {{filePathForDisplay}}.', - 'Saving project summary...': 'Salvando resumo do projeto...', - 'Generating project summary...': 'Gerando resumo do projeto...', - 'Failed to generate summary - no text content received from LLM response': - 'Falha ao gerar resumo - nenhum conteúdo de texto recebido da resposta do LLM', + "Generate a project summary and save it to .qwen/PROJECT_SUMMARY.md": + "Gerar um resumo do projeto e salvá-lo em .qwen/PROJECT_SUMMARY.md", + "No chat client available to generate summary.": + "Nenhum cliente de chat disponível para gerar o resumo.", + "Already generating summary, wait for previous request to complete": + "Já gerando resumo, aguarde a conclusão da solicitação anterior", + "No conversation found to summarize.": "Nenhuma conversa encontrada para resumir.", + "Failed to generate project context summary: {{error}}": + "Falha ao gerar resumo do contexto do projeto: {{error}}", + "Saved project summary to {{filePathForDisplay}}.": + "Resumo do projeto salvo em {{filePathForDisplay}}.", + "Saving project summary...": "Salvando resumo do projeto...", + "Generating project summary...": "Gerando resumo do projeto...", + "Failed to generate summary - no text content received from LLM response": + "Falha ao gerar resumo - nenhum conteúdo de texto recebido da resposta do LLM", // ============================================================================ // Commands - Model // ============================================================================ - 'Switch the model for this session': 'Trocar o modelo para esta sessão', - 'Set fast model for background tasks': - 'Definir modelo rápido para tarefas em segundo plano', - 'Content generator configuration not available.': - 'Configuração do gerador de conteúdo não disponível.', - 'Authentication type not available.': 'Tipo de autenticação não disponível.', - 'No models available for the current authentication type ({{authType}}).': - 'Nenhum modelo disponível para o tipo de autenticação atual ({{authType}}).', + "Switch the model for this session": "Trocar o modelo para esta sessão", + "Set fast model for background tasks": "Definir modelo rápido para tarefas em segundo plano", + "Content generator configuration not available.": + "Configuração do gerador de conteúdo não disponível.", + "Authentication type not available.": "Tipo de autenticação não disponível.", + "No models available for the current authentication type ({{authType}}).": + "Nenhum modelo disponível para o tipo de autenticação atual ({{authType}}).", // ============================================================================ // Commands - Clear // ============================================================================ - 'Starting a new session, resetting chat, and clearing terminal.': - 'Iniciando uma nova sessão, resetando o chat e limpando o terminal.', - 'Starting a new session and clearing.': - 'Iniciando uma nova sessão e limpando.', + "Starting a new session, resetting chat, and clearing terminal.": + "Iniciando uma nova sessão, resetando o chat e limpando o terminal.", + "Starting a new session and clearing.": "Iniciando uma nova sessão e limpando.", // ============================================================================ // Commands - Compress // ============================================================================ - 'Already compressing, wait for previous request to complete': - 'Já comprimindo, aguarde a conclusão da solicitação anterior', - 'Failed to compress chat history.': 'Falha ao comprimir histórico do chat.', - 'Failed to compress chat history: {{error}}': - 'Falha ao comprimir histórico do chat: {{error}}', - 'Compressing chat history': 'Comprimindo histórico do chat', - 'Chat history compressed from {{originalTokens}} to {{newTokens}} tokens.': - 'Histórico do chat comprimido de {{originalTokens}} para {{newTokens}} tokens.', - 'Compression was not beneficial for this history size.': - 'A compressão não foi benéfica para este tamanho de histórico.', - 'Chat history compression did not reduce size. This may indicate issues with the compression prompt.': - 'A compressão do histórico do chat não reduziu o tamanho. Isso pode indicar problemas com o prompt de compressão.', - 'Could not compress chat history due to a token counting error.': - 'Não foi possível comprimir o histórico do chat devido a um erro de contagem de tokens.', - 'Chat history is already compressed.': - 'O histórico do chat já está comprimido.', + "Already compressing, wait for previous request to complete": + "Já comprimindo, aguarde a conclusão da solicitação anterior", + "Failed to compress chat history.": "Falha ao comprimir histórico do chat.", + "Failed to compress chat history: {{error}}": "Falha ao comprimir histórico do chat: {{error}}", + "Compressing chat history": "Comprimindo histórico do chat", + "Chat history compressed from {{originalTokens}} to {{newTokens}} tokens.": + "Histórico do chat comprimido de {{originalTokens}} para {{newTokens}} tokens.", + "Compression was not beneficial for this history size.": + "A compressão não foi benéfica para este tamanho de histórico.", + "Chat history compression did not reduce size. This may indicate issues with the compression prompt.": + "A compressão do histórico do chat não reduziu o tamanho. Isso pode indicar problemas com o prompt de compressão.", + "Could not compress chat history due to a token counting error.": + "Não foi possível comprimir o histórico do chat devido a um erro de contagem de tokens.", + "Chat history is already compressed.": "O histórico do chat já está comprimido.", // ============================================================================ // Commands - Directory // ============================================================================ - 'Configuration is not available.': 'A configuração não está disponível.', - 'Please provide at least one path to add.': - 'Forneça pelo menos um caminho para adicionar.', - 'The /directory add command is not supported in restrictive sandbox profiles. Please use --include-directories when starting the session instead.': - 'O comando /directory add não é suportado em perfis de sandbox restritivos. Use --include-directories ao iniciar a sessão.', - "Error adding '{{path}}': {{error}}": - "Erro ao adicionar '{{path}}': {{error}}", - 'Successfully added QWEN.md files from the following directories if there are:\n- {{directories}}': - 'Arquivos QWEN.md adicionados com sucesso dos seguintes diretórios, se houverem:\n- {{directories}}', - 'Error refreshing memory: {{error}}': 'Erro ao atualizar memória: {{error}}', - 'Successfully added directories:\n- {{directories}}': - 'Diretórios adicionados com sucesso:\n- {{directories}}', - 'Current workspace directories:\n{{directories}}': - 'Diretórios atuais do workspace:\n{{directories}}', + "Configuration is not available.": "A configuração não está disponível.", + "Please provide at least one path to add.": "Forneça pelo menos um caminho para adicionar.", + "The /directory add command is not supported in restrictive sandbox profiles. Please use --include-directories when starting the session instead.": + "O comando /directory add não é suportado em perfis de sandbox restritivos. Use --include-directories ao iniciar a sessão.", + "Error adding '{{path}}': {{error}}": "Erro ao adicionar '{{path}}': {{error}}", + "Successfully added QWEN.md files from the following directories if there are:\n- {{directories}}": + "Arquivos QWEN.md adicionados com sucesso dos seguintes diretórios, se houverem:\n- {{directories}}", + "Error refreshing memory: {{error}}": "Erro ao atualizar memória: {{error}}", + "Successfully added directories:\n- {{directories}}": + "Diretórios adicionados com sucesso:\n- {{directories}}", + "Current workspace directories:\n{{directories}}": + "Diretórios atuais do workspace:\n{{directories}}", // ============================================================================ // Commands - Docs // ============================================================================ - 'Please open the following URL in your browser to view the documentation:\n{{url}}': - 'Abra a seguinte URL no seu navegador para ver a documentação:\n{{url}}', - 'Opening documentation in your browser: {{url}}': - 'Abrindo documentação no seu navegador: {{url}}', + "Please open the following URL in your browser to view the documentation:\n{{url}}": + "Abra a seguinte URL no seu navegador para ver a documentação:\n{{url}}", + "Opening documentation in your browser: {{url}}": + "Abrindo documentação no seu navegador: {{url}}", // ============================================================================ // Dialogs - Tool Confirmation // ============================================================================ - 'Do you want to proceed?': 'Você deseja prosseguir?', - 'Yes, allow once': 'Sim, permitir uma vez', - 'Allow always': 'Permitir sempre', - Yes: 'Sim', - No: 'Não', - 'No (esc)': 'Não (esc)', - 'Yes, allow always for this session': 'Sim, permitir sempre para esta sessão', + "Do you want to proceed?": "Você deseja prosseguir?", + "Yes, allow once": "Sim, permitir uma vez", + "Allow always": "Permitir sempre", + Yes: "Sim", + No: "Não", + "No (esc)": "Não (esc)", + "Yes, allow always for this session": "Sim, permitir sempre para esta sessão", // MCP Management - Core translations - 'Manage MCP servers': 'Gerenciar servidores MCP', - 'Server Detail': 'Detalhes do servidor', - 'Disable Server': 'Desativar servidor', - Tools: 'Ferramentas', - 'Tool Detail': 'Detalhes da ferramenta', - 'MCP Management': 'Gerenciamento MCP', - 'Loading...': 'Carregando...', - 'Unknown step': 'Etapa desconhecida', - 'Esc to back': 'Esc para voltar', - '↑↓ to navigate · Enter to select · Esc to close': - '↑↓ navegar · Enter selecionar · Esc fechar', - '↑↓ to navigate · Enter to select · Esc to back': - '↑↓ navegar · Enter selecionar · Esc voltar', - '↑↓ to navigate · Enter to confirm · Esc to back': - '↑↓ navegar · Enter confirmar · Esc voltar', - 'User Settings (global)': 'Configurações do usuário (global)', - 'Workspace Settings (project-specific)': - 'Configurações do workspace (específico do projeto)', - 'Disable server:': 'Desativar servidor:', - 'Select where to add the server to the exclude list:': - 'Selecione onde adicionar o servidor à lista de exclusão:', - 'Press Enter to confirm, Esc to cancel': - 'Enter para confirmar, Esc para cancelar', - Disable: 'Desativar', - Enable: 'Ativar', - Authenticate: 'Autenticar', - 'Re-authenticate': 'Reautenticar', - 'Clear Authentication': 'Limpar autenticação', - disabled: 'desativado', - 'Server:': 'Servidor:', - Reconnect: 'Reconectar', - 'View tools': 'Ver ferramentas', - 'Status:': 'Status:', - 'Source:': 'Fonte:', - 'Command:': 'Comando:', - 'Working Directory:': 'Diretório de trabalho:', - 'Capabilities:': 'Capacidades:', - 'No server selected': 'Nenhum servidor selecionado', - '(disabled)': '(desativado)', - 'Error:': 'Erro:', - tool: 'ferramenta', - tools: 'ferramentas', - connected: 'conectado', - connecting: 'conectando', - disconnected: 'desconectado', - error: 'erro', + "Manage MCP servers": "Gerenciar servidores MCP", + "Server Detail": "Detalhes do servidor", + "Disable Server": "Desativar servidor", + Tools: "Ferramentas", + "Tool Detail": "Detalhes da ferramenta", + "MCP Management": "Gerenciamento MCP", + "Loading...": "Carregando...", + "Unknown step": "Etapa desconhecida", + "Esc to back": "Esc para voltar", + "↑↓ to navigate · Enter to select · Esc to close": "↑↓ navegar · Enter selecionar · Esc fechar", + "↑↓ to navigate · Enter to select · Esc to back": "↑↓ navegar · Enter selecionar · Esc voltar", + "↑↓ to navigate · Enter to confirm · Esc to back": "↑↓ navegar · Enter confirmar · Esc voltar", + "User Settings (global)": "Configurações do usuário (global)", + "Workspace Settings (project-specific)": "Configurações do workspace (específico do projeto)", + "Disable server:": "Desativar servidor:", + "Select where to add the server to the exclude list:": + "Selecione onde adicionar o servidor à lista de exclusão:", + "Press Enter to confirm, Esc to cancel": "Enter para confirmar, Esc para cancelar", + Disable: "Desativar", + Enable: "Ativar", + Authenticate: "Autenticar", + "Re-authenticate": "Reautenticar", + "Clear Authentication": "Limpar autenticação", + disabled: "desativado", + "Server:": "Servidor:", + Reconnect: "Reconectar", + "View tools": "Ver ferramentas", + "Status:": "Status:", + "Source:": "Fonte:", + "Command:": "Comando:", + "Working Directory:": "Diretório de trabalho:", + "Capabilities:": "Capacidades:", + "No server selected": "Nenhum servidor selecionado", + "(disabled)": "(desativado)", + "Error:": "Erro:", + tool: "ferramenta", + tools: "ferramentas", + connected: "conectado", + connecting: "conectando", + disconnected: "desconectado", + error: "erro", // MCP Server List - 'User MCPs': 'MCPs do usuário', - 'Project MCPs': 'MCPs do projeto', - 'Extension MCPs': 'MCPs de extensão', - server: 'servidor', - servers: 'servidores', - 'Add MCP servers to your settings to get started.': - 'Adicione servidores MCP às suas configurações para começar.', - 'Run qwen --debug to see error logs': - 'Execute qwen --debug para ver os logs de erro', + "User MCPs": "MCPs do usuário", + "Project MCPs": "MCPs do projeto", + "Extension MCPs": "MCPs de extensão", + server: "servidor", + servers: "servidores", + "Add MCP servers to your settings to get started.": + "Adicione servidores MCP às suas configurações para começar.", + "Run qwen --debug to see error logs": "Execute qwen --debug para ver os logs de erro", // MCP OAuth Authentication - 'OAuth Authentication': 'Autenticação OAuth', - 'Press Enter to start authentication, Esc to go back': - 'Pressione Enter para iniciar a autenticação, Esc para voltar', - 'Authenticating... Please complete the login in your browser.': - 'Autenticando... Por favor, conclua o login no seu navegador.', - 'Press Enter or Esc to go back': 'Pressione Enter ou Esc para voltar', + "OAuth Authentication": "Autenticação OAuth", + "Press Enter to start authentication, Esc to go back": + "Pressione Enter para iniciar a autenticação, Esc para voltar", + "Authenticating... Please complete the login in your browser.": + "Autenticando... Por favor, conclua o login no seu navegador.", + "Press Enter or Esc to go back": "Pressione Enter ou Esc para voltar", // MCP Tool List - 'No tools available for this server.': - 'Nenhuma ferramenta disponível para este servidor.', - destructive: 'destrutivo', - 'read-only': 'somente leitura', - 'open-world': 'mundo aberto', - idempotent: 'idempotente', - 'Tools for {{name}}': 'Ferramentas para {{name}}', - 'Tools for {{serverName}}': 'Ferramentas para {{serverName}}', - '{{current}}/{{total}}': '{{current}}/{{total}}', + "No tools available for this server.": "Nenhuma ferramenta disponível para este servidor.", + destructive: "destrutivo", + "read-only": "somente leitura", + "open-world": "mundo aberto", + idempotent: "idempotente", + "Tools for {{name}}": "Ferramentas para {{name}}", + "Tools for {{serverName}}": "Ferramentas para {{serverName}}", + "{{current}}/{{total}}": "{{current}}/{{total}}", // MCP Tool Detail - required: 'obrigatório', - Type: 'Tipo', - Enum: 'Enumeração', - Parameters: 'Parâmetros', - 'No tool selected': 'Nenhuma ferramenta selecionada', - Annotations: 'Anotações', - Title: 'Título', - 'Read Only': 'Somente leitura', - Destructive: 'Destrutivo', - Idempotent: 'Idempotente', - 'Open World': 'Mundo aberto', - Server: 'Servidor', + required: "obrigatório", + Type: "Tipo", + Enum: "Enumeração", + Parameters: "Parâmetros", + "No tool selected": "Nenhuma ferramenta selecionada", + Annotations: "Anotações", + Title: "Título", + "Read Only": "Somente leitura", + Destructive: "Destrutivo", + Idempotent: "Idempotente", + "Open World": "Mundo aberto", + Server: "Servidor", // Invalid tool related translations - '{{count}} invalid tools': '{{count}} ferramentas inválidas', - invalid: 'inválido', - 'invalid: {{reason}}': 'inválido: {{reason}}', - 'missing name': 'nome ausente', - 'missing description': 'descrição ausente', - '(unnamed)': '(sem nome)', - 'Warning: This tool cannot be called by the LLM': - 'Aviso: Esta ferramenta não pode ser chamada pelo LLM', - Reason: 'Motivo', - 'Tools must have both name and description to be used by the LLM.': - 'As ferramentas devem ter tanto nome quanto descrição para serem usadas pelo LLM.', - 'Modify in progress:': 'Modificação em progresso:', - 'Save and close external editor to continue': - 'Salve e feche o editor externo para continuar', - 'Apply this change?': 'Aplicar esta alteração?', - 'Yes, allow always': 'Sim, permitir sempre', - 'Modify with external editor': 'Modificar com editor externo', - 'No, suggest changes (esc)': 'Não, sugerir alterações (esc)', - "Allow execution of: '{{command}}'?": - "Permitir a execução de: '{{command}}'?", - 'Yes, allow always ...': 'Sim, permitir sempre ...', - 'Always allow in this project': 'Sempre permitir neste projeto', - 'Always allow {{action}} in this project': - 'Sempre permitir {{action}} neste projeto', - 'Always allow for this user': 'Sempre permitir para este usuário', - 'Always allow {{action}} for this user': - 'Sempre permitir {{action}} para este usuário', - 'Yes, restore previous mode ({{mode}})': - 'Sim, restaurar modo anterior ({{mode}})', - 'Yes, and auto-accept edits': 'Sim, e aceitar edições automaticamente', - 'Yes, and manually approve edits': 'Sim, e aprovar edições manualmente', - 'No, keep planning (esc)': 'Não, continuar planejando (esc)', - 'URLs to fetch:': 'URLs para buscar:', - 'MCP Server: {{server}}': 'Servidor MCP: {{server}}', - 'Tool: {{tool}}': 'Ferramenta: {{tool}}', + "{{count}} invalid tools": "{{count}} ferramentas inválidas", + invalid: "inválido", + "invalid: {{reason}}": "inválido: {{reason}}", + "missing name": "nome ausente", + "missing description": "descrição ausente", + "(unnamed)": "(sem nome)", + "Warning: This tool cannot be called by the LLM": + "Aviso: Esta ferramenta não pode ser chamada pelo LLM", + Reason: "Motivo", + "Tools must have both name and description to be used by the LLM.": + "As ferramentas devem ter tanto nome quanto descrição para serem usadas pelo LLM.", + "Modify in progress:": "Modificação em progresso:", + "Save and close external editor to continue": "Salve e feche o editor externo para continuar", + "Apply this change?": "Aplicar esta alteração?", + "Yes, allow always": "Sim, permitir sempre", + "Modify with external editor": "Modificar com editor externo", + "No, suggest changes (esc)": "Não, sugerir alterações (esc)", + "Allow execution of: '{{command}}'?": "Permitir a execução de: '{{command}}'?", + "Yes, allow always ...": "Sim, permitir sempre ...", + "Always allow in this project": "Sempre permitir neste projeto", + "Always allow {{action}} in this project": "Sempre permitir {{action}} neste projeto", + "Always allow for this user": "Sempre permitir para este usuário", + "Always allow {{action}} for this user": "Sempre permitir {{action}} para este usuário", + "Yes, restore previous mode ({{mode}})": "Sim, restaurar modo anterior ({{mode}})", + "Yes, and auto-accept edits": "Sim, e aceitar edições automaticamente", + "Yes, and manually approve edits": "Sim, e aprovar edições manualmente", + "No, keep planning (esc)": "Não, continuar planejando (esc)", + "URLs to fetch:": "URLs para buscar:", + "MCP Server: {{server}}": "Servidor MCP: {{server}}", + "Tool: {{tool}}": "Ferramenta: {{tool}}", 'Allow execution of MCP tool "{{tool}}" from server "{{server}}"?': 'Permitir a execução da ferramenta MCP "{{tool}}" do servidor "{{server}}"?', 'Yes, always allow tool "{{tool}}" from server "{{server}}"': @@ -1208,738 +1117,696 @@ export default { // ============================================================================ // Dialogs - Shell Confirmation // ============================================================================ - 'Shell Command Execution': 'Execução de Comando Shell', - 'A custom command wants to run the following shell commands:': - 'Um comando personalizado deseja executar os seguintes comandos shell:', + "Shell Command Execution": "Execução de Comando Shell", + "A custom command wants to run the following shell commands:": + "Um comando personalizado deseja executar os seguintes comandos shell:", // ============================================================================ // Dialogs - Pro Quota // ============================================================================ - 'Pro quota limit reached for {{model}}.': - 'Limite de cota Pro atingido para {{model}}.', - 'Change auth (executes the /auth command)': - 'Alterar autenticação (executa o comando /auth)', - 'Continue with {{model}}': 'Continuar com {{model}}', + "Pro quota limit reached for {{model}}.": "Limite de cota Pro atingido para {{model}}.", + "Change auth (executes the /auth command)": "Alterar autenticação (executa o comando /auth)", + "Continue with {{model}}": "Continuar com {{model}}", // ============================================================================ // Dialogs - Welcome Back // ============================================================================ - 'Current Plan:': 'Plano Atual:', - 'Progress: {{done}}/{{total}} tasks completed': - 'Progresso: {{done}}/{{total}} tarefas concluídas', - ', {{inProgress}} in progress': ', {{inProgress}} em progresso', - 'Pending Tasks:': 'Tarefas Pendentes:', - 'What would you like to do?': 'O que você gostaria de fazer?', - 'Choose how to proceed with your session:': - 'Escolha como proceder com sua sessão:', - 'Start new chat session': 'Iniciar nova sessão de chat', - 'Continue previous conversation': 'Continuar conversa anterior', - '👋 Welcome back! (Last updated: {{timeAgo}})': - '👋 Bem-vindo de volta! (Última atualização: {{timeAgo}})', - '🎯 Overall Goal:': '🎯 Objetivo Geral:', + "Current Plan:": "Plano Atual:", + "Progress: {{done}}/{{total}} tasks completed": + "Progresso: {{done}}/{{total}} tarefas concluídas", + ", {{inProgress}} in progress": ", {{inProgress}} em progresso", + "Pending Tasks:": "Tarefas Pendentes:", + "What would you like to do?": "O que você gostaria de fazer?", + "Choose how to proceed with your session:": "Escolha como proceder com sua sessão:", + "Start new chat session": "Iniciar nova sessão de chat", + "Continue previous conversation": "Continuar conversa anterior", + "👋 Welcome back! (Last updated: {{timeAgo}})": + "👋 Bem-vindo de volta! (Última atualização: {{timeAgo}})", + "🎯 Overall Goal:": "🎯 Objetivo Geral:", // ============================================================================ // Dialogs - Auth // ============================================================================ - 'Get started': 'Começar', - 'Select Authentication Method': 'Selecionar Método de Autenticação', - 'OpenAI API key is required to use OpenAI authentication.': - 'A chave da API do OpenAI é necessária para usar a autenticação do OpenAI.', - 'You must select an auth method to proceed. Press Ctrl+C again to exit.': - 'Você deve selecionar um método de autenticação para prosseguir. Pressione Ctrl+C novamente para sair.', - 'Terms of Services and Privacy Notice': - 'Termos de Serviço e Aviso de Privacidade', - 'Paid \u00B7 Up to 6,000 requests/5 hrs \u00B7 All Alibaba Cloud Coding Plan Models': - 'Pago \u00B7 Até 6.000 solicitações/5 hrs \u00B7 Todos os modelos Alibaba Cloud Coding Plan', - 'Alibaba Cloud Coding Plan': 'Alibaba Cloud Coding Plan', - 'Bring your own API key': 'Traga sua própria chave API', - 'API-KEY': 'API-KEY', - 'Use coding plan credentials or your own api-keys/providers.': - 'Use credenciais do Coding Plan ou suas próprias chaves API/provedores.', - OpenAI: 'OpenAI', - 'Failed to login. Message: {{message}}': - 'Falha ao fazer login. Mensagem: {{message}}', - 'Authentication is enforced to be {{enforcedType}}, but you are currently using {{currentType}}.': - 'A autenticação é forçada para {{enforcedType}}, mas você está usando {{currentType}} no momento.', - 'Please visit this URL to authorize:': 'Visite esta URL para autorizar:', - 'Or scan the QR code below:': 'Ou escaneie o código QR abaixo:', - 'Waiting for authorization': 'Aguardando autorização', - 'Time remaining:': 'Tempo restante:', - '(Press ESC or CTRL+C to cancel)': '(Pressione ESC ou CTRL+C para cancelar)', - 'OAuth token expired (over {{seconds}} seconds). Please select authentication method again.': - 'Token OAuth expirado (mais de {{seconds}} segundos). Selecione o método de autenticação novamente.', - 'Press any key to return to authentication type selection.': - 'Pressione qualquer tecla para retornar à seleção do tipo de autenticação.', - 'Authentication timed out. Please try again.': - 'A autenticação expirou. Tente novamente.', - 'Waiting for auth... (Press ESC or CTRL+C to cancel)': - 'Aguardando autenticação... (Pressione ESC ou CTRL+C para cancelar)', - 'Missing API key for OpenAI-compatible auth. Set settings.security.auth.apiKey, or set the {{envKeyHint}} environment variable.': - 'Chave de API ausente para autenticação compatível com OpenAI. Defina settings.security.auth.apiKey ou a variável de ambiente {{envKeyHint}}.', - '{{envKeyHint}} environment variable not found.': - 'Variável de ambiente {{envKeyHint}} não encontrada.', - '{{envKeyHint}} environment variable not found. Please set it in your .env file or environment variables.': - 'Variável de ambiente {{envKeyHint}} não encontrada. Defina-a no seu arquivo .env ou variáveis de ambiente.', - '{{envKeyHint}} environment variable not found (or set settings.security.auth.apiKey). Please set it in your .env file or environment variables.': - 'Variável de ambiente {{envKeyHint}} não encontrada (ou defina settings.security.auth.apiKey). Defina-a no seu arquivo .env ou variáveis de ambiente.', - 'Missing API key for OpenAI-compatible auth. Set the {{envKeyHint}} environment variable.': - 'Chave de API ausente para autenticação compatível com OpenAI. Defina a variável de ambiente {{envKeyHint}}.', - 'Anthropic provider missing required baseUrl in modelProviders[].baseUrl.': - 'Provedor Anthropic sem a baseUrl necessária em modelProviders[].baseUrl.', - 'ANTHROPIC_BASE_URL environment variable not found.': - 'Variável de ambiente ANTHROPIC_BASE_URL não encontrada.', - 'Invalid auth method selected.': - 'Método de autenticação inválido selecionado.', - 'Failed to authenticate. Message: {{message}}': - 'Falha ao autenticar. Mensagem: {{message}}', - 'Authenticated successfully with {{authType}} credentials.': - 'Autenticado com sucesso com credenciais {{authType}}.', + "Get started": "Começar", + "Select Authentication Method": "Selecionar Método de Autenticação", + "OpenAI API key is required to use OpenAI authentication.": + "A chave da API do OpenAI é necessária para usar a autenticação do OpenAI.", + "You must select an auth method to proceed. Press Ctrl+C again to exit.": + "Você deve selecionar um método de autenticação para prosseguir. Pressione Ctrl+C novamente para sair.", + "Terms of Services and Privacy Notice": "Termos de Serviço e Aviso de Privacidade", + "Paid \u00B7 Up to 6,000 requests/5 hrs \u00B7 All Alibaba Cloud Coding Plan Models": + "Pago \u00B7 Até 6.000 solicitações/5 hrs \u00B7 Todos os modelos Alibaba Cloud Coding Plan", + "Alibaba Cloud Coding Plan": "Alibaba Cloud Coding Plan", + "Bring your own API key": "Traga sua própria chave API", + "API-KEY": "API-KEY", + "Use coding plan credentials or your own api-keys/providers.": + "Use credenciais do Coding Plan ou suas próprias chaves API/provedores.", + OpenAI: "OpenAI", + "Failed to login. Message: {{message}}": "Falha ao fazer login. Mensagem: {{message}}", + "Authentication is enforced to be {{enforcedType}}, but you are currently using {{currentType}}.": + "A autenticação é forçada para {{enforcedType}}, mas você está usando {{currentType}} no momento.", + "Please visit this URL to authorize:": "Visite esta URL para autorizar:", + "Or scan the QR code below:": "Ou escaneie o código QR abaixo:", + "Waiting for authorization": "Aguardando autorização", + "Time remaining:": "Tempo restante:", + "(Press ESC or CTRL+C to cancel)": "(Pressione ESC ou CTRL+C para cancelar)", + "OAuth token expired (over {{seconds}} seconds). Please select authentication method again.": + "Token OAuth expirado (mais de {{seconds}} segundos). Selecione o método de autenticação novamente.", + "Press any key to return to authentication type selection.": + "Pressione qualquer tecla para retornar à seleção do tipo de autenticação.", + "Authentication timed out. Please try again.": "A autenticação expirou. Tente novamente.", + "Waiting for auth... (Press ESC or CTRL+C to cancel)": + "Aguardando autenticação... (Pressione ESC ou CTRL+C para cancelar)", + "Missing API key for OpenAI-compatible auth. Set settings.security.auth.apiKey, or set the {{envKeyHint}} environment variable.": + "Chave de API ausente para autenticação compatível com OpenAI. Defina settings.security.auth.apiKey ou a variável de ambiente {{envKeyHint}}.", + "{{envKeyHint}} environment variable not found.": + "Variável de ambiente {{envKeyHint}} não encontrada.", + "{{envKeyHint}} environment variable not found. Please set it in your .env file or environment variables.": + "Variável de ambiente {{envKeyHint}} não encontrada. Defina-a no seu arquivo .env ou variáveis de ambiente.", + "{{envKeyHint}} environment variable not found (or set settings.security.auth.apiKey). Please set it in your .env file or environment variables.": + "Variável de ambiente {{envKeyHint}} não encontrada (ou defina settings.security.auth.apiKey). Defina-a no seu arquivo .env ou variáveis de ambiente.", + "Missing API key for OpenAI-compatible auth. Set the {{envKeyHint}} environment variable.": + "Chave de API ausente para autenticação compatível com OpenAI. Defina a variável de ambiente {{envKeyHint}}.", + "Anthropic provider missing required baseUrl in modelProviders[].baseUrl.": + "Provedor Anthropic sem a baseUrl necessária em modelProviders[].baseUrl.", + "ANTHROPIC_BASE_URL environment variable not found.": + "Variável de ambiente ANTHROPIC_BASE_URL não encontrada.", + "Invalid auth method selected.": "Método de autenticação inválido selecionado.", + "Failed to authenticate. Message: {{message}}": "Falha ao autenticar. Mensagem: {{message}}", + "Authenticated successfully with {{authType}} credentials.": + "Autenticado com sucesso com credenciais {{authType}}.", 'Invalid QWEN_DEFAULT_AUTH_TYPE value: "{{value}}". Valid values are: {{validValues}}': 'Valor QWEN_DEFAULT_AUTH_TYPE inválido: "{{value}}". Valores válidos são: {{validValues}}', - 'OpenAI Configuration Required': 'Configuração do OpenAI Necessária', - 'Please enter your OpenAI configuration. You can get an API key from': - 'Insira sua configuração do OpenAI. Você pode obter uma chave de API de', - 'API Key:': 'Chave da API:', - 'Invalid credentials: {{errorMessage}}': - 'Credenciais inválidas: {{errorMessage}}', - 'Failed to validate credentials': 'Falha ao validar credenciais', - 'Press Enter to continue, Tab/↑↓ to navigate, Esc to cancel': - 'Pressione Enter para continuar, Tab/↑↓ para navegar, Esc para cancelar', + "OpenAI Configuration Required": "Configuração do OpenAI Necessária", + "Please enter your OpenAI configuration. You can get an API key from": + "Insira sua configuração do OpenAI. Você pode obter uma chave de API de", + "API Key:": "Chave da API:", + "Invalid credentials: {{errorMessage}}": "Credenciais inválidas: {{errorMessage}}", + "Failed to validate credentials": "Falha ao validar credenciais", + "Press Enter to continue, Tab/↑↓ to navigate, Esc to cancel": + "Pressione Enter para continuar, Tab/↑↓ para navegar, Esc para cancelar", // ============================================================================ // Dialogs - Model // ============================================================================ - 'Select Model': 'Selecionar Modelo', - '(Press Esc to close)': '(Pressione Esc para fechar)', - 'Current (effective) configuration': 'Configuração atual (efetiva)', - AuthType: 'AuthType', - 'API Key': 'Chave da API', - unset: 'não definido', - '(default)': '(padrão)', - '(set)': '(definido)', - '(not set)': '(não definido)', - Modality: 'Modalidade', - 'Context Window': 'Janela de Contexto', - text: 'texto', - 'text-only': 'somente texto', - image: 'imagem', - pdf: 'PDF', - audio: 'áudio', - video: 'vídeo', - 'not set': 'não definido', - none: 'nenhum', - unknown: 'desconhecido', + "Select Model": "Selecionar Modelo", + "(Press Esc to close)": "(Pressione Esc para fechar)", + "Current (effective) configuration": "Configuração atual (efetiva)", + AuthType: "AuthType", + "API Key": "Chave da API", + unset: "não definido", + "(default)": "(padrão)", + "(set)": "(definido)", + "(not set)": "(não definido)", + Modality: "Modalidade", + "Context Window": "Janela de Contexto", + text: "texto", + "text-only": "somente texto", + image: "imagem", + pdf: "PDF", + audio: "áudio", + video: "vídeo", + "not set": "não definido", + none: "nenhum", + unknown: "desconhecido", "Failed to switch model to '{{modelId}}'.\n\n{{error}}": "Falha ao trocar o modelo para '{{modelId}}'.\n\n{{error}}", - 'Qwen 3.6 Plus — efficient hybrid model with leading coding performance': - 'Qwen 3.6 Plus — modelo híbrido eficiente com desempenho líder em programação', - 'The latest Qwen Vision model from Alibaba Cloud ModelStudio (version: qwen3-vl-plus-2025-09-23)': - 'O modelo Qwen Vision mais recente do Alibaba Cloud ModelStudio (versão: qwen3-vl-plus-2025-09-23)', + "Qwen 3.6 Plus — efficient hybrid model with leading coding performance": + "Qwen 3.6 Plus — modelo híbrido eficiente com desempenho líder em programação", + "The latest Qwen Vision model from Alibaba Cloud ModelStudio (version: qwen3-vl-plus-2025-09-23)": + "O modelo Qwen Vision mais recente do Alibaba Cloud ModelStudio (versão: qwen3-vl-plus-2025-09-23)", // ============================================================================ // Dialogs - Permissions // ============================================================================ - 'Manage folder trust settings': - 'Gerenciar configurações de confiança de pasta', - 'Manage permission rules': 'Gerenciar regras de permissão', - Allow: 'Permitir', - Ask: 'Perguntar', - Deny: 'Negar', - Workspace: 'Área de trabalho', + "Manage folder trust settings": "Gerenciar configurações de confiança de pasta", + "Manage permission rules": "Gerenciar regras de permissão", + Allow: "Permitir", + Ask: "Perguntar", + Deny: "Negar", + Workspace: "Área de trabalho", "Qwen Code won't ask before using allowed tools.": - 'O Qwen Code não perguntará antes de usar ferramentas permitidas.', - 'Qwen Code will ask before using these tools.': - 'O Qwen Code perguntará antes de usar essas ferramentas.', - 'Qwen Code is not allowed to use denied tools.': - 'O Qwen Code não tem permissão para usar ferramentas negadas.', - 'Manage trusted directories for this workspace.': - 'Gerenciar diretórios confiáveis para esta área de trabalho.', - 'Any use of the {{tool}} tool': 'Qualquer uso da ferramenta {{tool}}', - "{{tool}} commands matching '{{pattern}}'": - "Comandos {{tool}} correspondentes a '{{pattern}}'", - 'From user settings': 'Das configurações do usuário', - 'From project settings': 'Das configurações do projeto', - 'From session': 'Da sessão', - 'Project settings (local)': 'Configurações do projeto (local)', - 'Saved in .qwen/settings.local.json': 'Salvo em .qwen/settings.local.json', - 'Project settings': 'Configurações do projeto', - 'Checked in at .qwen/settings.json': 'Registrado em .qwen/settings.json', - 'User settings': 'Configurações do usuário', - 'Saved in at ~/.qwen/settings.json': 'Salvo em ~/.qwen/settings.json', - 'Add a new rule…': 'Adicionar nova regra…', - 'Add {{type}} permission rule': 'Adicionar regra de permissão {{type}}', - 'Permission rules are a tool name, optionally followed by a specifier in parentheses.': - 'Regras de permissão são um nome de ferramenta, opcionalmente seguido por um especificador entre parênteses.', - 'e.g.,': 'ex.', - or: 'ou', - 'Enter permission rule…': 'Insira a regra de permissão…', - 'Enter to submit · Esc to cancel': 'Enter para enviar · Esc para cancelar', - 'Where should this rule be saved?': 'Onde esta regra deve ser salva?', - 'Enter to confirm · Esc to cancel': - 'Enter para confirmar · Esc para cancelar', - 'Delete {{type}} rule?': 'Excluir regra {{type}}?', - 'Are you sure you want to delete this permission rule?': - 'Tem certeza de que deseja excluir esta regra de permissão?', - 'Permissions:': 'Permissões:', - '(←/→ or tab to cycle)': '(←/→ ou Tab para alternar)', - 'Press ↑↓ to navigate · Enter to select · Type to search · Esc to cancel': - '↑↓ para navegar · Enter para selecionar · Digite para pesquisar · Esc para cancelar', - 'Search…': 'Pesquisar…', - 'Use /trust to manage folder trust settings for this workspace.': - 'Use /trust para gerenciar as configurações de confiança de pasta desta área de trabalho.', + "O Qwen Code não perguntará antes de usar ferramentas permitidas.", + "Qwen Code will ask before using these tools.": + "O Qwen Code perguntará antes de usar essas ferramentas.", + "Qwen Code is not allowed to use denied tools.": + "O Qwen Code não tem permissão para usar ferramentas negadas.", + "Manage trusted directories for this workspace.": + "Gerenciar diretórios confiáveis para esta área de trabalho.", + "Any use of the {{tool}} tool": "Qualquer uso da ferramenta {{tool}}", + "{{tool}} commands matching '{{pattern}}'": "Comandos {{tool}} correspondentes a '{{pattern}}'", + "From user settings": "Das configurações do usuário", + "From project settings": "Das configurações do projeto", + "From session": "Da sessão", + "Project settings (local)": "Configurações do projeto (local)", + "Saved in .qwen/settings.local.json": "Salvo em .qwen/settings.local.json", + "Project settings": "Configurações do projeto", + "Checked in at .qwen/settings.json": "Registrado em .qwen/settings.json", + "User settings": "Configurações do usuário", + "Saved in at ~/.qwen/settings.json": "Salvo em ~/.qwen/settings.json", + "Add a new rule…": "Adicionar nova regra…", + "Add {{type}} permission rule": "Adicionar regra de permissão {{type}}", + "Permission rules are a tool name, optionally followed by a specifier in parentheses.": + "Regras de permissão são um nome de ferramenta, opcionalmente seguido por um especificador entre parênteses.", + "e.g.,": "ex.", + or: "ou", + "Enter permission rule…": "Insira a regra de permissão…", + "Enter to submit · Esc to cancel": "Enter para enviar · Esc para cancelar", + "Where should this rule be saved?": "Onde esta regra deve ser salva?", + "Enter to confirm · Esc to cancel": "Enter para confirmar · Esc para cancelar", + "Delete {{type}} rule?": "Excluir regra {{type}}?", + "Are you sure you want to delete this permission rule?": + "Tem certeza de que deseja excluir esta regra de permissão?", + "Permissions:": "Permissões:", + "(←/→ or tab to cycle)": "(←/→ ou Tab para alternar)", + "Press ↑↓ to navigate · Enter to select · Type to search · Esc to cancel": + "↑↓ para navegar · Enter para selecionar · Digite para pesquisar · Esc para cancelar", + "Search…": "Pesquisar…", + "Use /trust to manage folder trust settings for this workspace.": + "Use /trust para gerenciar as configurações de confiança de pasta desta área de trabalho.", // Workspace directory management - 'Add directory…': 'Adicionar diretório…', - 'Add directory to workspace': 'Adicionar diretório à área de trabalho', - 'Qwen Code can read files in the workspace, and make edits when auto-accept edits is on.': - 'O Qwen Code pode ler arquivos na área de trabalho e fazer edições quando a aceitação automática está ativada.', - 'Qwen Code will be able to read files in this directory and make edits when auto-accept edits is on.': - 'O Qwen Code poderá ler arquivos neste diretório e fazer edições quando a aceitação automática está ativada.', - 'Enter the path to the directory:': 'Insira o caminho do diretório:', - 'Enter directory path…': 'Insira o caminho do diretório…', - 'Tab to complete · Enter to add · Esc to cancel': - 'Tab para completar · Enter para adicionar · Esc para cancelar', - 'Remove directory?': 'Remover diretório?', - 'Are you sure you want to remove this directory from the workspace?': - 'Tem certeza de que deseja remover este diretório da área de trabalho?', - ' (Original working directory)': ' (Diretório de trabalho original)', - ' (from settings)': ' (das configurações)', - 'Directory does not exist.': 'O diretório não existe.', - 'Path is not a directory.': 'O caminho não é um diretório.', - 'This directory is already in the workspace.': - 'Este diretório já está na área de trabalho.', - 'Already covered by existing directory: {{dir}}': - 'Já coberto pelo diretório existente: {{dir}}', + "Add directory…": "Adicionar diretório…", + "Add directory to workspace": "Adicionar diretório à área de trabalho", + "Qwen Code can read files in the workspace, and make edits when auto-accept edits is on.": + "O Qwen Code pode ler arquivos na área de trabalho e fazer edições quando a aceitação automática está ativada.", + "Qwen Code will be able to read files in this directory and make edits when auto-accept edits is on.": + "O Qwen Code poderá ler arquivos neste diretório e fazer edições quando a aceitação automática está ativada.", + "Enter the path to the directory:": "Insira o caminho do diretório:", + "Enter directory path…": "Insira o caminho do diretório…", + "Tab to complete · Enter to add · Esc to cancel": + "Tab para completar · Enter para adicionar · Esc para cancelar", + "Remove directory?": "Remover diretório?", + "Are you sure you want to remove this directory from the workspace?": + "Tem certeza de que deseja remover este diretório da área de trabalho?", + " (Original working directory)": " (Diretório de trabalho original)", + " (from settings)": " (das configurações)", + "Directory does not exist.": "O diretório não existe.", + "Path is not a directory.": "O caminho não é um diretório.", + "This directory is already in the workspace.": "Este diretório já está na área de trabalho.", + "Already covered by existing directory: {{dir}}": "Já coberto pelo diretório existente: {{dir}}", // ============================================================================ // Status Bar // ============================================================================ - 'Using:': 'Usando:', - '{{count}} open file': '{{count}} arquivo aberto', - '{{count}} open files': '{{count}} arquivos abertos', - '(ctrl+g to view)': '(ctrl+g para ver)', - '{{count}} {{name}} file': '{{count}} arquivo {{name}}', - '{{count}} {{name}} files': '{{count}} arquivos {{name}}', - '{{count}} MCP server': '{{count}} servidor MCP', - '{{count}} MCP servers': '{{count}} servidores MCP', - '{{count}} Blocked': '{{count}} Bloqueados', - '(ctrl+t to view)': '(ctrl+t para ver)', - '(ctrl+t to toggle)': '(ctrl+t para alternar)', - 'Press Ctrl+C again to exit.': 'Pressione Ctrl+C novamente para sair.', - 'Press Ctrl+D again to exit.': 'Pressione Ctrl+D novamente para sair.', - 'Press Esc again to clear.': 'Pressione Esc novamente para limpar.', + "Using:": "Usando:", + "{{count}} open file": "{{count}} arquivo aberto", + "{{count}} open files": "{{count}} arquivos abertos", + "(ctrl+g to view)": "(ctrl+g para ver)", + "{{count}} {{name}} file": "{{count}} arquivo {{name}}", + "{{count}} {{name}} files": "{{count}} arquivos {{name}}", + "{{count}} MCP server": "{{count}} servidor MCP", + "{{count}} MCP servers": "{{count}} servidores MCP", + "{{count}} Blocked": "{{count}} Bloqueados", + "(ctrl+t to view)": "(ctrl+t para ver)", + "(ctrl+t to toggle)": "(ctrl+t para alternar)", + "Press Ctrl+C again to exit.": "Pressione Ctrl+C novamente para sair.", + "Press Ctrl+D again to exit.": "Pressione Ctrl+D novamente para sair.", + "Press Esc again to clear.": "Pressione Esc novamente para limpar.", // ============================================================================ // MCP Status // ============================================================================ - 'No MCP servers configured.': 'Nenhum servidor MCP configurado.', - '⏳ MCP servers are starting up ({{count}} initializing)...': - '⏳ Servidores MCP estão iniciando ({{count}} inicializando)...', - 'Note: First startup may take longer. Tool availability will update automatically.': - 'Nota: A primeira inicialização pode demorar mais. A disponibilidade da ferramenta será atualizada automaticamente.', - 'Configured MCP servers:': 'Servidores MCP configurados:', - Ready: 'Pronto', - 'Starting... (first startup may take longer)': - 'Iniciando... (a primeira inicialização pode demorar mais)', - Disconnected: 'Desconectado', - '{{count}} tool': '{{count}} ferramenta', - '{{count}} tools': '{{count}} ferramentas', - '{{count}} prompt': '{{count}} prompt', - '{{count}} prompts': '{{count}} prompts', - '(from {{extensionName}})': '(de {{extensionName}})', - OAuth: 'OAuth', - 'OAuth expired': 'OAuth expirado', - 'OAuth not authenticated': 'OAuth não autenticado', - 'tools and prompts will appear when ready': - 'ferramentas e prompts aparecerão quando estiverem prontos', - '{{count}} tools cached': '{{count}} ferramentas em cache', - 'Tools:': 'Ferramentas:', - 'Parameters:': 'Parâmetros:', - 'Prompts:': 'Prompts:', - Blocked: 'Bloqueado', - '💡 Tips:': '💡 Dicas:', - Use: 'Use', - 'to show server and tool descriptions': - 'para mostrar descrições de servidores e ferramentas', - 'to show tool parameter schemas': - 'para mostrar esquemas de parâmetros de ferramentas', - 'to hide descriptions': 'para ocultar descrições', - 'to authenticate with OAuth-enabled servers': - 'para autenticar com servidores habilitados para OAuth', - Press: 'Pressione', - 'to toggle tool descriptions on/off': - 'para alternar descrições de ferramentas ligadas/desligadas', + "No MCP servers configured.": "Nenhum servidor MCP configurado.", + "⏳ MCP servers are starting up ({{count}} initializing)...": + "⏳ Servidores MCP estão iniciando ({{count}} inicializando)...", + "Note: First startup may take longer. Tool availability will update automatically.": + "Nota: A primeira inicialização pode demorar mais. A disponibilidade da ferramenta será atualizada automaticamente.", + "Configured MCP servers:": "Servidores MCP configurados:", + Ready: "Pronto", + "Starting... (first startup may take longer)": + "Iniciando... (a primeira inicialização pode demorar mais)", + Disconnected: "Desconectado", + "{{count}} tool": "{{count}} ferramenta", + "{{count}} tools": "{{count}} ferramentas", + "{{count}} prompt": "{{count}} prompt", + "{{count}} prompts": "{{count}} prompts", + "(from {{extensionName}})": "(de {{extensionName}})", + OAuth: "OAuth", + "OAuth expired": "OAuth expirado", + "OAuth not authenticated": "OAuth não autenticado", + "tools and prompts will appear when ready": + "ferramentas e prompts aparecerão quando estiverem prontos", + "{{count}} tools cached": "{{count}} ferramentas em cache", + "Tools:": "Ferramentas:", + "Parameters:": "Parâmetros:", + "Prompts:": "Prompts:", + Blocked: "Bloqueado", + "💡 Tips:": "💡 Dicas:", + Use: "Use", + "to show server and tool descriptions": "para mostrar descrições de servidores e ferramentas", + "to show tool parameter schemas": "para mostrar esquemas de parâmetros de ferramentas", + "to hide descriptions": "para ocultar descrições", + "to authenticate with OAuth-enabled servers": + "para autenticar com servidores habilitados para OAuth", + Press: "Pressione", + "to toggle tool descriptions on/off": + "para alternar descrições de ferramentas ligadas/desligadas", "Starting OAuth authentication for MCP server '{{name}}'...": "Iniciando autenticação OAuth para servidor MCP '{{name}}'...", - 'Restarting MCP servers...': 'Reiniciando servidores MCP...', + "Restarting MCP servers...": "Reiniciando servidores MCP...", // ============================================================================ // Startup Tips // ============================================================================ - 'Tips:': 'Dicas:', - 'Use /compress when the conversation gets long to summarize history and free up context.': - 'Use /compress quando a conversa ficar longa para resumir o histórico e liberar contexto.', - 'Start a fresh idea with /clear or /new; the previous session stays available in history.': - 'Comece uma nova ideia com /clear ou /new; a sessão anterior permanece disponível no histórico.', - 'Use /bug to submit issues to the maintainers when something goes off.': - 'Use /bug para enviar problemas aos mantenedores quando algo der errado.', - 'Switch auth type quickly with /auth.': - 'Troque o tipo de autenticação rapidamente com /auth.', - 'You can run any shell commands from Qwen Code using ! (e.g. !ls).': - 'Você pode executar quaisquer comandos shell do Qwen Code usando ! (ex: !ls).', - 'Type / to open the command popup; Tab autocompletes slash commands and saved prompts.': - 'Digite / para abrir o popup de comandos; Tab autocompleta comandos de barra e prompts salvos.', - 'You can resume a previous conversation by running qwen --continue or qwen --resume.': - 'Você pode retomar uma conversa anterior executando qwen --continue ou qwen --resume.', - 'You can switch permission mode quickly with Shift+Tab or /approval-mode.': - 'Você pode alternar o modo de permissão rapidamente com Shift+Tab ou /approval-mode.', - 'Try /insight to generate personalized insights from your chat history.': - 'Experimente /insight para gerar insights personalizados do seu histórico de conversas.', + "Tips:": "Dicas:", + "Use /compress when the conversation gets long to summarize history and free up context.": + "Use /compress quando a conversa ficar longa para resumir o histórico e liberar contexto.", + "Start a fresh idea with /clear or /new; the previous session stays available in history.": + "Comece uma nova ideia com /clear ou /new; a sessão anterior permanece disponível no histórico.", + "Use /bug to submit issues to the maintainers when something goes off.": + "Use /bug para enviar problemas aos mantenedores quando algo der errado.", + "Switch auth type quickly with /auth.": "Troque o tipo de autenticação rapidamente com /auth.", + "You can run any shell commands from Qwen Code using ! (e.g. !ls).": + "Você pode executar quaisquer comandos shell do Qwen Code usando ! (ex: !ls).", + "Type / to open the command popup; Tab autocompletes slash commands and saved prompts.": + "Digite / para abrir o popup de comandos; Tab autocompleta comandos de barra e prompts salvos.", + "You can resume a previous conversation by running qwen --continue or qwen --resume.": + "Você pode retomar uma conversa anterior executando qwen --continue ou qwen --resume.", + "You can switch permission mode quickly with Shift+Tab or /approval-mode.": + "Você pode alternar o modo de permissão rapidamente com Shift+Tab ou /approval-mode.", + "Try /insight to generate personalized insights from your chat history.": + "Experimente /insight para gerar insights personalizados do seu histórico de conversas.", // ============================================================================ // Exit Screen / Stats // ============================================================================ - 'Agent powering down. Goodbye!': 'Agente desligando. Adeus!', - 'To continue this session, run': 'Para continuar esta sessão, execute', - 'Interaction Summary': 'Resumo da Interação', - 'Session ID:': 'ID da Sessão:', - 'Tool Calls:': 'Chamadas de Ferramenta:', - 'Success Rate:': 'Taxa de Sucesso:', - 'User Agreement:': 'Acordo do Usuário:', - reviewed: 'revisado', - 'Code Changes:': 'Alterações de Código:', - Performance: 'Desempenho', - 'Wall Time:': 'Tempo Total:', - 'Agent Active:': 'Agente Ativo:', - 'API Time:': 'Tempo de API:', - 'Tool Time:': 'Tempo de Ferramenta:', - 'Session Stats': 'Estatísticas da Sessão', - 'Model Usage': 'Uso do Modelo', - Reqs: 'Reqs', - 'Input Tokens': 'Tokens de Entrada', - 'Output Tokens': 'Tokens de Saída', - 'Savings Highlight:': 'Destaque de Economia:', - 'of input tokens were served from the cache, reducing costs.': - 'de tokens de entrada foram servidos do cache, reduzindo custos.', - 'Tip: For a full token breakdown, run `/stats model`.': - 'Dica: Para um detalhamento completo de tokens, execute `/stats model`.', - 'Model Stats For Nerds': 'Estatísticas de Modelo Para Nerds', - 'Tool Stats For Nerds': 'Estatísticas de Ferramenta Para Nerds', - Metric: 'Métrica', - API: 'API', - Requests: 'Solicitações', - Errors: 'Erros', - 'Avg Latency': 'Latência Média', - Tokens: 'Tokens', - Total: 'Total', - Prompt: 'Prompt', - Cached: 'Cacheado', - Thoughts: 'Pensamentos', - Tool: 'Ferramenta', - Output: 'Saída', - 'No API calls have been made in this session.': - 'Nenhuma chamada de API foi feita nesta sessão.', - 'Tool Name': 'Nome da Ferramenta', - Calls: 'Chamadas', - 'Success Rate': 'Taxa de Sucesso', - 'Avg Duration': 'Duração Média', - 'User Decision Summary': 'Resumo de Decisão do Usuário', - 'Total Reviewed Suggestions:': 'Total de Sugestões Revisadas:', - ' » Accepted:': ' » Aceitas:', - ' » Rejected:': ' » Rejeitadas:', - ' » Modified:': ' » Modificadas:', - ' Overall Agreement Rate:': ' Taxa Geral de Acordo:', - 'No tool calls have been made in this session.': - 'Nenhuma chamada de ferramenta foi feita nesta sessão.', - 'Session start time is unavailable, cannot calculate stats.': - 'Hora de início da sessão indisponível, não é possível calcular estatísticas.', + "Agent powering down. Goodbye!": "Agente desligando. Adeus!", + "To continue this session, run": "Para continuar esta sessão, execute", + "Interaction Summary": "Resumo da Interação", + "Session ID:": "ID da Sessão:", + "Tool Calls:": "Chamadas de Ferramenta:", + "Success Rate:": "Taxa de Sucesso:", + "User Agreement:": "Acordo do Usuário:", + reviewed: "revisado", + "Code Changes:": "Alterações de Código:", + Performance: "Desempenho", + "Wall Time:": "Tempo Total:", + "Agent Active:": "Agente Ativo:", + "API Time:": "Tempo de API:", + "Tool Time:": "Tempo de Ferramenta:", + "Session Stats": "Estatísticas da Sessão", + "Model Usage": "Uso do Modelo", + Reqs: "Reqs", + "Input Tokens": "Tokens de Entrada", + "Output Tokens": "Tokens de Saída", + "Savings Highlight:": "Destaque de Economia:", + "of input tokens were served from the cache, reducing costs.": + "de tokens de entrada foram servidos do cache, reduzindo custos.", + "Tip: For a full token breakdown, run `/stats model`.": + "Dica: Para um detalhamento completo de tokens, execute `/stats model`.", + "Model Stats For Nerds": "Estatísticas de Modelo Para Nerds", + "Tool Stats For Nerds": "Estatísticas de Ferramenta Para Nerds", + Metric: "Métrica", + API: "API", + Requests: "Solicitações", + Errors: "Erros", + "Avg Latency": "Latência Média", + Tokens: "Tokens", + Total: "Total", + Prompt: "Prompt", + Cached: "Cacheado", + Thoughts: "Pensamentos", + Tool: "Ferramenta", + Output: "Saída", + "No API calls have been made in this session.": "Nenhuma chamada de API foi feita nesta sessão.", + "Tool Name": "Nome da Ferramenta", + Calls: "Chamadas", + "Success Rate": "Taxa de Sucesso", + "Avg Duration": "Duração Média", + "User Decision Summary": "Resumo de Decisão do Usuário", + "Total Reviewed Suggestions:": "Total de Sugestões Revisadas:", + " » Accepted:": " » Aceitas:", + " » Rejected:": " » Rejeitadas:", + " » Modified:": " » Modificadas:", + " Overall Agreement Rate:": " Taxa Geral de Acordo:", + "No tool calls have been made in this session.": + "Nenhuma chamada de ferramenta foi feita nesta sessão.", + "Session start time is unavailable, cannot calculate stats.": + "Hora de início da sessão indisponível, não é possível calcular estatísticas.", // ============================================================================ // Command Format Migration // ============================================================================ - 'Command Format Migration': 'Migração de Formato de Comando', - 'Found {{count}} TOML command file:': - 'Encontrado {{count}} arquivo de comando TOML:', - 'Found {{count}} TOML command files:': - 'Encontrados {{count}} arquivos de comando TOML:', - '... and {{count}} more': '... e mais {{count}}', - 'The TOML format is deprecated. Would you like to migrate them to Markdown format?': - 'O formato TOML está obsoleto. Você gostaria de migrá-los para o formato Markdown?', - '(Backups will be created and original files will be preserved)': - '(Backups serão criados e arquivos originais serão preservados)', + "Command Format Migration": "Migração de Formato de Comando", + "Found {{count}} TOML command file:": "Encontrado {{count}} arquivo de comando TOML:", + "Found {{count}} TOML command files:": "Encontrados {{count}} arquivos de comando TOML:", + "... and {{count}} more": "... e mais {{count}}", + "The TOML format is deprecated. Would you like to migrate them to Markdown format?": + "O formato TOML está obsoleto. Você gostaria de migrá-los para o formato Markdown?", + "(Backups will be created and original files will be preserved)": + "(Backups serão criados e arquivos originais serão preservados)", // ============================================================================ // Loading Phrases // ============================================================================ - 'Waiting for user confirmation...': 'Aguardando confirmação do usuário...', - '(esc to cancel, {{time}})': '(esc para cancelar, {{time}})', + "Waiting for user confirmation...": "Aguardando confirmação do usuário...", + "(esc to cancel, {{time}})": "(esc para cancelar, {{time}})", WITTY_LOADING_PHRASES: [ - 'Estou com sorte', - 'Enviando maravilhas...', - 'Pintando os serifos de volta...', - 'Navegando pelo mofo limoso...', - 'Consultando os espíritos digitais...', - 'Reticulando splines...', - 'Aquecendo os hamsters da IA...', - 'Perguntando à concha mágica...', - 'Gerando réplica espirituosa...', - 'Polindo os algoritmos...', - 'Não apresse a perfeição (ou meu código)...', - 'Preparando bytes frescos...', - 'Contando elétrons...', - 'Engajando processadores cognitivos...', - 'Verificando erros de sintaxe no universo...', - 'Um momento, otimizando o humor...', - 'Embaralhando piadas...', - 'Desembaraçando redes neurais...', - 'Compilando brilhantismo...', - 'Carregando humor.exe...', - 'Invocando a nuvem da sabedoria...', - 'Preparando uma resposta espirituosa...', - 'Só um segundo, estou depurando a realidade...', - 'Confundindo as opções...', - 'Sintonizando as frequências cósmicas...', - 'Criando uma resposta digna da sua paciência...', - 'Compilando os 1s e 0s...', - 'Resolvendo dependências... e crises existenciais...', - 'Desfragmentando memórias... tanto RAM quanto pessoais...', - 'Reiniciando o módulo de humor...', - 'Fazendo cache do essencial (principalmente memes de gatos)...', - 'Otimizando para velocidade absurda', - 'Trocando bits... não conte para os bytes...', - 'Coletando lixo... volto já...', - 'Montando a internet...', - 'Convertendo café em código...', - 'Atualizando a sintaxe da realidade...', - 'Reconectando as sinapses...', - 'Procurando um ponto e vírgula perdido...', - 'Lubrificando as engrenagens da máquina...', - 'Pré-aquecendo os servidores...', - 'Calibrando o capacitor de fluxo...', - 'Engajando o motor de improbabilidade...', - 'Canalizando a Força...', - 'Alinhando as estrelas para uma resposta ideal...', - 'Assim dizemos todos...', - 'Carregando a próxima grande ideia...', - 'Só um momento, estou na zona...', - 'Preparando para deslumbrá-lo com brilhantismo...', - 'Só um tique, estou polindo minha inteligência...', - 'Segure firme, estou criando uma obra-prima...', - 'Só um instante, estou depurando o universo...', - 'Só um momento, estou alinhando os pixels...', - 'Só um segundo, estou otimizando o humor...', - 'Só um momento, estou ajustando os algoritmos...', - 'Velocidade de dobra engajada...', - 'Minerando mais cristais de Dilithium...', - 'Não entre em pânico...', - 'Seguindo o coelho branco...', - 'A verdade está lá fora... em algum lugar...', - 'Soprando o cartucho...', - 'Carregando... Faça um barrel roll!', - 'Aguardando o respawn...', - 'Terminando a Kessel Run em menos de 12 parsecs...', - 'O bolo não é uma mentira, só ainda está carregando...', - 'Mexendo na tela de criação de personagem...', - 'Só um momento, estou encontrando o meme certo...', + "Estou com sorte", + "Enviando maravilhas...", + "Pintando os serifos de volta...", + "Navegando pelo mofo limoso...", + "Consultando os espíritos digitais...", + "Reticulando splines...", + "Aquecendo os hamsters da IA...", + "Perguntando à concha mágica...", + "Gerando réplica espirituosa...", + "Polindo os algoritmos...", + "Não apresse a perfeição (ou meu código)...", + "Preparando bytes frescos...", + "Contando elétrons...", + "Engajando processadores cognitivos...", + "Verificando erros de sintaxe no universo...", + "Um momento, otimizando o humor...", + "Embaralhando piadas...", + "Desembaraçando redes neurais...", + "Compilando brilhantismo...", + "Carregando humor.exe...", + "Invocando a nuvem da sabedoria...", + "Preparando uma resposta espirituosa...", + "Só um segundo, estou depurando a realidade...", + "Confundindo as opções...", + "Sintonizando as frequências cósmicas...", + "Criando uma resposta digna da sua paciência...", + "Compilando os 1s e 0s...", + "Resolvendo dependências... e crises existenciais...", + "Desfragmentando memórias... tanto RAM quanto pessoais...", + "Reiniciando o módulo de humor...", + "Fazendo cache do essencial (principalmente memes de gatos)...", + "Otimizando para velocidade absurda", + "Trocando bits... não conte para os bytes...", + "Coletando lixo... volto já...", + "Montando a internet...", + "Convertendo café em código...", + "Atualizando a sintaxe da realidade...", + "Reconectando as sinapses...", + "Procurando um ponto e vírgula perdido...", + "Lubrificando as engrenagens da máquina...", + "Pré-aquecendo os servidores...", + "Calibrando o capacitor de fluxo...", + "Engajando o motor de improbabilidade...", + "Canalizando a Força...", + "Alinhando as estrelas para uma resposta ideal...", + "Assim dizemos todos...", + "Carregando a próxima grande ideia...", + "Só um momento, estou na zona...", + "Preparando para deslumbrá-lo com brilhantismo...", + "Só um tique, estou polindo minha inteligência...", + "Segure firme, estou criando uma obra-prima...", + "Só um instante, estou depurando o universo...", + "Só um momento, estou alinhando os pixels...", + "Só um segundo, estou otimizando o humor...", + "Só um momento, estou ajustando os algoritmos...", + "Velocidade de dobra engajada...", + "Minerando mais cristais de Dilithium...", + "Não entre em pânico...", + "Seguindo o coelho branco...", + "A verdade está lá fora... em algum lugar...", + "Soprando o cartucho...", + "Carregando... Faça um barrel roll!", + "Aguardando o respawn...", + "Terminando a Kessel Run em menos de 12 parsecs...", + "O bolo não é uma mentira, só ainda está carregando...", + "Mexendo na tela de criação de personagem...", + "Só um momento, estou encontrando o meme certo...", "Pressionando 'A' para continuar...", - 'Pastoreando gatos digitais...', - 'Polindo os pixels...', - 'Encontrando um trocadilho adequado para a tela de carregamento...', - 'Distraindo você com esta frase espirituosa...', - 'Quase lá... provavelmente...', - 'Nossos hamsters estão trabalhando o mais rápido que podem...', - 'Dando um tapinha na cabeça do Cloudy...', - 'Acariciando o gato...', - 'Dando um Rickroll no meu chefe...', - 'Never gonna give you up, never gonna let you down...', - 'Tocando o baixo...', - 'Provando as amoras...', - 'Estou indo longe, estou indo pela velocidade...', - 'Isso é vida real? Ou é apenas fantasia?...', - 'Tenho um bom pressentimento sobre isso...', - 'Cutucando o urso...', - 'Fazendo pesquisa sobre os últimos memes...', - 'Descobrindo como tornar isso mais espirituoso...', - 'Hmmm... deixe-me pensar...', - 'O que você chama de um peixe sem olhos? Um pxe...', - 'Por que o computador foi à terapia? Porque tinha muitos bytes...', - 'Por que programadores não gostam da natureza? Porque tem muitos bugs...', - 'Por que programadores preferem o modo escuro? Porque a luz atrai bugs...', - 'Por que o desenvolvedor faliu? Porque usou todo o seu cache...', - 'O que você pode fazer com um lápis quebrado? Nada, ele não tem ponta...', - 'Aplicando manutenção percussiva...', - 'Procurando a orientação correta do USB...', - 'Garantindo que a fumaça mágica permaneça dentro dos fios...', - 'Tentando sair do Vim...', - 'Girando a roda do hamster...', - 'Isso não é um bug, é um recurso não documentado...', - 'Engajar.', - 'Eu voltarei... com uma resposta.', - 'Meu outro processo é uma TARDIS...', - 'Comungando com o espírito da máquina...', - 'Deixando os pensamentos marinarem...', - 'Lembrei agora onde coloquei minhas chaves...', - 'Ponderando a orbe...', - 'Eu vi coisas que vocês não acreditariam... como um usuário que lê mensagens de carregamento.', - 'Iniciando olhar pensativo...', - 'Qual é o lanche favorito de um computador? Microchips.', - 'Por que desenvolvedores Java usam óculos? Porque eles não C#.', - 'Carregando o laser... pew pew!', - 'Dividindo por zero... só brincando!', - 'Procurando por um supervisor adulto... digo, processando.', - 'Fazendo bip boop.', - 'Buffering... porque até as IAs precisam de um momento.', - 'Entrelaçando partículas quânticas para uma resposta mais rápida...', - 'Polindo o cromo... nos algoritmos.', - 'Você não está entretido? (Trabalhando nisso!)', - 'Invocando os gremlins do código... para ajudar, é claro.', - 'Só esperando o som da conexão discada terminar...', - 'Recalibrando o humorômetro.', - 'Minha outra tela de carregamento é ainda mais engraçada.', - 'Tenho quase certeza que tem um gato andando no teclado em algum lugar...', - 'Aumentando... Aumentando... Ainda carregando.', - 'Não é um bug, é um recurso... desta tela de carregamento.', - 'Você já tentou desligar e ligar de novo? (A tela de carregamento, não eu.)', - 'Construindo pilares adicionais...', + "Pastoreando gatos digitais...", + "Polindo os pixels...", + "Encontrando um trocadilho adequado para a tela de carregamento...", + "Distraindo você com esta frase espirituosa...", + "Quase lá... provavelmente...", + "Nossos hamsters estão trabalhando o mais rápido que podem...", + "Dando um tapinha na cabeça do Cloudy...", + "Acariciando o gato...", + "Dando um Rickroll no meu chefe...", + "Never gonna give you up, never gonna let you down...", + "Tocando o baixo...", + "Provando as amoras...", + "Estou indo longe, estou indo pela velocidade...", + "Isso é vida real? Ou é apenas fantasia?...", + "Tenho um bom pressentimento sobre isso...", + "Cutucando o urso...", + "Fazendo pesquisa sobre os últimos memes...", + "Descobrindo como tornar isso mais espirituoso...", + "Hmmm... deixe-me pensar...", + "O que você chama de um peixe sem olhos? Um pxe...", + "Por que o computador foi à terapia? Porque tinha muitos bytes...", + "Por que programadores não gostam da natureza? Porque tem muitos bugs...", + "Por que programadores preferem o modo escuro? Porque a luz atrai bugs...", + "Por que o desenvolvedor faliu? Porque usou todo o seu cache...", + "O que você pode fazer com um lápis quebrado? Nada, ele não tem ponta...", + "Aplicando manutenção percussiva...", + "Procurando a orientação correta do USB...", + "Garantindo que a fumaça mágica permaneça dentro dos fios...", + "Tentando sair do Vim...", + "Girando a roda do hamster...", + "Isso não é um bug, é um recurso não documentado...", + "Engajar.", + "Eu voltarei... com uma resposta.", + "Meu outro processo é uma TARDIS...", + "Comungando com o espírito da máquina...", + "Deixando os pensamentos marinarem...", + "Lembrei agora onde coloquei minhas chaves...", + "Ponderando a orbe...", + "Eu vi coisas que vocês não acreditariam... como um usuário que lê mensagens de carregamento.", + "Iniciando olhar pensativo...", + "Qual é o lanche favorito de um computador? Microchips.", + "Por que desenvolvedores Java usam óculos? Porque eles não C#.", + "Carregando o laser... pew pew!", + "Dividindo por zero... só brincando!", + "Procurando por um supervisor adulto... digo, processando.", + "Fazendo bip boop.", + "Buffering... porque até as IAs precisam de um momento.", + "Entrelaçando partículas quânticas para uma resposta mais rápida...", + "Polindo o cromo... nos algoritmos.", + "Você não está entretido? (Trabalhando nisso!)", + "Invocando os gremlins do código... para ajudar, é claro.", + "Só esperando o som da conexão discada terminar...", + "Recalibrando o humorômetro.", + "Minha outra tela de carregamento é ainda mais engraçada.", + "Tenho quase certeza que tem um gato andando no teclado em algum lugar...", + "Aumentando... Aumentando... Ainda carregando.", + "Não é um bug, é um recurso... desta tela de carregamento.", + "Você já tentou desligar e ligar de novo? (A tela de carregamento, não eu.)", + "Construindo pilares adicionais...", ], // ============================================================================ // Extension Settings Input // ============================================================================ - 'Enter value...': 'Digite o valor...', - 'Enter sensitive value...': 'Digite o valor sensível...', - 'Press Enter to submit, Escape to cancel': - 'Pressione Enter para enviar, Escape para cancelar', + "Enter value...": "Digite o valor...", + "Enter sensitive value...": "Digite o valor sensível...", + "Press Enter to submit, Escape to cancel": "Pressione Enter para enviar, Escape para cancelar", // ============================================================================ // Command Migration Tool // ============================================================================ - 'Markdown file already exists: {{filename}}': - 'Arquivo Markdown já existe: {{filename}}', - 'TOML Command Format Deprecation Notice': - 'Aviso de Obsolescência do Formato de Comando TOML', - 'Found {{count}} command file(s) in TOML format:': - 'Encontrado(s) {{count}} arquivo(s) de comando no formato TOML:', - 'The TOML format for commands is being deprecated in favor of Markdown format.': - 'O formato TOML para comandos está sendo descontinuado em favor do formato Markdown.', - 'Markdown format is more readable and easier to edit.': - 'O formato Markdown é mais legível e fácil de editar.', - 'You can migrate these files automatically using:': - 'Você pode migrar esses arquivos automaticamente usando:', - 'Or manually convert each file:': 'Ou converter manualmente cada arquivo:', - 'TOML: prompt = "..." / description = "..."': - 'TOML: prompt = "..." / description = "..."', - 'Markdown: YAML frontmatter + content': - 'Markdown: YAML frontmatter + conteúdo', - 'The migration tool will:': 'A ferramenta de migração irá:', - 'Convert TOML files to Markdown': 'Converter arquivos TOML para Markdown', - 'Create backups of original files': 'Criar backups dos arquivos originais', - 'Preserve all command functionality': - 'Preservar toda a funcionalidade do comando', - 'TOML format will continue to work for now, but migration is recommended.': - 'O formato TOML continuará a funcionar por enquanto, mas a migração é recomendada.', + "Markdown file already exists: {{filename}}": "Arquivo Markdown já existe: {{filename}}", + "TOML Command Format Deprecation Notice": "Aviso de Obsolescência do Formato de Comando TOML", + "Found {{count}} command file(s) in TOML format:": + "Encontrado(s) {{count}} arquivo(s) de comando no formato TOML:", + "The TOML format for commands is being deprecated in favor of Markdown format.": + "O formato TOML para comandos está sendo descontinuado em favor do formato Markdown.", + "Markdown format is more readable and easier to edit.": + "O formato Markdown é mais legível e fácil de editar.", + "You can migrate these files automatically using:": + "Você pode migrar esses arquivos automaticamente usando:", + "Or manually convert each file:": "Ou converter manualmente cada arquivo:", + 'TOML: prompt = "..." / description = "..."': 'TOML: prompt = "..." / description = "..."', + "Markdown: YAML frontmatter + content": "Markdown: YAML frontmatter + conteúdo", + "The migration tool will:": "A ferramenta de migração irá:", + "Convert TOML files to Markdown": "Converter arquivos TOML para Markdown", + "Create backups of original files": "Criar backups dos arquivos originais", + "Preserve all command functionality": "Preservar toda a funcionalidade do comando", + "TOML format will continue to work for now, but migration is recommended.": + "O formato TOML continuará a funcionar por enquanto, mas a migração é recomendada.", // ============================================================================ // Extensions - Explore Command // ============================================================================ - 'Open extensions page in your browser': - 'Abrir página de extensões no seu navegador', - 'Unknown extensions source: {{source}}.': - 'Fonte de extensões desconhecida: {{source}}.', - 'Would open extensions page in your browser: {{url}} (skipped in test environment)': - 'Abriria a página de extensões no seu navegador: {{url}} (pulado no ambiente de teste)', - 'View available extensions at {{url}}': - 'Ver extensões disponíveis em {{url}}', - 'Opening extensions page in your browser: {{url}}': - 'Abrindo página de extensões no seu navegador: {{url}}', - 'Failed to open browser. Check out the extensions gallery at {{url}}': - 'Falha ao abrir o navegador. Confira a galeria de extensões em {{url}}', + "Open extensions page in your browser": "Abrir página de extensões no seu navegador", + "Unknown extensions source: {{source}}.": "Fonte de extensões desconhecida: {{source}}.", + "Would open extensions page in your browser: {{url}} (skipped in test environment)": + "Abriria a página de extensões no seu navegador: {{url}} (pulado no ambiente de teste)", + "View available extensions at {{url}}": "Ver extensões disponíveis em {{url}}", + "Opening extensions page in your browser: {{url}}": + "Abrindo página de extensões no seu navegador: {{url}}", + "Failed to open browser. Check out the extensions gallery at {{url}}": + "Falha ao abrir o navegador. Confira a galeria de extensões em {{url}}", // ============================================================================ // Custom API Key Configuration // ============================================================================ - 'You can configure your API key and models in settings.json': - 'Você pode configurar sua chave de API e modelos em settings.json', - 'Refer to the documentation for setup instructions': - 'Consulte a documentação para instruções de configuração', + "You can configure your API key and models in settings.json": + "Você pode configurar sua chave de API e modelos em settings.json", + "Refer to the documentation for setup instructions": + "Consulte a documentação para instruções de configuração", // ============================================================================ // Coding Plan Authentication // ============================================================================ - 'API key cannot be empty.': 'A chave de API não pode estar vazia.', - 'You can get your Coding Plan API key here': - 'Você pode obter sua chave de API do Coding Plan aqui', - 'New model configurations are available for Alibaba Cloud Coding Plan. Update now?': - 'Novas configurações de modelo estão disponíveis para o Alibaba Cloud Coding Plan. Atualizar agora?', - 'Coding Plan configuration updated successfully. New models are now available.': - 'Configuração do Coding Plan atualizada com sucesso. Novos modelos agora estão disponíveis.', - 'Coding Plan API key not found. Please re-authenticate with Coding Plan.': - 'Chave de API do Coding Plan não encontrada. Por favor, re-autentique com o Coding Plan.', - 'Failed to update Coding Plan configuration: {{message}}': - 'Falha ao atualizar a configuração do Coding Plan: {{message}}', + "API key cannot be empty.": "A chave de API não pode estar vazia.", + "You can get your Coding Plan API key here": + "Você pode obter sua chave de API do Coding Plan aqui", + "New model configurations are available for Alibaba Cloud Coding Plan. Update now?": + "Novas configurações de modelo estão disponíveis para o Alibaba Cloud Coding Plan. Atualizar agora?", + "Coding Plan configuration updated successfully. New models are now available.": + "Configuração do Coding Plan atualizada com sucesso. Novos modelos agora estão disponíveis.", + "Coding Plan API key not found. Please re-authenticate with Coding Plan.": + "Chave de API do Coding Plan não encontrada. Por favor, re-autentique com o Coding Plan.", + "Failed to update Coding Plan configuration: {{message}}": + "Falha ao atualizar a configuração do Coding Plan: {{message}}", // ============================================================================ // Auth Dialog - View Titles and Labels // ============================================================================ - 'Coding Plan': 'Coding Plan', + "Coding Plan": "Coding Plan", "Paste your api key of ModelStudio Coding Plan and you're all set!": - 'Cole sua chave de API do ModelStudio Coding Plan e pronto!', - Custom: 'Personalizado', - 'More instructions about configuring `modelProviders` manually.': - 'Mais instruções sobre como configurar `modelProviders` manualmente.', - 'Select API-KEY configuration mode:': - 'Selecione o modo de configuração da API-KEY:', - '(Press Escape to go back)': '(Pressione Escape para voltar)', - '(Press Enter to submit, Escape to cancel)': - '(Pressione Enter para enviar, Escape para cancelar)', - 'More instructions please check:': 'Mais instruções, consulte:', - 'Select Region for Coding Plan': 'Selecionar região do Coding Plan', - 'Choose based on where your account is registered': - 'Escolha com base em onde sua conta está registrada', - 'Enter Coding Plan API Key': 'Inserir chave de API do Coding Plan', + "Cole sua chave de API do ModelStudio Coding Plan e pronto!", + Custom: "Personalizado", + "More instructions about configuring `modelProviders` manually.": + "Mais instruções sobre como configurar `modelProviders` manualmente.", + "Select API-KEY configuration mode:": "Selecione o modo de configuração da API-KEY:", + "(Press Escape to go back)": "(Pressione Escape para voltar)", + "(Press Enter to submit, Escape to cancel)": + "(Pressione Enter para enviar, Escape para cancelar)", + "More instructions please check:": "Mais instruções, consulte:", + "Select Region for Coding Plan": "Selecionar região do Coding Plan", + "Choose based on where your account is registered": + "Escolha com base em onde sua conta está registrada", + "Enter Coding Plan API Key": "Inserir chave de API do Coding Plan", // ============================================================================ // Coding Plan International Updates // ============================================================================ - 'New model configurations are available for {{region}}. Update now?': - 'Novas configurações de modelo estão disponíveis para o {{region}}. Atualizar agora?', + "New model configurations are available for {{region}}. Update now?": + "Novas configurações de modelo estão disponíveis para o {{region}}. Atualizar agora?", '{{region}} configuration updated successfully. Model switched to "{{model}}".': 'Configuração do {{region}} atualizada com sucesso. Modelo alterado para "{{model}}".', - 'Authenticated successfully with {{region}}. API key and model configs saved to settings.json (backed up).': - 'Autenticado com sucesso com {{region}}. Chave de API e configurações de modelo salvas em settings.json (com backup).', + "Authenticated successfully with {{region}}. API key and model configs saved to settings.json (backed up).": + "Autenticado com sucesso com {{region}}. Chave de API e configurações de modelo salvas em settings.json (com backup).", // ============================================================================ // Context Usage Component // ============================================================================ - 'Context Usage': 'Uso do Contexto', - 'No API response yet. Send a message to see actual usage.': - 'Ainda não há resposta da API. Envie uma mensagem para ver o uso real.', - 'Estimated pre-conversation overhead': 'Sobrecarga estimada pré-conversa', - 'Context window': 'Janela de Contexto', - tokens: 'tokens', - Used: 'Usado', - Free: 'Livre', - 'Autocompact buffer': 'Buffer de autocompactação', - 'Usage by category': 'Uso por categoria', - 'System prompt': 'Prompt do sistema', - 'Built-in tools': 'Ferramentas integradas', - 'MCP tools': 'Ferramentas MCP', - 'Memory files': 'Arquivos de memória', - Skills: 'Habilidades', - Messages: 'Mensagens', - 'Show context window usage breakdown.': - 'Exibe a divisão de uso da janela de contexto.', - 'Run /context detail for per-item breakdown.': - 'Execute /context detail para detalhamento por item.', - active: 'ativo', - 'body loaded': 'conteúdo carregado', - memory: 'memória', - '{{region}} configuration updated successfully.': - 'Configuração do {{region}} atualizada com sucesso.', - 'Authenticated successfully with {{region}}. API key and model configs saved to settings.json.': - 'Autenticado com sucesso com {{region}}. Chave de API e configurações de modelo salvas em settings.json.', - 'Tip: Use /model to switch between available Coding Plan models.': - 'Dica: Use /model para alternar entre os modelos disponíveis do Coding Plan.', + "Context Usage": "Uso do Contexto", + "No API response yet. Send a message to see actual usage.": + "Ainda não há resposta da API. Envie uma mensagem para ver o uso real.", + "Estimated pre-conversation overhead": "Sobrecarga estimada pré-conversa", + "Context window": "Janela de Contexto", + tokens: "tokens", + Used: "Usado", + Free: "Livre", + "Autocompact buffer": "Buffer de autocompactação", + "Usage by category": "Uso por categoria", + "System prompt": "Prompt do sistema", + "Built-in tools": "Ferramentas integradas", + "MCP tools": "Ferramentas MCP", + "Memory files": "Arquivos de memória", + Skills: "Habilidades", + Messages: "Mensagens", + "Show context window usage breakdown.": "Exibe a divisão de uso da janela de contexto.", + "Run /context detail for per-item breakdown.": + "Execute /context detail para detalhamento por item.", + active: "ativo", + "body loaded": "conteúdo carregado", + memory: "memória", + "{{region}} configuration updated successfully.": + "Configuração do {{region}} atualizada com sucesso.", + "Authenticated successfully with {{region}}. API key and model configs saved to settings.json.": + "Autenticado com sucesso com {{region}}. Chave de API e configurações de modelo salvas em settings.json.", + "Tip: Use /model to switch between available Coding Plan models.": + "Dica: Use /model para alternar entre os modelos disponíveis do Coding Plan.", // ============================================================================ // Ask User Question Tool // ============================================================================ - 'Please answer the following question(s):': - 'Por favor, responda à(s) seguinte(s) pergunta(s):', - 'Cannot ask user questions in non-interactive mode. Please run in interactive mode to use this tool.': - 'Não é possível fazer perguntas ao usuário no modo não interativo. Por favor, execute no modo interativo para usar esta ferramenta.', - 'User declined to answer the questions.': - 'O usuário recusou responder às perguntas.', - 'User has provided the following answers:': - 'O usuário forneceu as seguintes respostas:', - 'Failed to process user answers:': - 'Falha ao processar as respostas do usuário:', - 'Type something...': 'Digite algo...', - Submit: 'Enviar', - 'Submit answers': 'Enviar respostas', - Cancel: 'Cancelar', - 'Your answers:': 'Suas respostas:', - '(not answered)': '(não respondido)', - 'Ready to submit your answers?': 'Pronto para enviar suas respostas?', - '↑/↓: Navigate | ←/→: Switch tabs | Enter: Select': - '↑/↓: Navegar | ←/→: Alternar abas | Enter: Selecionar', - '↑/↓: Navigate | ←/→: Switch tabs | Space/Enter: Toggle | Esc: Cancel': - '↑/↓: Navegar | ←/→: Alternar abas | Space/Enter: Alternar | Esc: Cancelar', - '↑/↓: Navigate | Space/Enter: Toggle | Esc: Cancel': - '↑/↓: Navegar | Space/Enter: Alternar | Esc: Cancelar', - '↑/↓: Navigate | Enter: Select | Esc: Cancel': - '↑/↓: Navegar | Enter: Selecionar | Esc: Cancelar', + "Please answer the following question(s):": "Por favor, responda à(s) seguinte(s) pergunta(s):", + "Cannot ask user questions in non-interactive mode. Please run in interactive mode to use this tool.": + "Não é possível fazer perguntas ao usuário no modo não interativo. Por favor, execute no modo interativo para usar esta ferramenta.", + "User declined to answer the questions.": "O usuário recusou responder às perguntas.", + "User has provided the following answers:": "O usuário forneceu as seguintes respostas:", + "Failed to process user answers:": "Falha ao processar as respostas do usuário:", + "Type something...": "Digite algo...", + Submit: "Enviar", + "Submit answers": "Enviar respostas", + Cancel: "Cancelar", + "Your answers:": "Suas respostas:", + "(not answered)": "(não respondido)", + "Ready to submit your answers?": "Pronto para enviar suas respostas?", + "↑/↓: Navigate | ←/→: Switch tabs | Enter: Select": + "↑/↓: Navegar | ←/→: Alternar abas | Enter: Selecionar", + "↑/↓: Navigate | ←/→: Switch tabs | Space/Enter: Toggle | Esc: Cancel": + "↑/↓: Navegar | ←/→: Alternar abas | Space/Enter: Alternar | Esc: Cancelar", + "↑/↓: Navigate | Space/Enter: Toggle | Esc: Cancel": + "↑/↓: Navegar | Space/Enter: Alternar | Esc: Cancelar", + "↑/↓: Navigate | Enter: Select | Esc: Cancel": "↑/↓: Navegar | Enter: Selecionar | Esc: Cancelar", // ============================================================================ // Commands - Auth // ============================================================================ - 'Authenticate using Alibaba Cloud Coding Plan': - 'Autenticar usando Alibaba Cloud Coding Plan', - 'Region for Coding Plan (china/global)': - 'Região para Coding Plan (china/global)', - 'API key for Coding Plan': 'Chave de API para Coding Plan', - 'Show current authentication status': 'Mostrar status atual de autenticação', - 'Authentication completed successfully.': - 'Autenticação concluída com sucesso.', - 'Processing Alibaba Cloud Coding Plan authentication...': - 'Processando autenticação Alibaba Cloud Coding Plan...', - 'Successfully authenticated with Alibaba Cloud Coding Plan.': - 'Autenticado com sucesso via Alibaba Cloud Coding Plan.', - 'Failed to authenticate with Coding Plan: {{error}}': - 'Falha ao autenticar com Coding Plan: {{error}}', - '中国 (China)': '中国 (China)', - '阿里云百炼 (aliyun.com)': '阿里云百炼 (aliyun.com)', - Global: 'Global', - 'Alibaba Cloud (alibabacloud.com)': 'Alibaba Cloud (alibabacloud.com)', - 'Select region for Coding Plan:': 'Selecione a região para Coding Plan:', - 'Enter your Coding Plan API key: ': - 'Insira sua chave de API do Coding Plan: ', - 'Select authentication method:': 'Selecione o método de autenticação:', - '\n=== Authentication Status ===\n': '\n=== Status de Autenticação ===\n', - '⚠️ No authentication method configured.\n': - '⚠️ Nenhum método de autenticação configurado.\n', - 'Run one of the following commands to get started:\n': - 'Execute um dos seguintes comandos para começar:\n', - ' qwen auth coding-plan - Authenticate with Alibaba Cloud Coding Plan\n': - ' qwen auth coding-plan - Autenticar com Alibaba Cloud Coding Plan\n', - 'Or simply run:': 'Ou simplesmente execute:', - ' qwen auth - Interactive authentication setup\n': - ' qwen auth - Configuração interativa de autenticação\n', - '✓ Authentication Method: Alibaba Cloud Coding Plan': - '✓ Método de autenticação: Alibaba Cloud Coding Plan', - '中国 (China) - 阿里云百炼': '中国 (China) - 阿里云百炼', - 'Global - Alibaba Cloud': 'Global - Alibaba Cloud', - ' Region: {{region}}': ' Região: {{region}}', - ' Current Model: {{model}}': ' Modelo atual: {{model}}', - ' Config Version: {{version}}': ' Versão da configuração: {{version}}', - ' Status: API key configured\n': ' Status: Chave de API configurada\n', - '⚠️ Authentication Method: Alibaba Cloud Coding Plan (Incomplete)': - '⚠️ Método de autenticação: Alibaba Cloud Coding Plan (Incompleto)', - ' Issue: API key not found in environment or settings\n': - ' Problema: Chave de API não encontrada no ambiente ou configurações\n', - ' Run `qwen auth coding-plan` to re-configure.\n': - ' Execute `qwen auth coding-plan` para reconfigurar.\n', - '✓ Authentication Method: {{type}}': '✓ Método de autenticação: {{type}}', - ' Status: Configured\n': ' Status: Configurado\n', - 'Failed to check authentication status: {{error}}': - 'Falha ao verificar status de autenticação: {{error}}', - 'Select an option:': 'Selecione uma opção:', - 'Raw mode not available. Please run in an interactive terminal.': - 'Modo raw não disponível. Execute em um terminal interativo.', - '(Use ↑ ↓ arrows to navigate, Enter to select, Ctrl+C to exit)\n': - '(Use ↑ ↓ para navegar, Enter para selecionar, Ctrl+C para sair)\n', - verbose: 'detalhado', - 'Show full tool output and thinking in verbose mode (toggle with Ctrl+O).': - 'Mostrar saída completa da ferramenta e raciocínio no modo detalhado (alternar com Ctrl+O).', - 'Press Ctrl+O to show full tool output': - 'Pressione Ctrl+O para exibir a saída completa da ferramenta', + "Authenticate using Alibaba Cloud Coding Plan": "Autenticar usando Alibaba Cloud Coding Plan", + "Region for Coding Plan (china/global)": "Região para Coding Plan (china/global)", + "API key for Coding Plan": "Chave de API para Coding Plan", + "Show current authentication status": "Mostrar status atual de autenticação", + "Authentication completed successfully.": "Autenticação concluída com sucesso.", + "Processing Alibaba Cloud Coding Plan authentication...": + "Processando autenticação Alibaba Cloud Coding Plan...", + "Successfully authenticated with Alibaba Cloud Coding Plan.": + "Autenticado com sucesso via Alibaba Cloud Coding Plan.", + "Failed to authenticate with Coding Plan: {{error}}": + "Falha ao autenticar com Coding Plan: {{error}}", + "中国 (China)": "中国 (China)", + "阿里云百炼 (aliyun.com)": "阿里云百炼 (aliyun.com)", + Global: "Global", + "Alibaba Cloud (alibabacloud.com)": "Alibaba Cloud (alibabacloud.com)", + "Select region for Coding Plan:": "Selecione a região para Coding Plan:", + "Enter your Coding Plan API key: ": "Insira sua chave de API do Coding Plan: ", + "Select authentication method:": "Selecione o método de autenticação:", + "\n=== Authentication Status ===\n": "\n=== Status de Autenticação ===\n", + "⚠️ No authentication method configured.\n": "⚠️ Nenhum método de autenticação configurado.\n", + "Run one of the following commands to get started:\n": + "Execute um dos seguintes comandos para começar:\n", + " qwen auth coding-plan - Authenticate with Alibaba Cloud Coding Plan\n": + " qwen auth coding-plan - Autenticar com Alibaba Cloud Coding Plan\n", + "Or simply run:": "Ou simplesmente execute:", + " qwen auth - Interactive authentication setup\n": + " qwen auth - Configuração interativa de autenticação\n", + "✓ Authentication Method: Alibaba Cloud Coding Plan": + "✓ Método de autenticação: Alibaba Cloud Coding Plan", + "中国 (China) - 阿里云百炼": "中国 (China) - 阿里云百炼", + "Global - Alibaba Cloud": "Global - Alibaba Cloud", + " Region: {{region}}": " Região: {{region}}", + " Current Model: {{model}}": " Modelo atual: {{model}}", + " Config Version: {{version}}": " Versão da configuração: {{version}}", + " Status: API key configured\n": " Status: Chave de API configurada\n", + "⚠️ Authentication Method: Alibaba Cloud Coding Plan (Incomplete)": + "⚠️ Método de autenticação: Alibaba Cloud Coding Plan (Incompleto)", + " Issue: API key not found in environment or settings\n": + " Problema: Chave de API não encontrada no ambiente ou configurações\n", + " Run `qwen auth coding-plan` to re-configure.\n": + " Execute `qwen auth coding-plan` para reconfigurar.\n", + "✓ Authentication Method: {{type}}": "✓ Método de autenticação: {{type}}", + " Status: Configured\n": " Status: Configurado\n", + "Failed to check authentication status: {{error}}": + "Falha ao verificar status de autenticação: {{error}}", + "Select an option:": "Selecione uma opção:", + "Raw mode not available. Please run in an interactive terminal.": + "Modo raw não disponível. Execute em um terminal interativo.", + "(Use ↑ ↓ arrows to navigate, Enter to select, Ctrl+C to exit)\n": + "(Use ↑ ↓ para navegar, Enter para selecionar, Ctrl+C para sair)\n", + verbose: "detalhado", + "Show full tool output and thinking in verbose mode (toggle with Ctrl+O).": + "Mostrar saída completa da ferramenta e raciocínio no modo detalhado (alternar com Ctrl+O).", + "Press Ctrl+O to show full tool output": + "Pressione Ctrl+O para exibir a saída completa da ferramenta", - 'Switch to plan mode or exit plan mode': - 'Switch to plan mode or exit plan mode', - 'Exited plan mode. Previous approval mode restored.': - 'Exited plan mode. Previous approval mode restored.', - 'Enabled plan mode. The agent will analyze and plan without executing tools.': - 'Enabled plan mode. The agent will analyze and plan without executing tools.', + "Switch to plan mode or exit plan mode": "Switch to plan mode or exit plan mode", + "Exited plan mode. Previous approval mode restored.": + "Exited plan mode. Previous approval mode restored.", + "Enabled plan mode. The agent will analyze and plan without executing tools.": + "Enabled plan mode. The agent will analyze and plan without executing tools.", 'Already in plan mode. Use "/plan exit" to exit plan mode.': 'Already in plan mode. Use "/plan exit" to exit plan mode.', 'Not in plan mode. Use "/plan" to enter plan mode first.': diff --git a/apps/airiscode-cli/src/i18n/locales/ru.js b/apps/airiscode-cli/src/i18n/locales/ru.js index 9fec3ec25..05e182a41 100644 --- a/apps/airiscode-cli/src/i18n/locales/ru.js +++ b/apps/airiscode-cli/src/i18n/locales/ru.js @@ -12,1116 +12,1032 @@ export default { // Справка / Компоненты интерфейса // ============================================================================ // Attachment hints - '↑ to manage attachments': '↑ управление вложениями', - '← → select, Delete to remove, ↓ to exit': - '← → выбрать, Delete удалить, ↓ выйти', - 'Attachments: ': 'Вложения: ', + "↑ to manage attachments": "↑ управление вложениями", + "← → select, Delete to remove, ↓ to exit": "← → выбрать, Delete удалить, ↓ выйти", + "Attachments: ": "Вложения: ", - 'Basics:': 'Основы:', - 'Add context': 'Добавить контекст', - 'Use {{symbol}} to specify files for context (e.g., {{example}}) to target specific files or folders.': - 'Используйте {{symbol}} для добавления файлов в контекст (например, {{example}}) для выбора конкретных файлов или папок).', - '@': '@', - '@src/myFile.ts': '@src/myFile.ts', - 'Shell mode': 'Режим терминала', - 'YOLO mode': 'Режим YOLO', - 'plan mode': 'Режим планирования', - 'auto-accept edits': 'Режим принятия правок', - 'Accepting edits': 'Принятие правок', - '(shift + tab to cycle)': '(shift + tab для переключения)', - '(tab to cycle)': '(Tab для переключения)', - 'Execute shell commands via {{symbol}} (e.g., {{example1}}) or use natural language (e.g., {{example2}}).': - 'Выполняйте команды терминала через {{symbol}} (например, {{example1}}) или используйте естественный язык (например, {{example2}}).', - '!': '!', - '!npm run start': '!npm run start', - 'start server': 'start server', - 'Commands:': 'Команды:', - 'shell command': 'команда терминала', - 'Model Context Protocol command (from external servers)': - 'Команда Model Context Protocol (из внешних серверов)', - 'Keyboard Shortcuts:': 'Горячие клавиши:', - 'Toggle this help display': 'Показать/скрыть эту справку', - 'Toggle shell mode': 'Переключить режим оболочки', - 'Open command menu': 'Открыть меню команд', - 'Add file context': 'Добавить файл в контекст', - 'Accept suggestion / Autocomplete': 'Принять подсказку / Автодополнение', - 'Reverse search history': 'Обратный поиск по истории', - 'Press ? again to close': 'Нажмите ? ещё раз, чтобы закрыть', - 'Jump through words in the input': 'Переход по словам во вводе', - 'Close dialogs, cancel requests, or quit application': - 'Закрыть диалоги, отменить запросы или выйти из приложения', - 'New line': 'Новая строка', - 'New line (Alt+Enter works for certain linux distros)': - 'Новая строка (Alt+Enter работает только в некоторых дистрибутивах Linux)', - 'Clear the screen': 'Очистить экран', - 'Open input in external editor': 'Открыть ввод во внешнем редакторе', - 'Send message': 'Отправить сообщение', - 'Initializing...': 'Инициализация...', - 'Connecting to MCP servers... ({{connected}}/{{total}})': - 'Подключение к MCP-серверам... ({{connected}}/{{total}})', - 'Type your message or @path/to/file': 'Введите сообщение или @путь/к/файлу', - '? for shortcuts': '? — горячие клавиши', + "Basics:": "Основы:", + "Add context": "Добавить контекст", + "Use {{symbol}} to specify files for context (e.g., {{example}}) to target specific files or folders.": + "Используйте {{symbol}} для добавления файлов в контекст (например, {{example}}) для выбора конкретных файлов или папок).", + "@": "@", + "@src/myFile.ts": "@src/myFile.ts", + "Shell mode": "Режим терминала", + "YOLO mode": "Режим YOLO", + "plan mode": "Режим планирования", + "auto-accept edits": "Режим принятия правок", + "Accepting edits": "Принятие правок", + "(shift + tab to cycle)": "(shift + tab для переключения)", + "(tab to cycle)": "(Tab для переключения)", + "Execute shell commands via {{symbol}} (e.g., {{example1}}) or use natural language (e.g., {{example2}}).": + "Выполняйте команды терминала через {{symbol}} (например, {{example1}}) или используйте естественный язык (например, {{example2}}).", + "!": "!", + "!npm run start": "!npm run start", + "start server": "start server", + "Commands:": "Команды:", + "shell command": "команда терминала", + "Model Context Protocol command (from external servers)": + "Команда Model Context Protocol (из внешних серверов)", + "Keyboard Shortcuts:": "Горячие клавиши:", + "Toggle this help display": "Показать/скрыть эту справку", + "Toggle shell mode": "Переключить режим оболочки", + "Open command menu": "Открыть меню команд", + "Add file context": "Добавить файл в контекст", + "Accept suggestion / Autocomplete": "Принять подсказку / Автодополнение", + "Reverse search history": "Обратный поиск по истории", + "Press ? again to close": "Нажмите ? ещё раз, чтобы закрыть", + "Jump through words in the input": "Переход по словам во вводе", + "Close dialogs, cancel requests, or quit application": + "Закрыть диалоги, отменить запросы или выйти из приложения", + "New line": "Новая строка", + "New line (Alt+Enter works for certain linux distros)": + "Новая строка (Alt+Enter работает только в некоторых дистрибутивах Linux)", + "Clear the screen": "Очистить экран", + "Open input in external editor": "Открыть ввод во внешнем редакторе", + "Send message": "Отправить сообщение", + "Initializing...": "Инициализация...", + "Connecting to MCP servers... ({{connected}}/{{total}})": + "Подключение к MCP-серверам... ({{connected}}/{{total}})", + "Type your message or @path/to/file": "Введите сообщение или @путь/к/файлу", + "? for shortcuts": "? — горячие клавиши", "Press 'i' for INSERT mode and 'Esc' for NORMAL mode.": "Нажмите 'i' для режима ВСТАВКА и 'Esc' для ОБЫЧНОГО режима.", - 'Cancel operation / Clear input (double press)': - 'Отменить операцию / Очистить ввод (двойное нажатие)', - 'Cycle approval modes': 'Переключение режимов подтверждения', - 'Cycle through your prompt history': 'Пролистать историю запросов', - 'For a full list of shortcuts, see {{docPath}}': - 'Полный список горячих клавиш см. в {{docPath}}', - 'docs/keyboard-shortcuts.md': 'docs/keyboard-shortcuts.md', - 'for help on Qwen Code': 'Справка по Qwen Code', - 'show version info': 'Просмотр информации о версии', - 'submit a bug report': 'Отправка отчёта об ошибке', - 'About Qwen Code': 'Об Qwen Code', - Status: 'Статус', + "Cancel operation / Clear input (double press)": + "Отменить операцию / Очистить ввод (двойное нажатие)", + "Cycle approval modes": "Переключение режимов подтверждения", + "Cycle through your prompt history": "Пролистать историю запросов", + "For a full list of shortcuts, see {{docPath}}": "Полный список горячих клавиш см. в {{docPath}}", + "docs/keyboard-shortcuts.md": "docs/keyboard-shortcuts.md", + "for help on Qwen Code": "Справка по Qwen Code", + "show version info": "Просмотр информации о версии", + "submit a bug report": "Отправка отчёта об ошибке", + "About Qwen Code": "Об Qwen Code", + Status: "Статус", // Keyboard shortcuts panel descriptions - 'for shell mode': 'режим оболочки', - 'for commands': 'меню команд', - 'for file paths': 'пути к файлам', - 'to clear input': 'очистить ввод', - 'to cycle approvals': 'переключить режим', - 'to quit': 'выход', - 'for newline': 'новая строка', - 'to clear screen': 'очистить экран', - 'to search history': 'поиск в истории', - 'to paste images': 'вставить изображения', - 'for external editor': 'внешний редактор', + "for shell mode": "режим оболочки", + "for commands": "меню команд", + "for file paths": "пути к файлам", + "to clear input": "очистить ввод", + "to cycle approvals": "переключить режим", + "to quit": "выход", + "for newline": "новая строка", + "to clear screen": "очистить экран", + "to search history": "поиск в истории", + "to paste images": "вставить изображения", + "for external editor": "внешний редактор", // ============================================================================ // Поля системной информации // ============================================================================ - 'Qwen Code': 'Qwen Code', - Runtime: 'Среда выполнения', - OS: 'ОС', - Auth: 'Аутентификация', - 'CLI Version': 'Версия CLI', - 'Git Commit': 'Git-коммит', - Model: 'Модель', - 'Fast Model': 'Быстрая модель', - Sandbox: 'Песочница', - 'OS Platform': 'Платформа ОС', - 'OS Arch': 'Архитектура ОС', - 'OS Release': 'Версия ОС', - 'Node.js Version': 'Версия Node.js', - 'NPM Version': 'Версия NPM', - 'Session ID': 'ID сессии', - 'Auth Method': 'Метод авторизации', - 'Base URL': 'Базовый URL', - Proxy: 'Прокси', - 'Memory Usage': 'Использование памяти', - 'IDE Client': 'Клиент IDE', + "Qwen Code": "Qwen Code", + Runtime: "Среда выполнения", + OS: "ОС", + Auth: "Аутентификация", + "CLI Version": "Версия CLI", + "Git Commit": "Git-коммит", + Model: "Модель", + "Fast Model": "Быстрая модель", + Sandbox: "Песочница", + "OS Platform": "Платформа ОС", + "OS Arch": "Архитектура ОС", + "OS Release": "Версия ОС", + "Node.js Version": "Версия Node.js", + "NPM Version": "Версия NPM", + "Session ID": "ID сессии", + "Auth Method": "Метод авторизации", + "Base URL": "Базовый URL", + Proxy: "Прокси", + "Memory Usage": "Использование памяти", + "IDE Client": "Клиент IDE", // ============================================================================ // Команды - Общие // ============================================================================ - 'Analyzes the project and creates a tailored QWEN.md file.': - 'Анализ проекта и создание адаптированного файла QWEN.md', - 'List available Qwen Code tools. Usage: /tools [desc]': - 'Просмотр доступных инструментов Qwen Code. Использование: /tools [desc]', - 'List available skills.': 'Показать доступные навыки.', - 'Available Qwen Code CLI tools:': 'Доступные инструменты Qwen Code CLI:', - 'No tools available': 'Нет доступных инструментов', - 'View or change the approval mode for tool usage': - 'Просмотр или изменение режима подтверждения для использования инструментов', + "Analyzes the project and creates a tailored QWEN.md file.": + "Анализ проекта и создание адаптированного файла QWEN.md", + "List available Qwen Code tools. Usage: /tools [desc]": + "Просмотр доступных инструментов Qwen Code. Использование: /tools [desc]", + "List available skills.": "Показать доступные навыки.", + "Available Qwen Code CLI tools:": "Доступные инструменты Qwen Code CLI:", + "No tools available": "Нет доступных инструментов", + "View or change the approval mode for tool usage": + "Просмотр или изменение режима подтверждения для использования инструментов", 'Invalid approval mode "{{arg}}". Valid modes: {{modes}}': 'Недопустимый режим подтверждения "{{arg}}". Допустимые режимы: {{modes}}', - 'Approval mode set to "{{mode}}"': - 'Режим подтверждения установлен на "{{mode}}"', - 'View or change the language setting': - 'Просмотр или изменение настроек языка', - 'change the theme': 'Изменение темы', - 'Select Theme': 'Выбор темы', - Preview: 'Предпросмотр', - '(Use Enter to select, Tab to configure scope)': - '(Enter для выбора, Tab для настройки области)', - '(Use Enter to apply scope, Tab to go back)': - '(Enter для применения области, Tab для возврата)', - 'Theme configuration unavailable due to NO_COLOR env variable.': - 'Настройка темы недоступна из-за переменной окружения NO_COLOR.', + 'Approval mode set to "{{mode}}"': 'Режим подтверждения установлен на "{{mode}}"', + "View or change the language setting": "Просмотр или изменение настроек языка", + "change the theme": "Изменение темы", + "Select Theme": "Выбор темы", + Preview: "Предпросмотр", + "(Use Enter to select, Tab to configure scope)": "(Enter для выбора, Tab для настройки области)", + "(Use Enter to apply scope, Tab to go back)": "(Enter для применения области, Tab для возврата)", + "Theme configuration unavailable due to NO_COLOR env variable.": + "Настройка темы недоступна из-за переменной окружения NO_COLOR.", 'Theme "{{themeName}}" not found.': 'Тема "{{themeName}}" не найдена.', 'Theme "{{themeName}}" not found in selected scope.': 'Тема "{{themeName}}" не найдена в выбранной области.', - 'Clear conversation history and free up context': - 'Очистить историю диалога и освободить контекст', - 'Compresses the context by replacing it with a summary.': - 'Сжатие контекста заменой на краткую сводку', - 'open full Qwen Code documentation in your browser': - 'Открытие полной документации Qwen Code в браузере', - 'Configuration not available.': 'Конфигурация недоступна.', - 'change the auth method': 'Изменение метода авторизации', - 'Configure authentication information for login': - 'Настройка аутентификационной информации для входа', - 'Copy the last result or code snippet to clipboard': - 'Копирование последнего результата или фрагмента кода в буфер обмена', + "Clear conversation history and free up context": + "Очистить историю диалога и освободить контекст", + "Compresses the context by replacing it with a summary.": + "Сжатие контекста заменой на краткую сводку", + "open full Qwen Code documentation in your browser": + "Открытие полной документации Qwen Code в браузере", + "Configuration not available.": "Конфигурация недоступна.", + "change the auth method": "Изменение метода авторизации", + "Configure authentication information for login": + "Настройка аутентификационной информации для входа", + "Copy the last result or code snippet to clipboard": + "Копирование последнего результата или фрагмента кода в буфер обмена", // ============================================================================ // Команды - Агенты // ============================================================================ - 'Manage subagents for specialized task delegation.': - 'Управление подагентами для делегирования специализированных задач', - 'Manage existing subagents (view, edit, delete).': - 'Управление существующими подагентами (просмотр, правка, удаление)', - 'Create a new subagent with guided setup.': - 'Создание нового подагента с пошаговой настройкой', + "Manage subagents for specialized task delegation.": + "Управление подагентами для делегирования специализированных задач", + "Manage existing subagents (view, edit, delete).": + "Управление существующими подагентами (просмотр, правка, удаление)", + "Create a new subagent with guided setup.": "Создание нового подагента с пошаговой настройкой", // ============================================================================ // Агенты - Диалог управления // ============================================================================ - Agents: 'Агенты', - 'Choose Action': 'Выберите действие', - 'Edit {{name}}': 'Редактировать {{name}}', - 'Edit Tools: {{name}}': 'Редактировать инструменты: {{name}}', - 'Edit Color: {{name}}': 'Редактировать цвет: {{name}}', - 'Delete {{name}}': 'Удалить {{name}}', - 'Unknown Step': 'Неизвестный шаг', - 'Esc to close': 'Esc для закрытия', - 'Enter to select, ↑↓ to navigate, Esc to close': - 'Enter для выбора, ↑↓ для навигации, Esc для закрытия', - 'Esc to go back': 'Esc для возврата', - 'Enter to confirm, Esc to cancel': 'Enter для подтверждения, Esc для отмены', - 'Enter to select, ↑↓ to navigate, Esc to go back': - 'Enter для выбора, ↑↓ для навигации, Esc для возврата', - 'Enter to submit, Esc to go back': 'Enter для отправки, Esc для возврата', - 'Invalid step: {{step}}': 'Неверный шаг: {{step}}', - 'No subagents found.': 'Подагенты не найдены.', + Agents: "Агенты", + "Choose Action": "Выберите действие", + "Edit {{name}}": "Редактировать {{name}}", + "Edit Tools: {{name}}": "Редактировать инструменты: {{name}}", + "Edit Color: {{name}}": "Редактировать цвет: {{name}}", + "Delete {{name}}": "Удалить {{name}}", + "Unknown Step": "Неизвестный шаг", + "Esc to close": "Esc для закрытия", + "Enter to select, ↑↓ to navigate, Esc to close": + "Enter для выбора, ↑↓ для навигации, Esc для закрытия", + "Esc to go back": "Esc для возврата", + "Enter to confirm, Esc to cancel": "Enter для подтверждения, Esc для отмены", + "Enter to select, ↑↓ to navigate, Esc to go back": + "Enter для выбора, ↑↓ для навигации, Esc для возврата", + "Enter to submit, Esc to go back": "Enter для отправки, Esc для возврата", + "Invalid step: {{step}}": "Неверный шаг: {{step}}", + "No subagents found.": "Подагенты не найдены.", "Use '/agents create' to create your first subagent.": "Используйте '/agents create' для создания первого подагента.", - '(built-in)': '(встроенный)', - '(overridden by project level agent)': - '(переопределен агентом уровня проекта)', - 'Project Level ({{path}})': 'Уровень проекта ({{path}})', - 'User Level ({{path}})': 'Уровень пользователя ({{path}})', - 'Built-in Agents': 'Встроенные агенты', - 'Extension Agents': 'Агенты расширений', - 'Using: {{count}} agents': 'Используется: {{count}} агент(ов)', - 'View Agent': 'Просмотреть агента', - 'Edit Agent': 'Редактировать агента', - 'Delete Agent': 'Удалить агента', - Back: 'Назад', - 'No agent selected': 'Агент не выбран', - 'File Path: ': 'Путь к файлу: ', - 'Tools: ': 'Инструменты: ', - 'Color: ': 'Цвет: ', - 'Description:': 'Описание:', - 'System Prompt:': 'Системный промпт:', - 'Open in editor': 'Открыть в редакторе', - 'Edit tools': 'Редактировать инструменты', - 'Edit color': 'Редактировать цвет', - '❌ Error:': '❌ Ошибка:', + "(built-in)": "(встроенный)", + "(overridden by project level agent)": "(переопределен агентом уровня проекта)", + "Project Level ({{path}})": "Уровень проекта ({{path}})", + "User Level ({{path}})": "Уровень пользователя ({{path}})", + "Built-in Agents": "Встроенные агенты", + "Extension Agents": "Агенты расширений", + "Using: {{count}} agents": "Используется: {{count}} агент(ов)", + "View Agent": "Просмотреть агента", + "Edit Agent": "Редактировать агента", + "Delete Agent": "Удалить агента", + Back: "Назад", + "No agent selected": "Агент не выбран", + "File Path: ": "Путь к файлу: ", + "Tools: ": "Инструменты: ", + "Color: ": "Цвет: ", + "Description:": "Описание:", + "System Prompt:": "Системный промпт:", + "Open in editor": "Открыть в редакторе", + "Edit tools": "Редактировать инструменты", + "Edit color": "Редактировать цвет", + "❌ Error:": "❌ Ошибка:", 'Are you sure you want to delete agent "{{name}}"?': 'Вы уверены, что хотите удалить агента "{{name}}"?', // ============================================================================ // Агенты - Мастер создания // ============================================================================ - 'Project Level (.qwen/agents/)': 'Уровень проекта (.qwen/agents/)', - 'User Level (~/.qwen/agents/)': 'Уровень пользователя (~/.qwen/agents/)', - '✅ Subagent Created Successfully!': '✅ Подагент успешно создан!', + "Project Level (.qwen/agents/)": "Уровень проекта (.qwen/agents/)", + "User Level (~/.qwen/agents/)": "Уровень пользователя (~/.qwen/agents/)", + "✅ Subagent Created Successfully!": "✅ Подагент успешно создан!", 'Subagent "{{name}}" has been saved to {{level}} level.': 'Подагент "{{name}}" сохранен на уровне {{level}}.', - 'Name: ': 'Имя: ', - 'Location: ': 'Расположение: ', - '❌ Error saving subagent:': '❌ Ошибка сохранения подагента:', - 'Warnings:': 'Предупреждения:', + "Name: ": "Имя: ", + "Location: ": "Расположение: ", + "❌ Error saving subagent:": "❌ Ошибка сохранения подагента:", + "Warnings:": "Предупреждения:", 'Name "{{name}}" already exists at {{level}} level - will overwrite existing subagent': 'Имя "{{name}}" уже существует на уровне {{level}} - существующий подагент будет перезаписан', 'Name "{{name}}" exists at user level - project level will take precedence': 'Имя "{{name}}" существует на уровне пользователя - уровень проекта будет иметь приоритет', 'Name "{{name}}" exists at project level - existing subagent will take precedence': 'Имя "{{name}}" существует на уровне проекта - существующий подагент будет иметь приоритет', - 'Description is over {{length}} characters': - 'Описание превышает {{length}} символов', - 'System prompt is over {{length}} characters': - 'Системный промпт превышает {{length}} символов', + "Description is over {{length}} characters": "Описание превышает {{length}} символов", + "System prompt is over {{length}} characters": "Системный промпт превышает {{length}} символов", // Агенты - Шаги мастера создания - 'Step {{n}}: Choose Location': 'Шаг {{n}}: Выберите расположение', - 'Step {{n}}: Choose Generation Method': 'Шаг {{n}}: Выберите метод генерации', - 'Generate with Qwen Code (Recommended)': - 'Сгенерировать с помощью Qwen Code (Рекомендуется)', - 'Manual Creation': 'Ручное создание', - 'Describe what this subagent should do and when it should be used. (Be comprehensive for best results)': - 'Опишите, что должен делать этот подагент и когда его следует использовать. (Будьте подробны для лучших результатов)', - 'e.g., Expert code reviewer that reviews code based on best practices...': - 'например, Экспертный ревьювер кода, проверяющий код на соответствие лучшим практикам...', - 'Generating subagent configuration...': 'Генерация конфигурации подагента...', - 'Failed to generate subagent: {{error}}': - 'Не удалось сгенерировать подагента: {{error}}', - 'Step {{n}}: Describe Your Subagent': 'Шаг {{n}}: Опишите подагента', - 'Step {{n}}: Enter Subagent Name': 'Шаг {{n}}: Введите имя подагента', - 'Step {{n}}: Enter System Prompt': 'Шаг {{n}}: Введите системный промпт', - 'Step {{n}}: Enter Description': 'Шаг {{n}}: Введите описание', + "Step {{n}}: Choose Location": "Шаг {{n}}: Выберите расположение", + "Step {{n}}: Choose Generation Method": "Шаг {{n}}: Выберите метод генерации", + "Generate with Qwen Code (Recommended)": "Сгенерировать с помощью Qwen Code (Рекомендуется)", + "Manual Creation": "Ручное создание", + "Describe what this subagent should do and when it should be used. (Be comprehensive for best results)": + "Опишите, что должен делать этот подагент и когда его следует использовать. (Будьте подробны для лучших результатов)", + "e.g., Expert code reviewer that reviews code based on best practices...": + "например, Экспертный ревьювер кода, проверяющий код на соответствие лучшим практикам...", + "Generating subagent configuration...": "Генерация конфигурации подагента...", + "Failed to generate subagent: {{error}}": "Не удалось сгенерировать подагента: {{error}}", + "Step {{n}}: Describe Your Subagent": "Шаг {{n}}: Опишите подагента", + "Step {{n}}: Enter Subagent Name": "Шаг {{n}}: Введите имя подагента", + "Step {{n}}: Enter System Prompt": "Шаг {{n}}: Введите системный промпт", + "Step {{n}}: Enter Description": "Шаг {{n}}: Введите описание", // Агенты - Выбор инструментов - 'Step {{n}}: Select Tools': 'Шаг {{n}}: Выберите инструменты', - 'All Tools (Default)': 'Все инструменты (по умолчанию)', - 'All Tools': 'Все инструменты', - 'Read-only Tools': 'Инструменты только для чтения', - 'Read & Edit Tools': 'Инструменты для чтения и редактирования', - 'Read & Edit & Execution Tools': - 'Инструменты для чтения, редактирования и выполнения', - 'All tools selected, including MCP tools': - 'Все инструменты выбраны, включая инструменты MCP', - 'Selected tools:': 'Выбранные инструменты:', - 'Read-only tools:': 'Инструменты только для чтения:', - 'Edit tools:': 'Инструменты редактирования:', - 'Execution tools:': 'Инструменты выполнения:', - 'Step {{n}}: Choose Background Color': 'Шаг {{n}}: Выберите цвет фона', - 'Step {{n}}: Confirm and Save': 'Шаг {{n}}: Подтвердите и сохраните', + "Step {{n}}: Select Tools": "Шаг {{n}}: Выберите инструменты", + "All Tools (Default)": "Все инструменты (по умолчанию)", + "All Tools": "Все инструменты", + "Read-only Tools": "Инструменты только для чтения", + "Read & Edit Tools": "Инструменты для чтения и редактирования", + "Read & Edit & Execution Tools": "Инструменты для чтения, редактирования и выполнения", + "All tools selected, including MCP tools": "Все инструменты выбраны, включая инструменты MCP", + "Selected tools:": "Выбранные инструменты:", + "Read-only tools:": "Инструменты только для чтения:", + "Edit tools:": "Инструменты редактирования:", + "Execution tools:": "Инструменты выполнения:", + "Step {{n}}: Choose Background Color": "Шаг {{n}}: Выберите цвет фона", + "Step {{n}}: Confirm and Save": "Шаг {{n}}: Подтвердите и сохраните", // Агенты - Навигация и инструкции - 'Esc to cancel': 'Esc для отмены', - 'Press Enter to save, e to save and edit, Esc to go back': - 'Enter для сохранения, e для сохранения и редактирования, Esc для возврата', - 'Press Enter to continue, {{navigation}}Esc to {{action}}': - 'Enter для продолжения, {{navigation}}Esc для {{action}}', - cancel: 'отмены', - 'go back': 'возврата', - '↑↓ to navigate, ': '↑↓ для навигации, ', - 'Enter a clear, unique name for this subagent.': - 'Введите четкое, уникальное имя для этого подагента.', - 'e.g., Code Reviewer': 'например, Ревьювер кода', - 'Name cannot be empty.': 'Имя не может быть пустым.', + "Esc to cancel": "Esc для отмены", + "Press Enter to save, e to save and edit, Esc to go back": + "Enter для сохранения, e для сохранения и редактирования, Esc для возврата", + "Press Enter to continue, {{navigation}}Esc to {{action}}": + "Enter для продолжения, {{navigation}}Esc для {{action}}", + cancel: "отмены", + "go back": "возврата", + "↑↓ to navigate, ": "↑↓ для навигации, ", + "Enter a clear, unique name for this subagent.": + "Введите четкое, уникальное имя для этого подагента.", + "e.g., Code Reviewer": "например, Ревьювер кода", + "Name cannot be empty.": "Имя не может быть пустым.", "Write the system prompt that defines this subagent's behavior. Be comprehensive for best results.": - 'Напишите системный промпт, определяющий поведение подагента. Будьте подробны для лучших результатов.', - 'e.g., You are an expert code reviewer...': - 'например, Вы экспертный ревьювер кода...', - 'System prompt cannot be empty.': 'Системный промпт не может быть пустым.', - 'Describe when and how this subagent should be used.': - 'Опишите, когда и как следует использовать этого подагента.', - 'e.g., Reviews code for best practices and potential bugs.': - 'например, Проверяет код на соответствие лучшим практикам и потенциальные ошибки.', - 'Description cannot be empty.': 'Описание не может быть пустым.', - 'Failed to launch editor: {{error}}': - 'Не удалось запустить редактор: {{error}}', - 'Failed to save and edit subagent: {{error}}': - 'Не удалось сохранить и отредактировать подагента: {{error}}', + "Напишите системный промпт, определяющий поведение подагента. Будьте подробны для лучших результатов.", + "e.g., You are an expert code reviewer...": "например, Вы экспертный ревьювер кода...", + "System prompt cannot be empty.": "Системный промпт не может быть пустым.", + "Describe when and how this subagent should be used.": + "Опишите, когда и как следует использовать этого подагента.", + "e.g., Reviews code for best practices and potential bugs.": + "например, Проверяет код на соответствие лучшим практикам и потенциальные ошибки.", + "Description cannot be empty.": "Описание не может быть пустым.", + "Failed to launch editor: {{error}}": "Не удалось запустить редактор: {{error}}", + "Failed to save and edit subagent: {{error}}": + "Не удалось сохранить и отредактировать подагента: {{error}}", // ============================================================================ // Команды - Общие (продолжение) // ============================================================================ - 'View and edit Qwen Code settings': 'Просмотр и изменение настроек Qwen Code', - Settings: 'Настройки', - 'To see changes, Qwen Code must be restarted. Press r to exit and apply changes now.': - 'Для применения изменений необходимо перезапустить Qwen Code. Нажмите r для выхода и применения изменений.', + "View and edit Qwen Code settings": "Просмотр и изменение настроек Qwen Code", + Settings: "Настройки", + "To see changes, Qwen Code must be restarted. Press r to exit and apply changes now.": + "Для применения изменений необходимо перезапустить Qwen Code. Нажмите r для выхода и применения изменений.", 'The command "/{{command}}" is not supported in non-interactive mode.': 'Команда "/{{command}}" не поддерживается в неинтерактивном режиме.', // ============================================================================ // Метки настроек // ============================================================================ - 'Vim Mode': 'Режим Vim', - 'Disable Auto Update': 'Отключить автообновление', - 'Attribution: commit': 'Атрибуция: коммит', - 'Terminal Bell Notification': 'Звуковое уведомление терминала', - 'Enable Usage Statistics': 'Включить сбор статистики использования', - Theme: 'Тема', - 'Preferred Editor': 'Предпочтительный редактор', - 'Auto-connect to IDE': 'Автоподключение к IDE', - 'Enable Prompt Completion': 'Включить автодополнение промптов', - 'Debug Keystroke Logging': 'Логирование нажатий клавиш для отладки', - 'Language: UI': 'Язык: интерфейс', - 'Language: Model': 'Язык: модель', - 'Output Format': 'Формат вывода', - 'Hide Window Title': 'Скрыть заголовок окна', - 'Show Status in Title': 'Показывать статус в заголовке', - 'Hide Tips': 'Скрыть подсказки', - 'Show Line Numbers in Code': 'Показывать номера строк в коде', - 'Show Citations': 'Показывать цитаты', - 'Custom Witty Phrases': 'Пользовательские остроумные фразы', - 'Show Welcome Back Dialog': 'Показывать диалог приветствия', - 'Enable User Feedback': 'Включить отзывы пользователей', - 'How is Qwen doing this session? (optional)': - 'Как дела у Qwen в этой сессии? (необязательно)', - Bad: 'Плохо', - Fine: 'Нормально', - Good: 'Хорошо', - Dismiss: 'Отклонить', - 'Not Sure Yet': 'Пока не уверен', - 'Any other key': 'Любая другая клавиша', - 'Disable Loading Phrases': 'Отключить фразы при загрузке', - 'Screen Reader Mode': 'Режим программы чтения с экрана', - 'IDE Mode': 'Режим IDE', - 'Max Session Turns': 'Макс. количество ходов сессии', - 'Skip Next Speaker Check': 'Пропустить проверку следующего говорящего', - 'Skip Loop Detection': 'Пропустить обнаружение циклов', - 'Skip Startup Context': 'Пропустить начальный контекст', - 'Enable OpenAI Logging': 'Включить логирование OpenAI', - 'OpenAI Logging Directory': 'Директория логов OpenAI', - Timeout: 'Таймаут', - 'Max Retries': 'Макс. количество попыток', - 'Disable Cache Control': 'Отключить управление кэшем', - 'Memory Discovery Max Dirs': 'Макс. директорий для поиска в памяти', - 'Load Memory From Include Directories': - 'Загружать память из включенных директорий', - 'Respect .gitignore': 'Учитывать .gitignore', - 'Respect .qwenignore': 'Учитывать .qwenignore', - 'Enable Recursive File Search': 'Включить рекурсивный поиск файлов', - 'Disable Fuzzy Search': 'Отключить нечеткий поиск', - 'Interactive Shell (PTY)': 'Интерактивный терминал (PTY)', - 'Show Color': 'Показывать цвета', - 'Auto Accept': 'Автоподтверждение', - 'Use Ripgrep': 'Использовать Ripgrep', - 'Use Builtin Ripgrep': 'Использовать встроенный Ripgrep', - 'Enable Tool Output Truncation': 'Включить обрезку вывода инструментов', - 'Tool Output Truncation Threshold': 'Порог обрезки вывода инструментов', - 'Tool Output Truncation Lines': 'Лимит строк вывода инструментов', - 'Folder Trust': 'Доверие к папке', - 'Vision Model Preview': 'Визуальная модель (предпросмотр)', - 'Tool Schema Compliance': 'Соответствие схеме инструмента', + "Vim Mode": "Режим Vim", + "Disable Auto Update": "Отключить автообновление", + "Attribution: commit": "Атрибуция: коммит", + "Terminal Bell Notification": "Звуковое уведомление терминала", + "Enable Usage Statistics": "Включить сбор статистики использования", + Theme: "Тема", + "Preferred Editor": "Предпочтительный редактор", + "Auto-connect to IDE": "Автоподключение к IDE", + "Enable Prompt Completion": "Включить автодополнение промптов", + "Debug Keystroke Logging": "Логирование нажатий клавиш для отладки", + "Language: UI": "Язык: интерфейс", + "Language: Model": "Язык: модель", + "Output Format": "Формат вывода", + "Hide Window Title": "Скрыть заголовок окна", + "Show Status in Title": "Показывать статус в заголовке", + "Hide Tips": "Скрыть подсказки", + "Show Line Numbers in Code": "Показывать номера строк в коде", + "Show Citations": "Показывать цитаты", + "Custom Witty Phrases": "Пользовательские остроумные фразы", + "Show Welcome Back Dialog": "Показывать диалог приветствия", + "Enable User Feedback": "Включить отзывы пользователей", + "How is Qwen doing this session? (optional)": "Как дела у Qwen в этой сессии? (необязательно)", + Bad: "Плохо", + Fine: "Нормально", + Good: "Хорошо", + Dismiss: "Отклонить", + "Not Sure Yet": "Пока не уверен", + "Any other key": "Любая другая клавиша", + "Disable Loading Phrases": "Отключить фразы при загрузке", + "Screen Reader Mode": "Режим программы чтения с экрана", + "IDE Mode": "Режим IDE", + "Max Session Turns": "Макс. количество ходов сессии", + "Skip Next Speaker Check": "Пропустить проверку следующего говорящего", + "Skip Loop Detection": "Пропустить обнаружение циклов", + "Skip Startup Context": "Пропустить начальный контекст", + "Enable OpenAI Logging": "Включить логирование OpenAI", + "OpenAI Logging Directory": "Директория логов OpenAI", + Timeout: "Таймаут", + "Max Retries": "Макс. количество попыток", + "Disable Cache Control": "Отключить управление кэшем", + "Memory Discovery Max Dirs": "Макс. директорий для поиска в памяти", + "Load Memory From Include Directories": "Загружать память из включенных директорий", + "Respect .gitignore": "Учитывать .gitignore", + "Respect .qwenignore": "Учитывать .qwenignore", + "Enable Recursive File Search": "Включить рекурсивный поиск файлов", + "Disable Fuzzy Search": "Отключить нечеткий поиск", + "Interactive Shell (PTY)": "Интерактивный терминал (PTY)", + "Show Color": "Показывать цвета", + "Auto Accept": "Автоподтверждение", + "Use Ripgrep": "Использовать Ripgrep", + "Use Builtin Ripgrep": "Использовать встроенный Ripgrep", + "Enable Tool Output Truncation": "Включить обрезку вывода инструментов", + "Tool Output Truncation Threshold": "Порог обрезки вывода инструментов", + "Tool Output Truncation Lines": "Лимит строк вывода инструментов", + "Folder Trust": "Доверие к папке", + "Vision Model Preview": "Визуальная модель (предпросмотр)", + "Tool Schema Compliance": "Соответствие схеме инструмента", // Варианты перечислений настроек - 'Auto (detect from system)': 'Авто (определить из системы)', - Text: 'Текст', - JSON: 'JSON', - Plan: 'План', - Default: 'По умолчанию', - 'Auto Edit': 'Авторедактирование', - YOLO: 'YOLO', - 'toggle vim mode on/off': 'Включение/выключение режима vim', - 'check session stats. Usage: /stats [model|tools]': - 'Просмотр статистики сессии. Использование: /stats [model|tools]', - 'Show model-specific usage statistics.': - 'Показать статистику использования модели.', - 'Show tool-specific usage statistics.': - 'Показать статистику использования инструментов.', - 'exit the cli': 'Выход из CLI', - 'Open MCP management dialog, or authenticate with OAuth-enabled servers': - 'Открыть диалог управления MCP или авторизоваться на сервере с поддержкой OAuth', - 'List configured MCP servers and tools, or authenticate with OAuth-enabled servers': - 'Показать настроенные MCP-серверы и инструменты, или авторизоваться на серверах с поддержкой OAuth', - 'Manage workspace directories': - 'Управление директориями рабочего пространства', - 'Add directories to the workspace. Use comma to separate multiple paths': - 'Добавить директории в рабочее пространство. Используйте запятую для разделения путей', - 'Show all directories in the workspace': - 'Показать все директории в рабочем пространстве', - 'set external editor preference': - 'Установка предпочитаемого внешнего редактора', - 'Select Editor': 'Выбрать редактор', - 'Editor Preference': 'Настройка редактора', - 'These editors are currently supported. Please note that some editors cannot be used in sandbox mode.': - 'В настоящее время поддерживаются следующие редакторы. Обратите внимание, что некоторые редакторы нельзя использовать в режиме песочницы.', - 'Your preferred editor is:': 'Ваш предпочитаемый редактор:', - 'Manage extensions': 'Управление расширениями', - 'Manage installed extensions': 'Управлять установленными расширениями', - 'List active extensions': 'Показать активные расширения', - 'Update extensions. Usage: update |--all': - 'Обновить расширения. Использование: update |--all', - 'Disable an extension': 'Отключить расширение', - 'Enable an extension': 'Включить расширение', - 'Install an extension from a git repo or local path': - 'Установить расширение из Git-репозитория или локального пути', - 'Uninstall an extension': 'Удалить расширение', - 'No extensions installed.': 'Расширения не установлены.', - 'Usage: /extensions update |--all': - 'Использование: /extensions update <имена-расширений>|--all', + "Auto (detect from system)": "Авто (определить из системы)", + Text: "Текст", + JSON: "JSON", + Plan: "План", + Default: "По умолчанию", + "Auto Edit": "Авторедактирование", + YOLO: "YOLO", + "toggle vim mode on/off": "Включение/выключение режима vim", + "check session stats. Usage: /stats [model|tools]": + "Просмотр статистики сессии. Использование: /stats [model|tools]", + "Show model-specific usage statistics.": "Показать статистику использования модели.", + "Show tool-specific usage statistics.": "Показать статистику использования инструментов.", + "exit the cli": "Выход из CLI", + "Open MCP management dialog, or authenticate with OAuth-enabled servers": + "Открыть диалог управления MCP или авторизоваться на сервере с поддержкой OAuth", + "List configured MCP servers and tools, or authenticate with OAuth-enabled servers": + "Показать настроенные MCP-серверы и инструменты, или авторизоваться на серверах с поддержкой OAuth", + "Manage workspace directories": "Управление директориями рабочего пространства", + "Add directories to the workspace. Use comma to separate multiple paths": + "Добавить директории в рабочее пространство. Используйте запятую для разделения путей", + "Show all directories in the workspace": "Показать все директории в рабочем пространстве", + "set external editor preference": "Установка предпочитаемого внешнего редактора", + "Select Editor": "Выбрать редактор", + "Editor Preference": "Настройка редактора", + "These editors are currently supported. Please note that some editors cannot be used in sandbox mode.": + "В настоящее время поддерживаются следующие редакторы. Обратите внимание, что некоторые редакторы нельзя использовать в режиме песочницы.", + "Your preferred editor is:": "Ваш предпочитаемый редактор:", + "Manage extensions": "Управление расширениями", + "Manage installed extensions": "Управлять установленными расширениями", + "List active extensions": "Показать активные расширения", + "Update extensions. Usage: update |--all": + "Обновить расширения. Использование: update |--all", + "Disable an extension": "Отключить расширение", + "Enable an extension": "Включить расширение", + "Install an extension from a git repo or local path": + "Установить расширение из Git-репозитория или локального пути", + "Uninstall an extension": "Удалить расширение", + "No extensions installed.": "Расширения не установлены.", + "Usage: /extensions update |--all": + "Использование: /extensions update <имена-расширений>|--all", 'Extension "{{name}}" not found.': 'Расширение "{{name}}" не найдено.', - 'No extensions to update.': 'Нет расширений для обновления.', - 'Usage: /extensions install ': - 'Использование: /extensions install <источник>', - 'Installing extension from "{{source}}"...': - 'Установка расширения из "{{source}}"...', - 'Extension "{{name}}" installed successfully.': - 'Расширение "{{name}}" успешно установлено.', + "No extensions to update.": "Нет расширений для обновления.", + "Usage: /extensions install ": "Использование: /extensions install <источник>", + 'Installing extension from "{{source}}"...': 'Установка расширения из "{{source}}"...', + 'Extension "{{name}}" installed successfully.': 'Расширение "{{name}}" успешно установлено.', 'Failed to install extension from "{{source}}": {{error}}': 'Не удалось установить расширение из "{{source}}": {{error}}', - 'Usage: /extensions uninstall ': - 'Использование: /extensions uninstall <имя-расширения>', + "Usage: /extensions uninstall ": + "Использование: /extensions uninstall <имя-расширения>", 'Uninstalling extension "{{name}}"...': 'Удаление расширения "{{name}}"...', - 'Extension "{{name}}" uninstalled successfully.': - 'Расширение "{{name}}" успешно удалено.', + 'Extension "{{name}}" uninstalled successfully.': 'Расширение "{{name}}" успешно удалено.', 'Failed to uninstall extension "{{name}}": {{error}}': 'Не удалось удалить расширение "{{name}}": {{error}}', - 'Usage: /extensions {{command}} [--scope=]': - 'Использование: /extensions {{command}} <расширение> [--scope=]', + "Usage: /extensions {{command}} [--scope=]": + "Использование: /extensions {{command}} <расширение> [--scope=]", 'Unsupported scope "{{scope}}", should be one of "user" or "workspace"': 'Неподдерживаемая область "{{scope}}", должна быть "user" или "workspace"', 'Extension "{{name}}" disabled for scope "{{scope}}"': 'Расширение "{{name}}" отключено для области "{{scope}}"', 'Extension "{{name}}" enabled for scope "{{scope}}"': 'Расширение "{{name}}" включено для области "{{scope}}"', - 'Do you want to continue? [Y/n]: ': 'Хотите продолжить? [Y/n]: ', - 'Do you want to continue?': 'Хотите продолжить?', + "Do you want to continue? [Y/n]: ": "Хотите продолжить? [Y/n]: ", + "Do you want to continue?": "Хотите продолжить?", 'Installing extension "{{name}}".': 'Установка расширения "{{name}}".', - '**Extensions may introduce unexpected behavior. Ensure you have investigated the extension source and trust the author.**': - '**Расширения могут вызывать неожиданное поведение. Убедитесь, что вы изучили источник расширения и доверяете автору.**', - 'This extension will run the following MCP servers:': - 'Это расширение запустит следующие MCP-серверы:', - local: 'локальный', - remote: 'удалённый', - 'This extension will add the following commands: {{commands}}.': - 'Это расширение добавит следующие команды: {{commands}}.', - 'This extension will append info to your QWEN.md context using {{fileName}}': - 'Это расширение добавит информацию в ваш контекст QWEN.md с помощью {{fileName}}', - 'This extension will exclude the following core tools: {{tools}}': - 'Это расширение исключит следующие основные инструменты: {{tools}}', - 'This extension will install the following skills:': - 'Это расширение установит следующие навыки:', - 'This extension will install the following subagents:': - 'Это расширение установит следующие подагенты:', + "**Extensions may introduce unexpected behavior. Ensure you have investigated the extension source and trust the author.**": + "**Расширения могут вызывать неожиданное поведение. Убедитесь, что вы изучили источник расширения и доверяете автору.**", + "This extension will run the following MCP servers:": + "Это расширение запустит следующие MCP-серверы:", + local: "локальный", + remote: "удалённый", + "This extension will add the following commands: {{commands}}.": + "Это расширение добавит следующие команды: {{commands}}.", + "This extension will append info to your QWEN.md context using {{fileName}}": + "Это расширение добавит информацию в ваш контекст QWEN.md с помощью {{fileName}}", + "This extension will exclude the following core tools: {{tools}}": + "Это расширение исключит следующие основные инструменты: {{tools}}", + "This extension will install the following skills:": "Это расширение установит следующие навыки:", + "This extension will install the following subagents:": + "Это расширение установит следующие подагенты:", 'Installation cancelled for "{{name}}".': 'Установка "{{name}}" отменена.', - 'You are installing an extension from {{originSource}}. Some features may not work perfectly with Qwen Code.': - 'Вы устанавливаете расширение от {{originSource}}. Некоторые функции могут работать не идеально с Qwen Code.', - '--ref and --auto-update are not applicable for marketplace extensions.': - '--ref и --auto-update неприменимы для расширений из маркетплейса.', + "You are installing an extension from {{originSource}}. Some features may not work perfectly with Qwen Code.": + "Вы устанавливаете расширение от {{originSource}}. Некоторые функции могут работать не идеально с Qwen Code.", + "--ref and --auto-update are not applicable for marketplace extensions.": + "--ref и --auto-update неприменимы для расширений из маркетплейса.", 'Extension "{{name}}" installed successfully and enabled.': 'Расширение "{{name}}" успешно установлено и включено.', - 'Installs an extension from a git repository URL, local path, or claude marketplace (marketplace-url:plugin-name).': - 'Устанавливает расширение из URL Git-репозитория, локального пути или маркетплейса Claude (marketplace-url:plugin-name).', - 'The github URL, local path, or marketplace source (marketplace-url:plugin-name) of the extension to install.': - 'URL GitHub, локальный путь или источник в маркетплейсе (marketplace-url:plugin-name) устанавливаемого расширения.', - 'The git ref to install from.': 'Git-ссылка для установки.', - 'Enable auto-update for this extension.': - 'Включить автообновление для этого расширения.', - 'Enable pre-release versions for this extension.': - 'Включить пре-релизные версии для этого расширения.', - 'Acknowledge the security risks of installing an extension and skip the confirmation prompt.': - 'Подтвердить риски безопасности установки расширения и пропустить запрос подтверждения.', - 'The source argument must be provided.': - 'Необходимо указать аргумент источника.', - 'Extension "{{name}}" successfully uninstalled.': - 'Расширение "{{name}}" успешно удалено.', - 'Uninstalls an extension.': 'Удаляет расширение.', - 'The name or source path of the extension to uninstall.': - 'Имя или путь к источнику удаляемого расширения.', - 'Please include the name of the extension to uninstall as a positional argument.': - 'Пожалуйста, укажите имя удаляемого расширения как позиционный аргумент.', - 'Enables an extension.': 'Включает расширение.', - 'The name of the extension to enable.': 'Имя включаемого расширения.', - 'The scope to enable the extenison in. If not set, will be enabled in all scopes.': - 'Область для включения расширения. Если не задана, будет включено во всех областях.', + "Installs an extension from a git repository URL, local path, or claude marketplace (marketplace-url:plugin-name).": + "Устанавливает расширение из URL Git-репозитория, локального пути или маркетплейса Claude (marketplace-url:plugin-name).", + "The github URL, local path, or marketplace source (marketplace-url:plugin-name) of the extension to install.": + "URL GitHub, локальный путь или источник в маркетплейсе (marketplace-url:plugin-name) устанавливаемого расширения.", + "The git ref to install from.": "Git-ссылка для установки.", + "Enable auto-update for this extension.": "Включить автообновление для этого расширения.", + "Enable pre-release versions for this extension.": + "Включить пре-релизные версии для этого расширения.", + "Acknowledge the security risks of installing an extension and skip the confirmation prompt.": + "Подтвердить риски безопасности установки расширения и пропустить запрос подтверждения.", + "The source argument must be provided.": "Необходимо указать аргумент источника.", + 'Extension "{{name}}" successfully uninstalled.': 'Расширение "{{name}}" успешно удалено.', + "Uninstalls an extension.": "Удаляет расширение.", + "The name or source path of the extension to uninstall.": + "Имя или путь к источнику удаляемого расширения.", + "Please include the name of the extension to uninstall as a positional argument.": + "Пожалуйста, укажите имя удаляемого расширения как позиционный аргумент.", + "Enables an extension.": "Включает расширение.", + "The name of the extension to enable.": "Имя включаемого расширения.", + "The scope to enable the extenison in. If not set, will be enabled in all scopes.": + "Область для включения расширения. Если не задана, будет включено во всех областях.", 'Extension "{{name}}" successfully enabled for scope "{{scope}}".': 'Расширение "{{name}}" успешно включено для области "{{scope}}".', 'Extension "{{name}}" successfully enabled in all scopes.': 'Расширение "{{name}}" успешно включено во всех областях.', - 'Invalid scope: {{scope}}. Please use one of {{scopes}}.': - 'Недопустимая область: {{scope}}. Пожалуйста, используйте одну из {{scopes}}.', - 'Disables an extension.': 'Отключает расширение.', - 'The name of the extension to disable.': 'Имя отключаемого расширения.', - 'The scope to disable the extenison in.': - 'Область для отключения расширения.', + "Invalid scope: {{scope}}. Please use one of {{scopes}}.": + "Недопустимая область: {{scope}}. Пожалуйста, используйте одну из {{scopes}}.", + "Disables an extension.": "Отключает расширение.", + "The name of the extension to disable.": "Имя отключаемого расширения.", + "The scope to disable the extenison in.": "Область для отключения расширения.", 'Extension "{{name}}" successfully disabled for scope "{{scope}}".': 'Расширение "{{name}}" успешно отключено для области "{{scope}}".', 'Extension "{{name}}" successfully updated: {{oldVersion}} → {{newVersion}}.': 'Расширение "{{name}}" успешно обновлено: {{oldVersion}} → {{newVersion}}.', 'Unable to install extension "{{name}}" due to missing install metadata': 'Невозможно установить расширение "{{name}}" из-за отсутствия метаданных установки', - 'Extension "{{name}}" is already up to date.': - 'Расширение "{{name}}" уже актуально.', - 'Updates all extensions or a named extension to the latest version.': - 'Обновляет все расширения или указанное расширение до последней версии.', - 'The name of the extension to update.': 'Имя обновляемого расширения.', - 'Update all extensions.': 'Обновить все расширения.', - 'Either an extension name or --all must be provided': - 'Необходимо указать имя расширения или --all', - 'Lists installed extensions.': 'Показывает установленные расширения.', - 'Path:': 'Путь:', - 'Source:': 'Источник:', - 'Type:': 'Тип:', - 'Ref:': 'Ссылка:', - 'Release tag:': 'Тег релиза:', - 'Enabled (User):': 'Включено (Пользователь):', - 'Enabled (Workspace):': 'Включено (Рабочее пространство):', - 'Context files:': 'Контекстные файлы:', - 'Skills:': 'Навыки:', - 'Agents:': 'Агенты:', - 'MCP servers:': 'MCP-серверы:', - 'Link extension failed to install.': - 'Не удалось установить связанное расширение.', + 'Extension "{{name}}" is already up to date.': 'Расширение "{{name}}" уже актуально.', + "Updates all extensions or a named extension to the latest version.": + "Обновляет все расширения или указанное расширение до последней версии.", + "The name of the extension to update.": "Имя обновляемого расширения.", + "Update all extensions.": "Обновить все расширения.", + "Either an extension name or --all must be provided": + "Необходимо указать имя расширения или --all", + "Lists installed extensions.": "Показывает установленные расширения.", + "Path:": "Путь:", + "Source:": "Источник:", + "Type:": "Тип:", + "Ref:": "Ссылка:", + "Release tag:": "Тег релиза:", + "Enabled (User):": "Включено (Пользователь):", + "Enabled (Workspace):": "Включено (Рабочее пространство):", + "Context files:": "Контекстные файлы:", + "Skills:": "Навыки:", + "Agents:": "Агенты:", + "MCP servers:": "MCP-серверы:", + "Link extension failed to install.": "Не удалось установить связанное расширение.", 'Extension "{{name}}" linked successfully and enabled.': 'Расширение "{{name}}" успешно связано и включено.', - 'Links an extension from a local path. Updates made to the local path will always be reflected.': - 'Связывает расширение из локального пути. Изменения в локальном пути будут всегда отражаться.', - 'The name of the extension to link.': 'Имя связываемого расширения.', - 'Set a specific setting for an extension.': - 'Установить конкретную настройку для расширения.', - 'Name of the extension to configure.': 'Имя настраиваемого расширения.', - 'The setting to configure (name or env var).': - 'Настройка для конфигурирования (имя или переменная окружения).', - 'The scope to set the setting in.': 'Область для установки настройки.', - 'List all settings for an extension.': 'Показать все настройки расширения.', - 'Name of the extension.': 'Имя расширения.', + "Links an extension from a local path. Updates made to the local path will always be reflected.": + "Связывает расширение из локального пути. Изменения в локальном пути будут всегда отражаться.", + "The name of the extension to link.": "Имя связываемого расширения.", + "Set a specific setting for an extension.": "Установить конкретную настройку для расширения.", + "Name of the extension to configure.": "Имя настраиваемого расширения.", + "The setting to configure (name or env var).": + "Настройка для конфигурирования (имя или переменная окружения).", + "The scope to set the setting in.": "Область для установки настройки.", + "List all settings for an extension.": "Показать все настройки расширения.", + "Name of the extension.": "Имя расширения.", 'Extension "{{name}}" has no settings to configure.': 'Расширение "{{name}}" не имеет настроек для конфигурирования.', 'Settings for "{{name}}":': 'Настройки для "{{name}}":', - '(workspace)': '(рабочее пространство)', - '(user)': '(пользователь)', - '[not set]': '[не задано]', - '[value stored in keychain]': '[значение хранится в связке ключей]', - 'Manage extension settings.': 'Управление настройками расширений.', - 'You need to specify a command (set or list).': - 'Необходимо указать команду (set или list).', + "(workspace)": "(рабочее пространство)", + "(user)": "(пользователь)", + "[not set]": "[не задано]", + "[value stored in keychain]": "[значение хранится в связке ключей]", + "Manage extension settings.": "Управление настройками расширений.", + "You need to specify a command (set or list).": "Необходимо указать команду (set или list).", // ============================================================================ // Plugin Choice / Marketplace // ============================================================================ - 'No plugins available in this marketplace.': - 'В этом маркетплейсе нет доступных плагинов.', + "No plugins available in this marketplace.": "В этом маркетплейсе нет доступных плагинов.", 'Select a plugin to install from marketplace "{{name}}":': 'Выберите плагин для установки из маркетплейса "{{name}}":', - 'Plugin selection cancelled.': 'Выбор плагина отменён.', + "Plugin selection cancelled.": "Выбор плагина отменён.", 'Select a plugin from "{{name}}"': 'Выберите плагин из "{{name}}"', - 'Use ↑↓ or j/k to navigate, Enter to select, Escape to cancel': - 'Используйте ↑↓ или j/k для навигации, Enter для выбора, Escape для отмены', - '{{count}} more above': 'ещё {{count}} выше', - '{{count}} more below': 'ещё {{count}} ниже', - 'manage IDE integration': 'Управление интеграцией с IDE', - 'check status of IDE integration': 'Проверить статус интеграции с IDE', - 'install required IDE companion for {{ideName}}': - 'Установить необходимый компаньон IDE для {{ideName}}', - 'enable IDE integration': 'Включение интеграции с IDE', - 'disable IDE integration': 'Отключение интеграции с IDE', - '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.': - 'Интеграция с IDE не поддерживается в вашем окружении. Для использования этой функции запустите Qwen Code в одной из поддерживаемых IDE: VS Code или форках VS Code.', - 'Set up GitHub Actions': 'Настройка GitHub Actions', - 'Configure terminal keybindings for multiline input (VS Code, Cursor, Windsurf, Trae)': - 'Настройка привязки клавиш терминала для многострочного ввода (VS Code, Cursor, Windsurf, Trae)', - 'Please restart your terminal for the changes to take effect.': - 'Пожалуйста, перезапустите терминал для применения изменений.', - 'Failed to configure terminal: {{error}}': - 'Не удалось настроить терминал: {{error}}', - 'Could not determine {{terminalName}} config path on Windows: APPDATA environment variable is not set.': - 'Не удалось определить путь конфигурации {{terminalName}} в Windows: переменная окружения APPDATA не установлена.', - '{{terminalName}} keybindings.json exists but is not a valid JSON array. Please fix the file manually or delete it to allow automatic configuration.': - '{{terminalName}} keybindings.json существует, но не является корректным массивом JSON. Пожалуйста, исправьте файл вручную или удалите его для автоматической настройки.', - 'File: {{file}}': 'Файл: {{file}}', - 'Failed to parse {{terminalName}} keybindings.json. The file contains invalid JSON. Please fix the file manually or delete it to allow automatic configuration.': - 'Не удалось разобрать {{terminalName}} keybindings.json. Файл содержит некорректный JSON. Пожалуйста, исправьте файл вручную или удалите его для автоматической настройки.', - 'Error: {{error}}': 'Ошибка: {{error}}', - 'Shift+Enter binding already exists': 'Привязка Shift+Enter уже существует', - 'Ctrl+Enter binding already exists': 'Привязка Ctrl+Enter уже существует', - 'Existing keybindings detected. Will not modify to avoid conflicts.': - 'Обнаружены существующие привязки клавиш. Не будут изменены во избежание конфликтов.', - 'Please check and modify manually if needed: {{file}}': - 'Пожалуйста, проверьте и измените вручную при необходимости: {{file}}', - 'Added Shift+Enter and Ctrl+Enter keybindings to {{terminalName}}.': - 'Добавлены привязки Shift+Enter и Ctrl+Enter для {{terminalName}}.', - 'Modified: {{file}}': 'Изменено: {{file}}', - '{{terminalName}} keybindings already configured.': - 'Привязки клавиш {{terminalName}} уже настроены.', - 'Failed to configure {{terminalName}}.': - 'Не удалось настроить {{terminalName}}.', - 'Your terminal is already configured for an optimal experience with multiline input (Shift+Enter and Ctrl+Enter).': - 'Ваш терминал уже настроен для оптимальной работы с многострочным вводом (Shift+Enter и Ctrl+Enter).', + "Use ↑↓ or j/k to navigate, Enter to select, Escape to cancel": + "Используйте ↑↓ или j/k для навигации, Enter для выбора, Escape для отмены", + "{{count}} more above": "ещё {{count}} выше", + "{{count}} more below": "ещё {{count}} ниже", + "manage IDE integration": "Управление интеграцией с IDE", + "check status of IDE integration": "Проверить статус интеграции с IDE", + "install required IDE companion for {{ideName}}": + "Установить необходимый компаньон IDE для {{ideName}}", + "enable IDE integration": "Включение интеграции с IDE", + "disable IDE integration": "Отключение интеграции с IDE", + "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.": + "Интеграция с IDE не поддерживается в вашем окружении. Для использования этой функции запустите Qwen Code в одной из поддерживаемых IDE: VS Code или форках VS Code.", + "Set up GitHub Actions": "Настройка GitHub Actions", + "Configure terminal keybindings for multiline input (VS Code, Cursor, Windsurf, Trae)": + "Настройка привязки клавиш терминала для многострочного ввода (VS Code, Cursor, Windsurf, Trae)", + "Please restart your terminal for the changes to take effect.": + "Пожалуйста, перезапустите терминал для применения изменений.", + "Failed to configure terminal: {{error}}": "Не удалось настроить терминал: {{error}}", + "Could not determine {{terminalName}} config path on Windows: APPDATA environment variable is not set.": + "Не удалось определить путь конфигурации {{terminalName}} в Windows: переменная окружения APPDATA не установлена.", + "{{terminalName}} keybindings.json exists but is not a valid JSON array. Please fix the file manually or delete it to allow automatic configuration.": + "{{terminalName}} keybindings.json существует, но не является корректным массивом JSON. Пожалуйста, исправьте файл вручную или удалите его для автоматической настройки.", + "File: {{file}}": "Файл: {{file}}", + "Failed to parse {{terminalName}} keybindings.json. The file contains invalid JSON. Please fix the file manually or delete it to allow automatic configuration.": + "Не удалось разобрать {{terminalName}} keybindings.json. Файл содержит некорректный JSON. Пожалуйста, исправьте файл вручную или удалите его для автоматической настройки.", + "Error: {{error}}": "Ошибка: {{error}}", + "Shift+Enter binding already exists": "Привязка Shift+Enter уже существует", + "Ctrl+Enter binding already exists": "Привязка Ctrl+Enter уже существует", + "Existing keybindings detected. Will not modify to avoid conflicts.": + "Обнаружены существующие привязки клавиш. Не будут изменены во избежание конфликтов.", + "Please check and modify manually if needed: {{file}}": + "Пожалуйста, проверьте и измените вручную при необходимости: {{file}}", + "Added Shift+Enter and Ctrl+Enter keybindings to {{terminalName}}.": + "Добавлены привязки Shift+Enter и Ctrl+Enter для {{terminalName}}.", + "Modified: {{file}}": "Изменено: {{file}}", + "{{terminalName}} keybindings already configured.": + "Привязки клавиш {{terminalName}} уже настроены.", + "Failed to configure {{terminalName}}.": "Не удалось настроить {{terminalName}}.", + "Your terminal is already configured for an optimal experience with multiline input (Shift+Enter and Ctrl+Enter).": + "Ваш терминал уже настроен для оптимальной работы с многострочным вводом (Shift+Enter и Ctrl+Enter).", // ============================================================================ // Commands - Hooks // ============================================================================ - 'Manage Qwen Code hooks': 'Управлять хуками Qwen Code', - 'List all configured hooks': 'Показать все настроенные хуки', - 'Enable a disabled hook': 'Включить отключенный хук', - 'Disable an active hook': 'Отключить активный хук', + "Manage Qwen Code hooks": "Управлять хуками Qwen Code", + "List all configured hooks": "Показать все настроенные хуки", + "Enable a disabled hook": "Включить отключенный хук", + "Disable an active hook": "Отключить активный хук", // Hooks - Dialog - Hooks: 'Хуки', - 'Loading hooks...': 'Загрузка хуков...', - 'Error loading hooks:': 'Ошибка загрузки хуков:', - 'Press Escape to close': 'Нажмите Escape для закрытия', - 'Press Escape, Ctrl+C, or Ctrl+D to cancel': - 'Нажмите Escape, Ctrl+C или Ctrl+D для отмены', - 'Press Space, Enter, or Escape to dismiss': - 'Нажмите Пробел, Enter или Escape для закрытия', - 'No hook selected': 'Хук не выбран', + Hooks: "Хуки", + "Loading hooks...": "Загрузка хуков...", + "Error loading hooks:": "Ошибка загрузки хуков:", + "Press Escape to close": "Нажмите Escape для закрытия", + "Press Escape, Ctrl+C, or Ctrl+D to cancel": "Нажмите Escape, Ctrl+C или Ctrl+D для отмены", + "Press Space, Enter, or Escape to dismiss": "Нажмите Пробел, Enter или Escape для закрытия", + "No hook selected": "Хук не выбран", // Hooks - List Step - 'No hook events found.': 'События хуков не найдены.', - '{{count}} hook configured': '{{count}} хук настроен', - '{{count}} hooks configured': '{{count}} хуков настроено', - 'This menu is read-only. To add or modify hooks, edit settings.json directly or ask Qwen Code.': - 'Это меню только для чтения. Чтобы добавить или изменить хуки, отредактируйте settings.json напрямую или спросите Qwen Code.', - 'Enter to select · Esc to cancel': 'Enter для выбора · Esc для отмены', + "No hook events found.": "События хуков не найдены.", + "{{count}} hook configured": "{{count}} хук настроен", + "{{count}} hooks configured": "{{count}} хуков настроено", + "This menu is read-only. To add or modify hooks, edit settings.json directly or ask Qwen Code.": + "Это меню только для чтения. Чтобы добавить или изменить хуки, отредактируйте settings.json напрямую или спросите Qwen Code.", + "Enter to select · Esc to cancel": "Enter для выбора · Esc для отмены", // Hooks - Detail Step - 'Exit codes:': 'Коды выхода:', - 'Configured hooks:': 'Настроенные хуки:', - 'No hooks configured for this event.': - 'Для этого события нет настроенных хуков.', - 'To add hooks, edit settings.json directly or ask Qwen.': - 'Чтобы добавить хуки, отредактируйте settings.json напрямую или спросите Qwen.', - 'Enter to select · Esc to go back': 'Enter для выбора · Esc для возврата', + "Exit codes:": "Коды выхода:", + "Configured hooks:": "Настроенные хуки:", + "No hooks configured for this event.": "Для этого события нет настроенных хуков.", + "To add hooks, edit settings.json directly or ask Qwen.": + "Чтобы добавить хуки, отредактируйте settings.json напрямую или спросите Qwen.", + "Enter to select · Esc to go back": "Enter для выбора · Esc для возврата", // Hooks - Config Detail Step - 'Hook details': 'Детали хука', - 'Event:': 'Событие:', - 'Extension:': 'Расширение:', - 'Desc:': 'Описание:', - 'No hook config selected': 'Конфигурация хука не выбрана', - 'To modify or remove this hook, edit settings.json directly or ask Qwen to help.': - 'Чтобы изменить или удалить этот хук, отредактируйте settings.json напрямую или спросите Qwen.', + "Hook details": "Детали хука", + "Event:": "Событие:", + "Extension:": "Расширение:", + "Desc:": "Описание:", + "No hook config selected": "Конфигурация хука не выбрана", + "To modify or remove this hook, edit settings.json directly or ask Qwen to help.": + "Чтобы изменить или удалить этот хук, отредактируйте settings.json напрямую или спросите Qwen.", // Hooks - Disabled Step - 'Hook Configuration - Disabled': 'Конфигурация хуков - Отключено', - 'All hooks are currently disabled. You have {{count}} that are not running.': - 'Все хуки в данный момент отключены. У вас {{count}} не выполняются.', - '{{count}} configured hook': '{{count}} настроенный хук', - '{{count}} configured hooks': '{{count}} настроенных хуков', - 'When hooks are disabled:': 'Когда хуки отключены:', - 'No hook commands will execute': 'Никакие команды хуков не будут выполняться', - 'StatusLine will not be displayed': 'StatusLine не будет отображаться', - 'Tool operations will proceed without hook validation': - 'Операции инструментов будут выполняться без проверки хуков', + "Hook Configuration - Disabled": "Конфигурация хуков - Отключено", + "All hooks are currently disabled. You have {{count}} that are not running.": + "Все хуки в данный момент отключены. У вас {{count}} не выполняются.", + "{{count}} configured hook": "{{count}} настроенный хук", + "{{count}} configured hooks": "{{count}} настроенных хуков", + "When hooks are disabled:": "Когда хуки отключены:", + "No hook commands will execute": "Никакие команды хуков не будут выполняться", + "StatusLine will not be displayed": "StatusLine не будет отображаться", + "Tool operations will proceed without hook validation": + "Операции инструментов будут выполняться без проверки хуков", 'To re-enable hooks, remove "disableAllHooks" from settings.json or ask Qwen Code.': 'Чтобы снова включить хуки, удалите "disableAllHooks" из settings.json или спросите Qwen Code.', // Hooks - Source - Project: 'Проект', - User: 'Пользователь', - System: 'Система', - Extension: 'Расширение', - 'Local Settings': 'Локальные настройки', - 'User Settings': 'Пользовательские настройки', - 'System Settings': 'Системные настройки', - Extensions: 'Расширения', + Project: "Проект", + User: "Пользователь", + System: "Система", + Extension: "Расширение", + "Local Settings": "Локальные настройки", + "User Settings": "Пользовательские настройки", + "System Settings": "Системные настройки", + Extensions: "Расширения", // Hooks - Status - '✓ Enabled': '✓ Включен', - '✗ Disabled': '✗ Отключен', + "✓ Enabled": "✓ Включен", + "✗ Disabled": "✗ Отключен", // Hooks - Event Descriptions (short) - 'Before tool execution': 'Перед выполнением инструмента', - 'After tool execution': 'После выполнения инструмента', - 'After tool execution fails': 'При неудачном выполнении инструмента', - 'When notifications are sent': 'При отправке уведомлений', - 'When the user submits a prompt': 'Когда пользователь отправляет промпт', - 'When a new session is started': 'При запуске новой сессии', - 'Right before Qwen Code concludes its response': - 'Непосредственно перед завершением ответа Qwen Code', - 'When a subagent (Agent tool call) is started': - 'При запуске субагента (вызов инструмента Agent)', - 'Right before a subagent concludes its response': - 'Непосредственно перед завершением ответа субагента', - 'Before conversation compaction': 'Перед сжатием разговора', - 'When a session is ending': 'При завершении сессии', - 'When a permission dialog is displayed': 'При отображении диалога разрешений', + "Before tool execution": "Перед выполнением инструмента", + "After tool execution": "После выполнения инструмента", + "After tool execution fails": "При неудачном выполнении инструмента", + "When notifications are sent": "При отправке уведомлений", + "When the user submits a prompt": "Когда пользователь отправляет промпт", + "When a new session is started": "При запуске новой сессии", + "Right before Qwen Code concludes its response": + "Непосредственно перед завершением ответа Qwen Code", + "When a subagent (Agent tool call) is started": "При запуске субагента (вызов инструмента Agent)", + "Right before a subagent concludes its response": + "Непосредственно перед завершением ответа субагента", + "Before conversation compaction": "Перед сжатием разговора", + "When a session is ending": "При завершении сессии", + "When a permission dialog is displayed": "При отображении диалога разрешений", // Hooks - Event Descriptions (detailed) - 'Input to command is JSON of tool call arguments.': - 'Ввод в команду — это JSON аргументов вызова инструмента.', + "Input to command is JSON of tool call arguments.": + "Ввод в команду — это JSON аргументов вызова инструмента.", 'Input to command is JSON with fields "inputs" (tool call arguments) and "response" (tool call response).': 'Ввод в команду — это JSON с полями "inputs" (аргументы вызова инструмента) и "response" (ответ вызова инструмента).', - 'Input to command is JSON with tool_name, tool_input, tool_use_id, error, error_type, is_interrupt, and is_timeout.': - 'Ввод в команду — это JSON с tool_name, tool_input, tool_use_id, error, error_type, is_interrupt и is_timeout.', - 'Input to command is JSON with notification message and type.': - 'Ввод в команду — это JSON с сообщением уведомления и типом.', - 'Input to command is JSON with original user prompt text.': - 'Ввод в команду — это JSON с исходным текстом промпта пользователя.', - 'Input to command is JSON with session start source.': - 'Ввод в команду — это JSON с источником запуска сессии.', - 'Input to command is JSON with session end reason.': - 'Ввод в команду — это JSON с причиной завершения сессии.', - 'Input to command is JSON with agent_id and agent_type.': - 'Ввод в команду — это JSON с agent_id и agent_type.', - 'Input to command is JSON with agent_id, agent_type, and agent_transcript_path.': - 'Ввод в команду — это JSON с agent_id, agent_type и agent_transcript_path.', - 'Input to command is JSON with compaction details.': - 'Ввод в команду — это JSON с деталями сжатия.', - 'Input to command is JSON with tool_name, tool_input, and tool_use_id. Output JSON with hookSpecificOutput containing decision to allow or deny.': - 'Ввод в команду — это JSON с tool_name, tool_input и tool_use_id. Вывод — JSON с hookSpecificOutput, содержащим решение о разрешении или отказе.', + "Input to command is JSON with tool_name, tool_input, tool_use_id, error, error_type, is_interrupt, and is_timeout.": + "Ввод в команду — это JSON с tool_name, tool_input, tool_use_id, error, error_type, is_interrupt и is_timeout.", + "Input to command is JSON with notification message and type.": + "Ввод в команду — это JSON с сообщением уведомления и типом.", + "Input to command is JSON with original user prompt text.": + "Ввод в команду — это JSON с исходным текстом промпта пользователя.", + "Input to command is JSON with session start source.": + "Ввод в команду — это JSON с источником запуска сессии.", + "Input to command is JSON with session end reason.": + "Ввод в команду — это JSON с причиной завершения сессии.", + "Input to command is JSON with agent_id and agent_type.": + "Ввод в команду — это JSON с agent_id и agent_type.", + "Input to command is JSON with agent_id, agent_type, and agent_transcript_path.": + "Ввод в команду — это JSON с agent_id, agent_type и agent_transcript_path.", + "Input to command is JSON with compaction details.": + "Ввод в команду — это JSON с деталями сжатия.", + "Input to command is JSON with tool_name, tool_input, and tool_use_id. Output JSON with hookSpecificOutput containing decision to allow or deny.": + "Ввод в команду — это JSON с tool_name, tool_input и tool_use_id. Вывод — JSON с hookSpecificOutput, содержащим решение о разрешении или отказе.", // Hooks - Exit Code Descriptions - 'stdout/stderr not shown': 'stdout/stderr не отображаются', - 'show stderr to model and continue conversation': - 'показать stderr модели и продолжить разговор', - 'show stderr to user only': 'показать stderr только пользователю', - 'stdout shown in transcript mode (ctrl+o)': - 'stdout отображается в режиме транскрипции (ctrl+o)', - 'show stderr to model immediately': 'показать stderr модели немедленно', - 'show stderr to user only but continue with tool call': - 'показать stderr только пользователю, но продолжить вызов инструмента', - 'block processing, erase original prompt, and show stderr to user only': - 'заблокировать обработку, стереть исходный промпт и показать stderr только пользователю', - 'stdout shown to Qwen': 'stdout показан Qwen', - 'show stderr to user only (blocking errors ignored)': - 'показать stderr только пользователю (блокирующие ошибки игнорируются)', - 'command completes successfully': 'команда успешно завершена', - 'stdout shown to subagent': 'stdout показан субагенту', - 'show stderr to subagent and continue having it run': - 'показать stderr субагенту и продолжить его выполнение', - 'stdout appended as custom compact instructions': - 'stdout добавлен как пользовательские инструкции сжатия', - 'block compaction': 'заблокировать сжатие', - 'show stderr to user only but continue with compaction': - 'показать stderr только пользователю, но продолжить сжатие', - 'use hook decision if provided': - 'использовать решение хука, если предоставлено', + "stdout/stderr not shown": "stdout/stderr не отображаются", + "show stderr to model and continue conversation": "показать stderr модели и продолжить разговор", + "show stderr to user only": "показать stderr только пользователю", + "stdout shown in transcript mode (ctrl+o)": "stdout отображается в режиме транскрипции (ctrl+o)", + "show stderr to model immediately": "показать stderr модели немедленно", + "show stderr to user only but continue with tool call": + "показать stderr только пользователю, но продолжить вызов инструмента", + "block processing, erase original prompt, and show stderr to user only": + "заблокировать обработку, стереть исходный промпт и показать stderr только пользователю", + "stdout shown to Qwen": "stdout показан Qwen", + "show stderr to user only (blocking errors ignored)": + "показать stderr только пользователю (блокирующие ошибки игнорируются)", + "command completes successfully": "команда успешно завершена", + "stdout shown to subagent": "stdout показан субагенту", + "show stderr to subagent and continue having it run": + "показать stderr субагенту и продолжить его выполнение", + "stdout appended as custom compact instructions": + "stdout добавлен как пользовательские инструкции сжатия", + "block compaction": "заблокировать сжатие", + "show stderr to user only but continue with compaction": + "показать stderr только пользователю, но продолжить сжатие", + "use hook decision if provided": "использовать решение хука, если предоставлено", // Hooks - Messages - 'Config not loaded.': 'Конфигурация не загружена.', - 'Hooks are not enabled. Enable hooks in settings to use this feature.': - 'Хуки не включены. Включите хуки в настройках, чтобы использовать эту функцию.', - 'No hooks configured. Add hooks in your settings.json file.': - 'Хуки не настроены. Добавьте хуки в файл settings.json.', - 'Configured Hooks ({{count}} total)': 'Настроенные хуки (всего {{count}})', + "Config not loaded.": "Конфигурация не загружена.", + "Hooks are not enabled. Enable hooks in settings to use this feature.": + "Хуки не включены. Включите хуки в настройках, чтобы использовать эту функцию.", + "No hooks configured. Add hooks in your settings.json file.": + "Хуки не настроены. Добавьте хуки в файл settings.json.", + "Configured Hooks ({{count}} total)": "Настроенные хуки (всего {{count}})", // ============================================================================ // Commands - Session Export // ============================================================================ - 'Export current session message history to a file': - 'Экспортировать историю сообщений текущей сессии в файл', - 'Export session to HTML format': 'Экспортировать сессию в формат HTML', - 'Export session to JSON format': 'Экспортировать сессию в формат JSON', - 'Export session to JSONL format (one message per line)': - 'Экспортировать сессию в формат JSONL (одно сообщение на строку)', - 'Export session to markdown format': - 'Экспортировать сессию в формат Markdown', + "Export current session message history to a file": + "Экспортировать историю сообщений текущей сессии в файл", + "Export session to HTML format": "Экспортировать сессию в формат HTML", + "Export session to JSON format": "Экспортировать сессию в формат JSON", + "Export session to JSONL format (one message per line)": + "Экспортировать сессию в формат JSONL (одно сообщение на строку)", + "Export session to markdown format": "Экспортировать сессию в формат Markdown", // ============================================================================ // Commands - Insights // ============================================================================ - 'generate personalized programming insights from your chat history': - 'Создать персонализированные инсайты по программированию на основе истории чата', + "generate personalized programming insights from your chat history": + "Создать персонализированные инсайты по программированию на основе истории чата", // ============================================================================ // Commands - Session History // ============================================================================ - 'Resume a previous session': 'Продолжить предыдущую сессию', - 'Restore a tool call. This will reset the conversation and file history to the state it was in when the tool call was suggested': - 'Восстановить вызов инструмента. Это вернет историю разговора и файлов к состоянию на момент, когда был предложен этот вызов инструмента', - 'Could not detect terminal type. Supported terminals: VS Code, Cursor, Windsurf, and Trae.': - 'Не удалось определить тип терминала. Поддерживаемые терминалы: VS Code, Cursor, Windsurf и Trae.', - 'Terminal "{{terminal}}" is not supported yet.': - 'Терминал "{{terminal}}" еще не поддерживается.', + "Resume a previous session": "Продолжить предыдущую сессию", + "Restore a tool call. This will reset the conversation and file history to the state it was in when the tool call was suggested": + "Восстановить вызов инструмента. Это вернет историю разговора и файлов к состоянию на момент, когда был предложен этот вызов инструмента", + "Could not detect terminal type. Supported terminals: VS Code, Cursor, Windsurf, and Trae.": + "Не удалось определить тип терминала. Поддерживаемые терминалы: VS Code, Cursor, Windsurf и Trae.", + 'Terminal "{{terminal}}" is not supported yet.': 'Терминал "{{terminal}}" еще не поддерживается.', // ============================================================================ // Команды - Язык // ============================================================================ - 'Invalid language. Available: {{options}}': - 'Недопустимый язык. Доступны: {{options}}', - 'Language subcommands do not accept additional arguments.': - 'Подкоманды языка не принимают дополнительных аргументов.', - 'Current UI language: {{lang}}': 'Текущий язык интерфейса: {{lang}}', - 'Current LLM output language: {{lang}}': 'Текущий язык вывода LLM: {{lang}}', - 'LLM output language not set': 'Язык вывода LLM не установлен', - 'Set UI language': 'Установка языка интерфейса', - 'Set LLM output language': 'Установка языка вывода LLM', - 'Usage: /language ui [{{options}}]': - 'Использование: /language ui [{{options}}]', - 'Usage: /language output ': - 'Использование: /language output ', - 'Example: /language output 中文': 'Пример: /language output 中文', - 'Example: /language output English': 'Пример: /language output English', - 'Example: /language output 日本語': 'Пример: /language output 日本語', - 'Example: /language output Português': 'Пример: /language output Português', - 'UI language changed to {{lang}}': 'Язык интерфейса изменен на {{lang}}', - 'LLM output language set to {{lang}}': - 'Язык вывода LLM установлен на {{lang}}', - 'LLM output language rule file generated at {{path}}': - 'Файл правил языка вывода LLM создан в {{path}}', - 'Please restart the application for the changes to take effect.': - 'Пожалуйста, перезапустите приложение для применения изменений.', - 'Failed to generate LLM output language rule file: {{error}}': - 'Не удалось создать файл правил языка вывода LLM: {{error}}', - 'Invalid command. Available subcommands:': - 'Неверная команда. Доступные подкоманды:', - 'Available subcommands:': 'Доступные подкоманды:', - 'To request additional UI language packs, please open an issue on GitHub.': - 'Для запроса дополнительных языковых пакетов интерфейса, пожалуйста, создайте обращение на GitHub.', - 'Available options:': 'Доступные варианты:', - 'Set UI language to {{name}}': 'Установить язык интерфейса на {{name}}', + "Invalid language. Available: {{options}}": "Недопустимый язык. Доступны: {{options}}", + "Language subcommands do not accept additional arguments.": + "Подкоманды языка не принимают дополнительных аргументов.", + "Current UI language: {{lang}}": "Текущий язык интерфейса: {{lang}}", + "Current LLM output language: {{lang}}": "Текущий язык вывода LLM: {{lang}}", + "LLM output language not set": "Язык вывода LLM не установлен", + "Set UI language": "Установка языка интерфейса", + "Set LLM output language": "Установка языка вывода LLM", + "Usage: /language ui [{{options}}]": "Использование: /language ui [{{options}}]", + "Usage: /language output ": "Использование: /language output ", + "Example: /language output 中文": "Пример: /language output 中文", + "Example: /language output English": "Пример: /language output English", + "Example: /language output 日本語": "Пример: /language output 日本語", + "Example: /language output Português": "Пример: /language output Português", + "UI language changed to {{lang}}": "Язык интерфейса изменен на {{lang}}", + "LLM output language set to {{lang}}": "Язык вывода LLM установлен на {{lang}}", + "LLM output language rule file generated at {{path}}": + "Файл правил языка вывода LLM создан в {{path}}", + "Please restart the application for the changes to take effect.": + "Пожалуйста, перезапустите приложение для применения изменений.", + "Failed to generate LLM output language rule file: {{error}}": + "Не удалось создать файл правил языка вывода LLM: {{error}}", + "Invalid command. Available subcommands:": "Неверная команда. Доступные подкоманды:", + "Available subcommands:": "Доступные подкоманды:", + "To request additional UI language packs, please open an issue on GitHub.": + "Для запроса дополнительных языковых пакетов интерфейса, пожалуйста, создайте обращение на GitHub.", + "Available options:": "Доступные варианты:", + "Set UI language to {{name}}": "Установить язык интерфейса на {{name}}", // ============================================================================ // Команды - Режим подтверждения // ============================================================================ - 'Tool Approval Mode': 'Режим подтверждения инструментов', - 'Current approval mode: {{mode}}': 'Текущий режим подтверждения: {{mode}}', - 'Available approval modes:': 'Доступные режимы подтверждения:', - 'Approval mode changed to: {{mode}}': - 'Режим подтверждения изменен на: {{mode}}', - 'Approval mode changed to: {{mode}} (saved to {{scope}} settings{{location}})': - 'Режим подтверждения изменен на: {{mode}} (сохранено в настройках {{scope}}{{location}})', - 'Usage: /approval-mode [--session|--user|--project]': - 'Использование: /approval-mode [--session|--user|--project]', - 'Scope subcommands do not accept additional arguments.': - 'Подкоманды области не принимают дополнительных аргументов.', - 'Plan mode - Analyze only, do not modify files or execute commands': - 'Режим планирования - только анализ, без изменения файлов или выполнения команд', - 'Default mode - Require approval for file edits or shell commands': - 'Режим по умолчанию - требуется подтверждение для редактирования файлов или команд терминала', - 'Auto-edit mode - Automatically approve file edits': - 'Режим авторедактирования - автоматическое подтверждение изменений файлов', - 'YOLO mode - Automatically approve all tools': - 'Режим YOLO - автоматическое подтверждение всех инструментов', - '{{mode}} mode': 'Режим {{mode}}', - 'Settings service is not available; unable to persist the approval mode.': - 'Служба настроек недоступна; невозможно сохранить режим подтверждения.', - 'Failed to save approval mode: {{error}}': - 'Не удалось сохранить режим подтверждения: {{error}}', - 'Failed to change approval mode: {{error}}': - 'Не удалось изменить режим подтверждения: {{error}}', - 'Apply to current session only (temporary)': - 'Применить только к текущей сессии (временно)', - 'Persist for this project/workspace': - 'Сохранить для этого проекта/рабочего пространства', - 'Persist for this user on this machine': - 'Сохранить для этого пользователя на этой машине', - 'Analyze only, do not modify files or execute commands': - 'Только анализ, без изменения файлов или выполнения команд', - 'Require approval for file edits or shell commands': - 'Требуется подтверждение для редактирования файлов или команд терминала', - 'Automatically approve file edits': - 'Автоматически подтверждать изменения файлов', - 'Automatically approve all tools': - 'Автоматически подтверждать все инструменты', - 'Workspace approval mode exists and takes priority. User-level change will have no effect.': - 'Режим подтверждения рабочего пространства существует и имеет приоритет. Изменение на уровне пользователя не будет иметь эффекта.', - 'Apply To': 'Применить к', - 'Workspace Settings': 'Настройки рабочего пространства', + "Tool Approval Mode": "Режим подтверждения инструментов", + "Current approval mode: {{mode}}": "Текущий режим подтверждения: {{mode}}", + "Available approval modes:": "Доступные режимы подтверждения:", + "Approval mode changed to: {{mode}}": "Режим подтверждения изменен на: {{mode}}", + "Approval mode changed to: {{mode}} (saved to {{scope}} settings{{location}})": + "Режим подтверждения изменен на: {{mode}} (сохранено в настройках {{scope}}{{location}})", + "Usage: /approval-mode [--session|--user|--project]": + "Использование: /approval-mode [--session|--user|--project]", + "Scope subcommands do not accept additional arguments.": + "Подкоманды области не принимают дополнительных аргументов.", + "Plan mode - Analyze only, do not modify files or execute commands": + "Режим планирования - только анализ, без изменения файлов или выполнения команд", + "Default mode - Require approval for file edits or shell commands": + "Режим по умолчанию - требуется подтверждение для редактирования файлов или команд терминала", + "Auto-edit mode - Automatically approve file edits": + "Режим авторедактирования - автоматическое подтверждение изменений файлов", + "YOLO mode - Automatically approve all tools": + "Режим YOLO - автоматическое подтверждение всех инструментов", + "{{mode}} mode": "Режим {{mode}}", + "Settings service is not available; unable to persist the approval mode.": + "Служба настроек недоступна; невозможно сохранить режим подтверждения.", + "Failed to save approval mode: {{error}}": "Не удалось сохранить режим подтверждения: {{error}}", + "Failed to change approval mode: {{error}}": "Не удалось изменить режим подтверждения: {{error}}", + "Apply to current session only (temporary)": "Применить только к текущей сессии (временно)", + "Persist for this project/workspace": "Сохранить для этого проекта/рабочего пространства", + "Persist for this user on this machine": "Сохранить для этого пользователя на этой машине", + "Analyze only, do not modify files or execute commands": + "Только анализ, без изменения файлов или выполнения команд", + "Require approval for file edits or shell commands": + "Требуется подтверждение для редактирования файлов или команд терминала", + "Automatically approve file edits": "Автоматически подтверждать изменения файлов", + "Automatically approve all tools": "Автоматически подтверждать все инструменты", + "Workspace approval mode exists and takes priority. User-level change will have no effect.": + "Режим подтверждения рабочего пространства существует и имеет приоритет. Изменение на уровне пользователя не будет иметь эффекта.", + "Apply To": "Применить к", + "Workspace Settings": "Настройки рабочего пространства", // ============================================================================ // Команды - Память // ============================================================================ - 'Commands for interacting with memory.': - 'Команды для взаимодействия с памятью', - 'Show the current memory contents.': 'Показать текущее содержимое памяти.', - 'Show project-level memory contents.': 'Показать память уровня проекта.', - 'Show global memory contents.': 'Показать глобальную память.', - 'Add content to project-level memory.': - 'Добавить содержимое в память уровня проекта.', - 'Add content to global memory.': 'Добавить содержимое в глобальную память.', - 'Refresh the memory from the source.': 'Обновить память из источника.', - 'Usage: /memory add --project ': - 'Использование: /memory add --project <текст для запоминания>', - 'Usage: /memory add --global ': - 'Использование: /memory add --global <текст для запоминания>', + "Commands for interacting with memory.": "Команды для взаимодействия с памятью", + "Show the current memory contents.": "Показать текущее содержимое памяти.", + "Show project-level memory contents.": "Показать память уровня проекта.", + "Show global memory contents.": "Показать глобальную память.", + "Add content to project-level memory.": "Добавить содержимое в память уровня проекта.", + "Add content to global memory.": "Добавить содержимое в глобальную память.", + "Refresh the memory from the source.": "Обновить память из источника.", + "Usage: /memory add --project ": + "Использование: /memory add --project <текст для запоминания>", + "Usage: /memory add --global ": + "Использование: /memory add --global <текст для запоминания>", 'Attempting to save to project memory: "{{text}}"': 'Попытка сохранить в память проекта: "{{text}}"', 'Attempting to save to global memory: "{{text}}"': 'Попытка сохранить в глобальную память: "{{text}}"', - 'Current memory content from {{count}} file(s):': - 'Текущее содержимое памяти из {{count}} файла(ов):', - 'Memory is currently empty.': 'Память в настоящее время пуста.', - 'Project memory file not found or is currently empty.': - 'Файл памяти проекта не найден или в настоящее время пуст.', - 'Global memory file not found or is currently empty.': - 'Файл глобальной памяти не найден или в настоящее время пуст.', - 'Global memory is currently empty.': - 'Глобальная память в настоящее время пуста.', - 'Global memory content:\n\n---\n{{content}}\n---': - 'Содержимое глобальной памяти:\n\n---\n{{content}}\n---', - 'Project memory content from {{path}}:\n\n---\n{{content}}\n---': - 'Содержимое памяти проекта из {{path}}:\n\n---\n{{content}}\n---', - 'Project memory is currently empty.': - 'Память проекта в настоящее время пуста.', - 'Refreshing memory from source files...': - 'Обновление памяти из исходных файлов...', - 'Add content to the memory. Use --global for global memory or --project for project memory.': - 'Добавить содержимое в память. Используйте --global для глобальной памяти или --project для памяти проекта.', - 'Usage: /memory add [--global|--project] ': - 'Использование: /memory add [--global|--project] <текст для запоминания>', + "Current memory content from {{count}} file(s):": + "Текущее содержимое памяти из {{count}} файла(ов):", + "Memory is currently empty.": "Память в настоящее время пуста.", + "Project memory file not found or is currently empty.": + "Файл памяти проекта не найден или в настоящее время пуст.", + "Global memory file not found or is currently empty.": + "Файл глобальной памяти не найден или в настоящее время пуст.", + "Global memory is currently empty.": "Глобальная память в настоящее время пуста.", + "Global memory content:\n\n---\n{{content}}\n---": + "Содержимое глобальной памяти:\n\n---\n{{content}}\n---", + "Project memory content from {{path}}:\n\n---\n{{content}}\n---": + "Содержимое памяти проекта из {{path}}:\n\n---\n{{content}}\n---", + "Project memory is currently empty.": "Память проекта в настоящее время пуста.", + "Refreshing memory from source files...": "Обновление памяти из исходных файлов...", + "Add content to the memory. Use --global for global memory or --project for project memory.": + "Добавить содержимое в память. Используйте --global для глобальной памяти или --project для памяти проекта.", + "Usage: /memory add [--global|--project] ": + "Использование: /memory add [--global|--project] <текст для запоминания>", 'Attempting to save to memory {{scope}}: "{{fact}}"': 'Попытка сохранить в память {{scope}}: "{{fact}}"', // ============================================================================ // Команды - MCP // ============================================================================ - 'Authenticate with an OAuth-enabled MCP server': - 'Авторизоваться на MCP-сервере с поддержкой OAuth', - 'List configured MCP servers and tools': - 'Просмотр настроенных MCP-серверов и инструментов', - 'Restarts MCP servers.': 'Перезапустить MCP-серверы.', - 'Could not retrieve tool registry.': - 'Не удалось получить реестр инструментов.', - 'No MCP servers configured with OAuth authentication.': - 'Нет MCP-серверов, настроенных с авторизацией OAuth.', - 'MCP servers with OAuth authentication:': 'MCP-серверы с авторизацией OAuth:', - 'Use /mcp auth to authenticate.': - 'Используйте /mcp auth <имя-сервера> для авторизации.', + "Authenticate with an OAuth-enabled MCP server": + "Авторизоваться на MCP-сервере с поддержкой OAuth", + "List configured MCP servers and tools": "Просмотр настроенных MCP-серверов и инструментов", + "Restarts MCP servers.": "Перезапустить MCP-серверы.", + "Could not retrieve tool registry.": "Не удалось получить реестр инструментов.", + "No MCP servers configured with OAuth authentication.": + "Нет MCP-серверов, настроенных с авторизацией OAuth.", + "MCP servers with OAuth authentication:": "MCP-серверы с авторизацией OAuth:", + "Use /mcp auth to authenticate.": + "Используйте /mcp auth <имя-сервера> для авторизации.", "MCP server '{{name}}' not found.": "MCP-сервер '{{name}}' не найден.", "Successfully authenticated and refreshed tools for '{{name}}'.": "Успешно авторизовано и обновлены инструменты для '{{name}}'.", "Failed to authenticate with MCP server '{{name}}': {{error}}": "Не удалось авторизоваться на MCP-сервере '{{name}}': {{error}}", - "Re-discovering tools from '{{name}}'...": - "Повторное обнаружение инструментов от '{{name}}'...", + "Re-discovering tools from '{{name}}'...": "Повторное обнаружение инструментов от '{{name}}'...", "Discovered {{count}} tool(s) from '{{name}}'.": "Обнаружено {{count}} инструмент(ов) от '{{name}}'.", - 'Authentication complete. Returning to server details...': - 'Аутентификация завершена. Возврат к деталям сервера...', - 'Authentication successful.': 'Аутентификация успешна.', - 'If the browser does not open, copy and paste this URL into your browser:': - 'Если браузер не открылся, скопируйте этот URL и вставьте его в браузер:', - 'Make sure to copy the COMPLETE URL - it may wrap across multiple lines.': - '⚠️ Убедитесь, что скопировали ПОЛНЫЙ URL — он может занимать несколько строк.', + "Authentication complete. Returning to server details...": + "Аутентификация завершена. Возврат к деталям сервера...", + "Authentication successful.": "Аутентификация успешна.", + "If the browser does not open, copy and paste this URL into your browser:": + "Если браузер не открылся, скопируйте этот URL и вставьте его в браузер:", + "Make sure to copy the COMPLETE URL - it may wrap across multiple lines.": + "⚠️ Убедитесь, что скопировали ПОЛНЫЙ URL — он может занимать несколько строк.", // ============================================================================ // Команды - Чат // ============================================================================ - 'Manage conversation history.': 'Управление историей диалогов.', - 'List saved conversation checkpoints': - 'Показать сохраненные точки восстановления диалога', - 'No saved conversation checkpoints found.': - 'Не найдено сохраненных точек восстановления диалога.', - 'List of saved conversations:': 'Список сохраненных диалогов:', - 'Note: Newest last, oldest first': - 'Примечание: новые последними, старые первыми', - 'Save the current conversation as a checkpoint. Usage: /chat save ': - 'Сохранить текущий диалог как точку восстановления. Использование: /chat save <тег>', - 'Missing tag. Usage: /chat save ': - 'Отсутствует тег. Использование: /chat save <тег>', - 'Delete a conversation checkpoint. Usage: /chat delete ': - 'Удалить точку восстановления диалога. Использование: /chat delete <тег>', - 'Missing tag. Usage: /chat delete ': - 'Отсутствует тег. Использование: /chat delete <тег>', + "Manage conversation history.": "Управление историей диалогов.", + "List saved conversation checkpoints": "Показать сохраненные точки восстановления диалога", + "No saved conversation checkpoints found.": + "Не найдено сохраненных точек восстановления диалога.", + "List of saved conversations:": "Список сохраненных диалогов:", + "Note: Newest last, oldest first": "Примечание: новые последними, старые первыми", + "Save the current conversation as a checkpoint. Usage: /chat save ": + "Сохранить текущий диалог как точку восстановления. Использование: /chat save <тег>", + "Missing tag. Usage: /chat save ": "Отсутствует тег. Использование: /chat save <тег>", + "Delete a conversation checkpoint. Usage: /chat delete ": + "Удалить точку восстановления диалога. Использование: /chat delete <тег>", + "Missing tag. Usage: /chat delete ": "Отсутствует тег. Использование: /chat delete <тег>", "Conversation checkpoint '{{tag}}' has been deleted.": "Точка восстановления диалога '{{tag}}' удалена.", "Error: No checkpoint found with tag '{{tag}}'.": "Ошибка: точка восстановления с тегом '{{tag}}' не найдена.", - 'Resume a conversation from a checkpoint. Usage: /chat resume ': - 'Возобновить диалог из точки восстановления. Использование: /chat resume <тег>', - 'Missing tag. Usage: /chat resume ': - 'Отсутствует тег. Использование: /chat resume <тег>', - 'No saved checkpoint found with tag: {{tag}}.': - 'Не найдена сохраненная точка восстановления с тегом: {{tag}}.', - 'A checkpoint with the tag {{tag}} already exists. Do you want to overwrite it?': - 'Точка восстановления с тегом {{tag}} уже существует. Перезаписать?', - 'No chat client available to save conversation.': - 'Нет доступного клиента чата для сохранения диалога.', - 'Conversation checkpoint saved with tag: {{tag}}.': - 'Точка восстановления диалога сохранена с тегом: {{tag}}.', - 'No conversation found to save.': 'Нет диалога для сохранения.', - 'No chat client available to share conversation.': - 'Нет доступного клиента чата для экспорта диалога.', - 'Invalid file format. Only .md and .json are supported.': - 'Неверный формат файла. Поддерживаются только .md и .json.', - 'Error sharing conversation: {{error}}': - 'Ошибка при экспорте диалога: {{error}}', - 'Conversation shared to {{filePath}}': 'Диалог экспортирован в {{filePath}}', - 'No conversation found to share.': 'Нет диалога для экспорта.', - 'Share the current conversation to a markdown or json file. Usage: /chat share ': - 'Экспортировать текущий диалог в markdown или json файл. Использование: /chat share <файл>', + "Resume a conversation from a checkpoint. Usage: /chat resume ": + "Возобновить диалог из точки восстановления. Использование: /chat resume <тег>", + "Missing tag. Usage: /chat resume ": "Отсутствует тег. Использование: /chat resume <тег>", + "No saved checkpoint found with tag: {{tag}}.": + "Не найдена сохраненная точка восстановления с тегом: {{tag}}.", + "A checkpoint with the tag {{tag}} already exists. Do you want to overwrite it?": + "Точка восстановления с тегом {{tag}} уже существует. Перезаписать?", + "No chat client available to save conversation.": + "Нет доступного клиента чата для сохранения диалога.", + "Conversation checkpoint saved with tag: {{tag}}.": + "Точка восстановления диалога сохранена с тегом: {{tag}}.", + "No conversation found to save.": "Нет диалога для сохранения.", + "No chat client available to share conversation.": + "Нет доступного клиента чата для экспорта диалога.", + "Invalid file format. Only .md and .json are supported.": + "Неверный формат файла. Поддерживаются только .md и .json.", + "Error sharing conversation: {{error}}": "Ошибка при экспорте диалога: {{error}}", + "Conversation shared to {{filePath}}": "Диалог экспортирован в {{filePath}}", + "No conversation found to share.": "Нет диалога для экспорта.", + "Share the current conversation to a markdown or json file. Usage: /chat share ": + "Экспортировать текущий диалог в markdown или json файл. Использование: /chat share <файл>", // ============================================================================ // Команды - Резюме // ============================================================================ - 'Generate a project summary and save it to .qwen/PROJECT_SUMMARY.md': - 'Сгенерировать сводку проекта и сохранить её в .qwen/PROJECT_SUMMARY.md', - 'No chat client available to generate summary.': - 'Нет доступного чат-клиента для генерации сводки.', - 'Already generating summary, wait for previous request to complete': - 'Генерация сводки уже выполняется, дождитесь завершения предыдущего запроса', - 'No conversation found to summarize.': - 'Не найдено диалогов для создания сводки.', - 'Failed to generate project context summary: {{error}}': - 'Не удалось сгенерировать сводку контекста проекта: {{error}}', - 'Saved project summary to {{filePathForDisplay}}.': - 'Сводка проекта сохранена в {{filePathForDisplay}}', - 'Saving project summary...': 'Сохранение сводки проекта...', - 'Generating project summary...': 'Генерация сводки проекта...', - 'Failed to generate summary - no text content received from LLM response': - 'Не удалось сгенерировать сводку - не получен текстовый контент из ответа LLM', + "Generate a project summary and save it to .qwen/PROJECT_SUMMARY.md": + "Сгенерировать сводку проекта и сохранить её в .qwen/PROJECT_SUMMARY.md", + "No chat client available to generate summary.": + "Нет доступного чат-клиента для генерации сводки.", + "Already generating summary, wait for previous request to complete": + "Генерация сводки уже выполняется, дождитесь завершения предыдущего запроса", + "No conversation found to summarize.": "Не найдено диалогов для создания сводки.", + "Failed to generate project context summary: {{error}}": + "Не удалось сгенерировать сводку контекста проекта: {{error}}", + "Saved project summary to {{filePathForDisplay}}.": + "Сводка проекта сохранена в {{filePathForDisplay}}", + "Saving project summary...": "Сохранение сводки проекта...", + "Generating project summary...": "Генерация сводки проекта...", + "Failed to generate summary - no text content received from LLM response": + "Не удалось сгенерировать сводку - не получен текстовый контент из ответа LLM", // ============================================================================ // Команды - Модель // ============================================================================ - 'Switch the model for this session': 'Переключение модели для этой сессии', - 'Set fast model for background tasks': - 'Установить быструю модель для фоновых задач', - 'Content generator configuration not available.': - 'Конфигурация генератора содержимого недоступна.', - 'Authentication type not available.': 'Тип авторизации недоступен.', - 'No models available for the current authentication type ({{authType}}).': - 'Нет доступных моделей для текущего типа авторизации ({{authType}}).', + "Switch the model for this session": "Переключение модели для этой сессии", + "Set fast model for background tasks": "Установить быструю модель для фоновых задач", + "Content generator configuration not available.": + "Конфигурация генератора содержимого недоступна.", + "Authentication type not available.": "Тип авторизации недоступен.", + "No models available for the current authentication type ({{authType}}).": + "Нет доступных моделей для текущего типа авторизации ({{authType}}).", // ============================================================================ // Команды - Очистка // ============================================================================ - 'Starting a new session, resetting chat, and clearing terminal.': - 'Начало новой сессии, сброс чата и очистка терминала.', - 'Starting a new session and clearing.': 'Начало новой сессии и очистка.', + "Starting a new session, resetting chat, and clearing terminal.": + "Начало новой сессии, сброс чата и очистка терминала.", + "Starting a new session and clearing.": "Начало новой сессии и очистка.", // ============================================================================ // Команды - Сжатие // ============================================================================ - 'Already compressing, wait for previous request to complete': - 'Уже выполняется сжатие, дождитесь завершения предыдущего запроса', - 'Failed to compress chat history.': 'Не удалось сжать историю чата.', - 'Failed to compress chat history: {{error}}': - 'Не удалось сжать историю чата: {{error}}', - 'Compressing chat history': 'Сжатие истории чата', - 'Chat history compressed from {{originalTokens}} to {{newTokens}} tokens.': - 'История чата сжата с {{originalTokens}} до {{newTokens}} токенов.', - 'Compression was not beneficial for this history size.': - 'Сжатие не было полезным для этого размера истории.', - 'Chat history compression did not reduce size. This may indicate issues with the compression prompt.': - 'Сжатие истории чата не уменьшило размер. Это может указывать на проблемы с промптом сжатия.', - 'Could not compress chat history due to a token counting error.': - 'Не удалось сжать историю чата из-за ошибки подсчета токенов.', - 'Chat history is already compressed.': 'История чата уже сжата.', + "Already compressing, wait for previous request to complete": + "Уже выполняется сжатие, дождитесь завершения предыдущего запроса", + "Failed to compress chat history.": "Не удалось сжать историю чата.", + "Failed to compress chat history: {{error}}": "Не удалось сжать историю чата: {{error}}", + "Compressing chat history": "Сжатие истории чата", + "Chat history compressed from {{originalTokens}} to {{newTokens}} tokens.": + "История чата сжата с {{originalTokens}} до {{newTokens}} токенов.", + "Compression was not beneficial for this history size.": + "Сжатие не было полезным для этого размера истории.", + "Chat history compression did not reduce size. This may indicate issues with the compression prompt.": + "Сжатие истории чата не уменьшило размер. Это может указывать на проблемы с промптом сжатия.", + "Could not compress chat history due to a token counting error.": + "Не удалось сжать историю чата из-за ошибки подсчета токенов.", + "Chat history is already compressed.": "История чата уже сжата.", // ============================================================================ // Команды - Директория // ============================================================================ - 'Configuration is not available.': 'Конфигурация недоступна.', - 'Please provide at least one path to add.': - 'Пожалуйста, укажите хотя бы один путь для добавления.', - 'The /directory add command is not supported in restrictive sandbox profiles. Please use --include-directories when starting the session instead.': - 'Команда /directory add не поддерживается в ограничительных профилях песочницы. Пожалуйста, используйте --include-directories при запуске сессии.', - "Error adding '{{path}}': {{error}}": - "Ошибка при добавлении '{{path}}': {{error}}", - 'Successfully added QWEN.md files from the following directories if there are:\n- {{directories}}': - 'Успешно добавлены файлы QWEN.md из следующих директорий (если они есть):\n- {{directories}}', - 'Error refreshing memory: {{error}}': - 'Ошибка при обновлении памяти: {{error}}', - 'Successfully added directories:\n- {{directories}}': - 'Успешно добавлены директории:\n- {{directories}}', - 'Current workspace directories:\n{{directories}}': - 'Текущие директории рабочего пространства:\n{{directories}}', + "Configuration is not available.": "Конфигурация недоступна.", + "Please provide at least one path to add.": + "Пожалуйста, укажите хотя бы один путь для добавления.", + "The /directory add command is not supported in restrictive sandbox profiles. Please use --include-directories when starting the session instead.": + "Команда /directory add не поддерживается в ограничительных профилях песочницы. Пожалуйста, используйте --include-directories при запуске сессии.", + "Error adding '{{path}}': {{error}}": "Ошибка при добавлении '{{path}}': {{error}}", + "Successfully added QWEN.md files from the following directories if there are:\n- {{directories}}": + "Успешно добавлены файлы QWEN.md из следующих директорий (если они есть):\n- {{directories}}", + "Error refreshing memory: {{error}}": "Ошибка при обновлении памяти: {{error}}", + "Successfully added directories:\n- {{directories}}": + "Успешно добавлены директории:\n- {{directories}}", + "Current workspace directories:\n{{directories}}": + "Текущие директории рабочего пространства:\n{{directories}}", // ============================================================================ // Команды - Документация // ============================================================================ - 'Please open the following URL in your browser to view the documentation:\n{{url}}': - 'Пожалуйста, откройте следующий URL в браузере для просмотра документации:\n{{url}}', - 'Opening documentation in your browser: {{url}}': - 'Открытие документации в браузере: {{url}}', + "Please open the following URL in your browser to view the documentation:\n{{url}}": + "Пожалуйста, откройте следующий URL в браузере для просмотра документации:\n{{url}}", + "Opening documentation in your browser: {{url}}": "Открытие документации в браузере: {{url}}", // ============================================================================ // Диалоги - Подтверждение инструментов // ============================================================================ - 'Do you want to proceed?': 'Вы хотите продолжить?', - 'Yes, allow once': 'Да, разрешить один раз', - 'Allow always': 'Всегда разрешать', - Yes: 'Да', - No: 'Нет', - 'No (esc)': 'Нет (esc)', - 'Yes, allow always for this session': 'Да, всегда разрешать для этой сессии', + "Do you want to proceed?": "Вы хотите продолжить?", + "Yes, allow once": "Да, разрешить один раз", + "Allow always": "Всегда разрешать", + Yes: "Да", + No: "Нет", + "No (esc)": "Нет (esc)", + "Yes, allow always for this session": "Да, всегда разрешать для этой сессии", // MCP Management - Core translations - Disable: 'Отключить', - Enable: 'Включить', - Authenticate: 'Аутентификация', - 'Re-authenticate': 'Повторная аутентификация', - 'Clear Authentication': 'Очистить аутентификацию', - disabled: 'отключен', - 'Server:': 'Сервер:', - Reconnect: 'Переподключить', - 'View tools': 'Просмотреть инструменты', - '(disabled)': '(отключен)', - 'Error:': 'Ошибка:', - tool: 'инструмент', - connected: 'подключен', - connecting: 'подключение', - disconnected: 'отключен', - error: 'ошибка', + Disable: "Отключить", + Enable: "Включить", + Authenticate: "Аутентификация", + "Re-authenticate": "Повторная аутентификация", + "Clear Authentication": "Очистить аутентификацию", + disabled: "отключен", + "Server:": "Сервер:", + Reconnect: "Переподключить", + "View tools": "Просмотреть инструменты", + "(disabled)": "(отключен)", + "Error:": "Ошибка:", + tool: "инструмент", + connected: "подключен", + connecting: "подключение", + disconnected: "отключен", + error: "ошибка", // Invalid tool related translations - '{{count}} invalid tools': '{{count}} недействительных инструментов', - invalid: 'недействительный', - 'invalid: {{reason}}': 'недействительно: {{reason}}', - 'missing name': 'отсутствует имя', - 'missing description': 'отсутствует описание', - '(unnamed)': '(без имени)', - 'Warning: This tool cannot be called by the LLM': - 'Предупреждение: Этот инструмент не может быть вызван LLM', - Reason: 'Причина', - 'Tools must have both name and description to be used by the LLM.': - 'Инструменты должны иметь как имя, так и описание, чтобы использоваться LLM.', - 'Modify in progress:': 'Идет изменение:', - 'Save and close external editor to continue': - 'Сохраните и закройте внешний редактор для продолжения', - 'Apply this change?': 'Применить это изменение?', - 'Yes, allow always': 'Да, всегда разрешать', - 'Modify with external editor': 'Изменить во внешнем редакторе', - 'No, suggest changes (esc)': 'Нет, предложить изменения (esc)', + "{{count}} invalid tools": "{{count}} недействительных инструментов", + invalid: "недействительный", + "invalid: {{reason}}": "недействительно: {{reason}}", + "missing name": "отсутствует имя", + "missing description": "отсутствует описание", + "(unnamed)": "(без имени)", + "Warning: This tool cannot be called by the LLM": + "Предупреждение: Этот инструмент не может быть вызван LLM", + Reason: "Причина", + "Tools must have both name and description to be used by the LLM.": + "Инструменты должны иметь как имя, так и описание, чтобы использоваться LLM.", + "Modify in progress:": "Идет изменение:", + "Save and close external editor to continue": + "Сохраните и закройте внешний редактор для продолжения", + "Apply this change?": "Применить это изменение?", + "Yes, allow always": "Да, всегда разрешать", + "Modify with external editor": "Изменить во внешнем редакторе", + "No, suggest changes (esc)": "Нет, предложить изменения (esc)", "Allow execution of: '{{command}}'?": "Разрешить выполнение: '{{command}}'?", - 'Yes, allow always ...': 'Да, всегда разрешать ...', - 'Always allow in this project': 'Всегда разрешать в этом проекте', - 'Always allow {{action}} in this project': - 'Всегда разрешать {{action}} в этом проекте', - 'Always allow for this user': 'Всегда разрешать для этого пользователя', - 'Always allow {{action}} for this user': - 'Всегда разрешать {{action}} для этого пользователя', - 'Yes, restore previous mode ({{mode}})': - 'Да, восстановить предыдущий режим ({{mode}})', - 'Yes, and auto-accept edits': 'Да, и автоматически принимать правки', - 'Yes, and manually approve edits': 'Да, и вручную подтверждать правки', - 'No, keep planning (esc)': 'Нет, продолжить планирование (esc)', - 'URLs to fetch:': 'URL для загрузки:', - 'MCP Server: {{server}}': 'MCP-сервер: {{server}}', - 'Tool: {{tool}}': 'Инструмент: {{tool}}', + "Yes, allow always ...": "Да, всегда разрешать ...", + "Always allow in this project": "Всегда разрешать в этом проекте", + "Always allow {{action}} in this project": "Всегда разрешать {{action}} в этом проекте", + "Always allow for this user": "Всегда разрешать для этого пользователя", + "Always allow {{action}} for this user": "Всегда разрешать {{action}} для этого пользователя", + "Yes, restore previous mode ({{mode}})": "Да, восстановить предыдущий режим ({{mode}})", + "Yes, and auto-accept edits": "Да, и автоматически принимать правки", + "Yes, and manually approve edits": "Да, и вручную подтверждать правки", + "No, keep planning (esc)": "Нет, продолжить планирование (esc)", + "URLs to fetch:": "URL для загрузки:", + "MCP Server: {{server}}": "MCP-сервер: {{server}}", + "Tool: {{tool}}": "Инструмент: {{tool}}", 'Allow execution of MCP tool "{{tool}}" from server "{{server}}"?': 'Разрешить выполнение инструмента MCP "{{tool}}" с сервера "{{server}}"?', 'Yes, always allow tool "{{tool}}" from server "{{server}}"': @@ -1132,357 +1048,336 @@ export default { // ============================================================================ // Диалоги - Подтверждение оболочки // ============================================================================ - 'Shell Command Execution': 'Выполнение команды терминала', - 'A custom command wants to run the following shell commands:': - 'Пользовательская команда хочет выполнить следующие команды терминала:', + "Shell Command Execution": "Выполнение команды терминала", + "A custom command wants to run the following shell commands:": + "Пользовательская команда хочет выполнить следующие команды терминала:", // ============================================================================ // Диалоги - Квота подписки Pro // ============================================================================ - 'Pro quota limit reached for {{model}}.': - 'Исчерпана квота подписки Pro для {{model}}.', - 'Change auth (executes the /auth command)': - 'Изменить авторизацию (выполняет команду /auth)', - 'Continue with {{model}}': 'Продолжить с {{model}}', + "Pro quota limit reached for {{model}}.": "Исчерпана квота подписки Pro для {{model}}.", + "Change auth (executes the /auth command)": "Изменить авторизацию (выполняет команду /auth)", + "Continue with {{model}}": "Продолжить с {{model}}", // ============================================================================ // Диалоги - Приветствие при возвращении // ============================================================================ - 'Current Plan:': 'Текущий план:', - 'Progress: {{done}}/{{total}} tasks completed': - 'Прогресс: {{done}}/{{total}} задач выполнено', - ', {{inProgress}} in progress': ', {{inProgress}} в процессе', - 'Pending Tasks:': 'Ожидающие задачи:', - 'What would you like to do?': 'Что вы хотите сделать?', - 'Choose how to proceed with your session:': - 'Выберите, как продолжить сессию:', - 'Start new chat session': 'Начать новую сессию чата', - 'Continue previous conversation': 'Продолжить предыдущий диалог', - '👋 Welcome back! (Last updated: {{timeAgo}})': - '👋 С возвращением! (Последнее обновление: {{timeAgo}})', - '🎯 Overall Goal:': '🎯 Общая цель:', + "Current Plan:": "Текущий план:", + "Progress: {{done}}/{{total}} tasks completed": "Прогресс: {{done}}/{{total}} задач выполнено", + ", {{inProgress}} in progress": ", {{inProgress}} в процессе", + "Pending Tasks:": "Ожидающие задачи:", + "What would you like to do?": "Что вы хотите сделать?", + "Choose how to proceed with your session:": "Выберите, как продолжить сессию:", + "Start new chat session": "Начать новую сессию чата", + "Continue previous conversation": "Продолжить предыдущий диалог", + "👋 Welcome back! (Last updated: {{timeAgo}})": + "👋 С возвращением! (Последнее обновление: {{timeAgo}})", + "🎯 Overall Goal:": "🎯 Общая цель:", // ============================================================================ // Диалоги - Авторизация // ============================================================================ - 'Get started': 'Начать', - 'Select Authentication Method': 'Выберите метод авторизации', - 'OpenAI API key is required to use OpenAI authentication.': - 'Для использования авторизации OpenAI требуется ключ API OpenAI.', - 'You must select an auth method to proceed. Press Ctrl+C again to exit.': - 'Вы должны выбрать метод авторизации для продолжения. Нажмите Ctrl+C снова для выхода.', - 'Terms of Services and Privacy Notice': - 'Условия обслуживания и уведомление о конфиденциальности', - 'Paid \u00B7 Up to 6,000 requests/5 hrs \u00B7 All Alibaba Cloud Coding Plan Models': - 'Платно \u00B7 До 6 000 запросов/5 часов \u00B7 Все модели Alibaba Cloud Coding Plan', - 'Alibaba Cloud Coding Plan': 'Alibaba Cloud Coding Plan', - 'Bring your own API key': 'Используйте свой API-ключ', - 'API-KEY': 'API-KEY', - 'Use coding plan credentials or your own api-keys/providers.': - 'Используйте учетные данные Coding Plan или свои собственные API-ключи/провайдеры.', - OpenAI: 'OpenAI', - 'Failed to login. Message: {{message}}': - 'Не удалось войти. Сообщение: {{message}}', - 'Authentication is enforced to be {{enforcedType}}, but you are currently using {{currentType}}.': - 'Авторизация должна быть {{enforcedType}}, но вы сейчас используете {{currentType}}.', - 'Please visit this URL to authorize:': - 'Пожалуйста, посетите этот URL для авторизации:', - 'Or scan the QR code below:': 'Или отсканируйте QR-код ниже:', - 'Waiting for authorization': 'Ожидание авторизации', - 'Time remaining:': 'Осталось времени:', - '(Press ESC or CTRL+C to cancel)': '(Нажмите ESC или CTRL+C для отмены)', - 'OAuth token expired (over {{seconds}} seconds). Please select authentication method again.': - 'Токен OAuth истек (более {{seconds}} секунд). Пожалуйста, выберите метод авторизации снова.', - 'Press any key to return to authentication type selection.': - 'Нажмите любую клавишу для возврата к выбору типа авторизации.', - 'Authentication timed out. Please try again.': - 'Время ожидания авторизации истекло. Пожалуйста, попробуйте снова.', - 'Waiting for auth... (Press ESC or CTRL+C to cancel)': - 'Ожидание авторизации... (Нажмите ESC или CTRL+C для отмены)', - 'Missing API key for OpenAI-compatible auth. Set settings.security.auth.apiKey, or set the {{envKeyHint}} environment variable.': - 'Отсутствует API-ключ для аутентификации, совместимой с OpenAI. Укажите settings.security.auth.apiKey или переменную окружения {{envKeyHint}}.', - '{{envKeyHint}} environment variable not found.': - 'Переменная окружения {{envKeyHint}} не найдена.', - '{{envKeyHint}} environment variable not found. Please set it in your .env file or environment variables.': - 'Переменная окружения {{envKeyHint}} не найдена. Укажите её в файле .env или среди системных переменных.', - '{{envKeyHint}} environment variable not found (or set settings.security.auth.apiKey). Please set it in your .env file or environment variables.': - 'Переменная окружения {{envKeyHint}} не найдена (или установите settings.security.auth.apiKey). Укажите её в файле .env или среди системных переменных.', - 'Missing API key for OpenAI-compatible auth. Set the {{envKeyHint}} environment variable.': - 'Отсутствует API-ключ для аутентификации, совместимой с OpenAI. Установите переменную окружения {{envKeyHint}}.', - 'Anthropic provider missing required baseUrl in modelProviders[].baseUrl.': - 'У провайдера Anthropic отсутствует обязательный baseUrl в modelProviders[].baseUrl.', - 'ANTHROPIC_BASE_URL environment variable not found.': - 'Переменная окружения ANTHROPIC_BASE_URL не найдена.', - 'Invalid auth method selected.': 'Выбран недопустимый метод авторизации.', - 'Failed to authenticate. Message: {{message}}': - 'Не удалось авторизоваться. Сообщение: {{message}}', - 'Authenticated successfully with {{authType}} credentials.': - 'Успешно авторизовано с учетными данными {{authType}}.', + "Get started": "Начать", + "Select Authentication Method": "Выберите метод авторизации", + "OpenAI API key is required to use OpenAI authentication.": + "Для использования авторизации OpenAI требуется ключ API OpenAI.", + "You must select an auth method to proceed. Press Ctrl+C again to exit.": + "Вы должны выбрать метод авторизации для продолжения. Нажмите Ctrl+C снова для выхода.", + "Terms of Services and Privacy Notice": "Условия обслуживания и уведомление о конфиденциальности", + "Paid \u00B7 Up to 6,000 requests/5 hrs \u00B7 All Alibaba Cloud Coding Plan Models": + "Платно \u00B7 До 6 000 запросов/5 часов \u00B7 Все модели Alibaba Cloud Coding Plan", + "Alibaba Cloud Coding Plan": "Alibaba Cloud Coding Plan", + "Bring your own API key": "Используйте свой API-ключ", + "API-KEY": "API-KEY", + "Use coding plan credentials or your own api-keys/providers.": + "Используйте учетные данные Coding Plan или свои собственные API-ключи/провайдеры.", + OpenAI: "OpenAI", + "Failed to login. Message: {{message}}": "Не удалось войти. Сообщение: {{message}}", + "Authentication is enforced to be {{enforcedType}}, but you are currently using {{currentType}}.": + "Авторизация должна быть {{enforcedType}}, но вы сейчас используете {{currentType}}.", + "Please visit this URL to authorize:": "Пожалуйста, посетите этот URL для авторизации:", + "Or scan the QR code below:": "Или отсканируйте QR-код ниже:", + "Waiting for authorization": "Ожидание авторизации", + "Time remaining:": "Осталось времени:", + "(Press ESC or CTRL+C to cancel)": "(Нажмите ESC или CTRL+C для отмены)", + "OAuth token expired (over {{seconds}} seconds). Please select authentication method again.": + "Токен OAuth истек (более {{seconds}} секунд). Пожалуйста, выберите метод авторизации снова.", + "Press any key to return to authentication type selection.": + "Нажмите любую клавишу для возврата к выбору типа авторизации.", + "Authentication timed out. Please try again.": + "Время ожидания авторизации истекло. Пожалуйста, попробуйте снова.", + "Waiting for auth... (Press ESC or CTRL+C to cancel)": + "Ожидание авторизации... (Нажмите ESC или CTRL+C для отмены)", + "Missing API key for OpenAI-compatible auth. Set settings.security.auth.apiKey, or set the {{envKeyHint}} environment variable.": + "Отсутствует API-ключ для аутентификации, совместимой с OpenAI. Укажите settings.security.auth.apiKey или переменную окружения {{envKeyHint}}.", + "{{envKeyHint}} environment variable not found.": + "Переменная окружения {{envKeyHint}} не найдена.", + "{{envKeyHint}} environment variable not found. Please set it in your .env file or environment variables.": + "Переменная окружения {{envKeyHint}} не найдена. Укажите её в файле .env или среди системных переменных.", + "{{envKeyHint}} environment variable not found (or set settings.security.auth.apiKey). Please set it in your .env file or environment variables.": + "Переменная окружения {{envKeyHint}} не найдена (или установите settings.security.auth.apiKey). Укажите её в файле .env или среди системных переменных.", + "Missing API key for OpenAI-compatible auth. Set the {{envKeyHint}} environment variable.": + "Отсутствует API-ключ для аутентификации, совместимой с OpenAI. Установите переменную окружения {{envKeyHint}}.", + "Anthropic provider missing required baseUrl in modelProviders[].baseUrl.": + "У провайдера Anthropic отсутствует обязательный baseUrl в modelProviders[].baseUrl.", + "ANTHROPIC_BASE_URL environment variable not found.": + "Переменная окружения ANTHROPIC_BASE_URL не найдена.", + "Invalid auth method selected.": "Выбран недопустимый метод авторизации.", + "Failed to authenticate. Message: {{message}}": + "Не удалось авторизоваться. Сообщение: {{message}}", + "Authenticated successfully with {{authType}} credentials.": + "Успешно авторизовано с учетными данными {{authType}}.", 'Invalid QWEN_DEFAULT_AUTH_TYPE value: "{{value}}". Valid values are: {{validValues}}': 'Неверное значение QWEN_DEFAULT_AUTH_TYPE: "{{value}}". Допустимые значения: {{validValues}}', - 'OpenAI Configuration Required': 'Требуется конфигурация OpenAI', - 'Please enter your OpenAI configuration. You can get an API key from': - 'Пожалуйста, введите конфигурацию OpenAI. Вы можете получить ключ API на', - 'API Key:': 'Ключ API:', - 'Invalid credentials: {{errorMessage}}': - 'Неверные учетные данные: {{errorMessage}}', - 'Failed to validate credentials': 'Не удалось проверить учетные данные', - 'Press Enter to continue, Tab/↑↓ to navigate, Esc to cancel': - 'Enter для продолжения, Tab/↑↓ для навигации, Esc для отмены', + "OpenAI Configuration Required": "Требуется конфигурация OpenAI", + "Please enter your OpenAI configuration. You can get an API key from": + "Пожалуйста, введите конфигурацию OpenAI. Вы можете получить ключ API на", + "API Key:": "Ключ API:", + "Invalid credentials: {{errorMessage}}": "Неверные учетные данные: {{errorMessage}}", + "Failed to validate credentials": "Не удалось проверить учетные данные", + "Press Enter to continue, Tab/↑↓ to navigate, Esc to cancel": + "Enter для продолжения, Tab/↑↓ для навигации, Esc для отмены", // ============================================================================ // Диалоги - Модель // ============================================================================ - 'Select Model': 'Выбрать модель', - '(Press Esc to close)': '(Нажмите Esc для закрытия)', - 'Current (effective) configuration': 'Текущая (фактическая) конфигурация', - AuthType: 'Тип авторизации', - 'API Key': 'API-ключ', - unset: 'не задано', - '(default)': '(по умолчанию)', - '(set)': '(установлено)', - '(not set)': '(не задано)', - Modality: 'Модальность', - 'Context Window': 'Контекстное окно', - text: 'текст', - 'text-only': 'только текст', - image: 'изображение', - pdf: 'PDF', - audio: 'аудио', - video: 'видео', - 'not set': 'не задано', - none: 'нет', - unknown: 'неизвестно', + "Select Model": "Выбрать модель", + "(Press Esc to close)": "(Нажмите Esc для закрытия)", + "Current (effective) configuration": "Текущая (фактическая) конфигурация", + AuthType: "Тип авторизации", + "API Key": "API-ключ", + unset: "не задано", + "(default)": "(по умолчанию)", + "(set)": "(установлено)", + "(not set)": "(не задано)", + Modality: "Модальность", + "Context Window": "Контекстное окно", + text: "текст", + "text-only": "только текст", + image: "изображение", + pdf: "PDF", + audio: "аудио", + video: "видео", + "not set": "не задано", + none: "нет", + unknown: "неизвестно", "Failed to switch model to '{{modelId}}'.\n\n{{error}}": "Не удалось переключиться на модель '{{modelId}}'.\n\n{{error}}", - 'Qwen 3.6 Plus — efficient hybrid model with leading coding performance': - 'Qwen 3.6 Plus — эффективная гибридная модель с лидирующей производительностью в программировании', - 'The latest Qwen Vision model from Alibaba Cloud ModelStudio (version: qwen3-vl-plus-2025-09-23)': - 'Последняя модель Qwen Vision от Alibaba Cloud ModelStudio (версия: qwen3-vl-plus-2025-09-23)', + "Qwen 3.6 Plus — efficient hybrid model with leading coding performance": + "Qwen 3.6 Plus — эффективная гибридная модель с лидирующей производительностью в программировании", + "The latest Qwen Vision model from Alibaba Cloud ModelStudio (version: qwen3-vl-plus-2025-09-23)": + "Последняя модель Qwen Vision от Alibaba Cloud ModelStudio (версия: qwen3-vl-plus-2025-09-23)", // ============================================================================ // Диалоги - Разрешения // ============================================================================ - 'Manage folder trust settings': 'Управление настройками доверия к папкам', - 'Manage permission rules': 'Управление правилами разрешений', - Allow: 'Разрешить', - Ask: 'Спросить', - Deny: 'Запретить', - Workspace: 'Рабочая область', + "Manage folder trust settings": "Управление настройками доверия к папкам", + "Manage permission rules": "Управление правилами разрешений", + Allow: "Разрешить", + Ask: "Спросить", + Deny: "Запретить", + Workspace: "Рабочая область", "Qwen Code won't ask before using allowed tools.": - 'Qwen Code не будет спрашивать перед использованием разрешённых инструментов.', - 'Qwen Code will ask before using these tools.': - 'Qwen Code спросит перед использованием этих инструментов.', - 'Qwen Code is not allowed to use denied tools.': - 'Qwen Code не может использовать запрещённые инструменты.', - 'Manage trusted directories for this workspace.': - 'Управление доверенными каталогами для этой рабочей области.', - 'Any use of the {{tool}} tool': 'Любое использование инструмента {{tool}}', - "{{tool}} commands matching '{{pattern}}'": - "Команды {{tool}}, соответствующие '{{pattern}}'", - 'From user settings': 'Из пользовательских настроек', - 'From project settings': 'Из настроек проекта', - 'From session': 'Из сессии', - 'Project settings (local)': 'Настройки проекта (локальные)', - 'Saved in .qwen/settings.local.json': 'Сохранено в .qwen/settings.local.json', - 'Project settings': 'Настройки проекта', - 'Checked in at .qwen/settings.json': 'Зафиксировано в .qwen/settings.json', - 'User settings': 'Пользовательские настройки', - 'Saved in at ~/.qwen/settings.json': 'Сохранено в ~/.qwen/settings.json', - 'Add a new rule…': 'Добавить новое правило…', - 'Add {{type}} permission rule': 'Добавить правило разрешения {{type}}', - 'Permission rules are a tool name, optionally followed by a specifier in parentheses.': - 'Правила разрешений — это имя инструмента, за которым может следовать спецификатор в скобках.', - 'e.g.,': 'напр.', - or: 'или', - 'Enter permission rule…': 'Введите правило разрешения…', - 'Enter to submit · Esc to cancel': 'Enter для отправки · Esc для отмены', - 'Where should this rule be saved?': 'Где сохранить это правило?', - 'Enter to confirm · Esc to cancel': - 'Enter для подтверждения · Esc для отмены', - 'Delete {{type}} rule?': 'Удалить правило {{type}}?', - 'Are you sure you want to delete this permission rule?': - 'Вы уверены, что хотите удалить это правило разрешения?', - 'Permissions:': 'Разрешения:', - '(←/→ or tab to cycle)': '(←/→ или Tab для переключения)', - 'Press ↑↓ to navigate · Enter to select · Type to search · Esc to cancel': - '↑↓ навигация · Enter выбор · Ввод для поиска · Esc отмена', - 'Search…': 'Поиск…', - 'Use /trust to manage folder trust settings for this workspace.': - 'Используйте /trust для управления настройками доверия к папкам этой рабочей области.', + "Qwen Code не будет спрашивать перед использованием разрешённых инструментов.", + "Qwen Code will ask before using these tools.": + "Qwen Code спросит перед использованием этих инструментов.", + "Qwen Code is not allowed to use denied tools.": + "Qwen Code не может использовать запрещённые инструменты.", + "Manage trusted directories for this workspace.": + "Управление доверенными каталогами для этой рабочей области.", + "Any use of the {{tool}} tool": "Любое использование инструмента {{tool}}", + "{{tool}} commands matching '{{pattern}}'": "Команды {{tool}}, соответствующие '{{pattern}}'", + "From user settings": "Из пользовательских настроек", + "From project settings": "Из настроек проекта", + "From session": "Из сессии", + "Project settings (local)": "Настройки проекта (локальные)", + "Saved in .qwen/settings.local.json": "Сохранено в .qwen/settings.local.json", + "Project settings": "Настройки проекта", + "Checked in at .qwen/settings.json": "Зафиксировано в .qwen/settings.json", + "User settings": "Пользовательские настройки", + "Saved in at ~/.qwen/settings.json": "Сохранено в ~/.qwen/settings.json", + "Add a new rule…": "Добавить новое правило…", + "Add {{type}} permission rule": "Добавить правило разрешения {{type}}", + "Permission rules are a tool name, optionally followed by a specifier in parentheses.": + "Правила разрешений — это имя инструмента, за которым может следовать спецификатор в скобках.", + "e.g.,": "напр.", + or: "или", + "Enter permission rule…": "Введите правило разрешения…", + "Enter to submit · Esc to cancel": "Enter для отправки · Esc для отмены", + "Where should this rule be saved?": "Где сохранить это правило?", + "Enter to confirm · Esc to cancel": "Enter для подтверждения · Esc для отмены", + "Delete {{type}} rule?": "Удалить правило {{type}}?", + "Are you sure you want to delete this permission rule?": + "Вы уверены, что хотите удалить это правило разрешения?", + "Permissions:": "Разрешения:", + "(←/→ or tab to cycle)": "(←/→ или Tab для переключения)", + "Press ↑↓ to navigate · Enter to select · Type to search · Esc to cancel": + "↑↓ навигация · Enter выбор · Ввод для поиска · Esc отмена", + "Search…": "Поиск…", + "Use /trust to manage folder trust settings for this workspace.": + "Используйте /trust для управления настройками доверия к папкам этой рабочей области.", // Workspace directory management - 'Add directory…': 'Добавить каталог…', - 'Add directory to workspace': 'Добавить каталог в рабочую область', - 'Qwen Code can read files in the workspace, and make edits when auto-accept edits is on.': - 'Qwen Code может читать файлы в рабочей области и вносить правки, когда автоприём правок включён.', - 'Qwen Code will be able to read files in this directory and make edits when auto-accept edits is on.': - 'Qwen Code сможет читать файлы в этом каталоге и вносить правки, когда автоприём правок включён.', - 'Enter the path to the directory:': 'Введите путь к каталогу:', - 'Enter directory path…': 'Введите путь к каталогу…', - 'Tab to complete · Enter to add · Esc to cancel': - 'Tab для завершения · Enter для добавления · Esc для отмены', - 'Remove directory?': 'Удалить каталог?', - 'Are you sure you want to remove this directory from the workspace?': - 'Вы уверены, что хотите удалить этот каталог из рабочей области?', - ' (Original working directory)': ' (Исходный рабочий каталог)', - ' (from settings)': ' (из настроек)', - 'Directory does not exist.': 'Каталог не существует.', - 'Path is not a directory.': 'Путь не является каталогом.', - 'This directory is already in the workspace.': - 'Этот каталог уже есть в рабочей области.', - 'Already covered by existing directory: {{dir}}': - 'Уже охвачен существующим каталогом: {{dir}}', + "Add directory…": "Добавить каталог…", + "Add directory to workspace": "Добавить каталог в рабочую область", + "Qwen Code can read files in the workspace, and make edits when auto-accept edits is on.": + "Qwen Code может читать файлы в рабочей области и вносить правки, когда автоприём правок включён.", + "Qwen Code will be able to read files in this directory and make edits when auto-accept edits is on.": + "Qwen Code сможет читать файлы в этом каталоге и вносить правки, когда автоприём правок включён.", + "Enter the path to the directory:": "Введите путь к каталогу:", + "Enter directory path…": "Введите путь к каталогу…", + "Tab to complete · Enter to add · Esc to cancel": + "Tab для завершения · Enter для добавления · Esc для отмены", + "Remove directory?": "Удалить каталог?", + "Are you sure you want to remove this directory from the workspace?": + "Вы уверены, что хотите удалить этот каталог из рабочей области?", + " (Original working directory)": " (Исходный рабочий каталог)", + " (from settings)": " (из настроек)", + "Directory does not exist.": "Каталог не существует.", + "Path is not a directory.": "Путь не является каталогом.", + "This directory is already in the workspace.": "Этот каталог уже есть в рабочей области.", + "Already covered by existing directory: {{dir}}": "Уже охвачен существующим каталогом: {{dir}}", // ============================================================================ // Строка состояния // ============================================================================ - 'Using:': 'Используется:', - '{{count}} open file': '{{count}} открытый файл', - '{{count}} open files': '{{count}} открытых файла(ов)', - '(ctrl+g to view)': '(ctrl+g для просмотра)', - '{{count}} {{name}} file': '{{count}} файл {{name}}', - '{{count}} {{name}} files': '{{count}} файла(ов) {{name}}', - '{{count}} MCP server': '{{count}} MCP-сервер', - '{{count}} MCP servers': '{{count}} MCP-сервера(ов)', - '{{count}} Blocked': '{{count}} заблокирован(о)', - '(ctrl+t to view)': '(ctrl+t для просмотра)', - '(ctrl+t to toggle)': '(ctrl+t для переключения)', - 'Press Ctrl+C again to exit.': 'Нажмите Ctrl+C снова для выхода.', - 'Press Ctrl+D again to exit.': 'Нажмите Ctrl+D снова для выхода.', - 'Press Esc again to clear.': 'Нажмите Esc снова для очистки.', + "Using:": "Используется:", + "{{count}} open file": "{{count}} открытый файл", + "{{count}} open files": "{{count}} открытых файла(ов)", + "(ctrl+g to view)": "(ctrl+g для просмотра)", + "{{count}} {{name}} file": "{{count}} файл {{name}}", + "{{count}} {{name}} files": "{{count}} файла(ов) {{name}}", + "{{count}} MCP server": "{{count}} MCP-сервер", + "{{count}} MCP servers": "{{count}} MCP-сервера(ов)", + "{{count}} Blocked": "{{count}} заблокирован(о)", + "(ctrl+t to view)": "(ctrl+t для просмотра)", + "(ctrl+t to toggle)": "(ctrl+t для переключения)", + "Press Ctrl+C again to exit.": "Нажмите Ctrl+C снова для выхода.", + "Press Ctrl+D again to exit.": "Нажмите Ctrl+D снова для выхода.", + "Press Esc again to clear.": "Нажмите Esc снова для очистки.", // ============================================================================ // Статус MCP // ============================================================================ - 'No MCP servers configured.': 'Не настроено MCP-серверов.', - '⏳ MCP servers are starting up ({{count}} initializing)...': - '⏳ MCP-серверы запускаются ({{count}} инициализируется)...', - 'Note: First startup may take longer. Tool availability will update automatically.': - 'Примечание: Первый запуск может занять больше времени. Доступность инструментов обновится автоматически.', - 'Configured MCP servers:': 'Настроенные MCP-серверы:', - Ready: 'Готов', - 'Starting... (first startup may take longer)': - 'Запуск... (первый запуск может занять больше времени)', - Disconnected: 'Отключен', - '{{count}} tool': '{{count}} инструмент', - '{{count}} tools': '{{count}} инструмента(ов)', - '{{count}} prompt': '{{count}} промпт', - '{{count}} prompts': '{{count}} промпта(ов)', - '(from {{extensionName}})': '(от {{extensionName}})', - OAuth: 'OAuth', - 'OAuth expired': 'OAuth истек', - 'OAuth not authenticated': 'OAuth не авторизован', - 'tools and prompts will appear when ready': - 'инструменты и промпты появятся, когда будут готовы', - '{{count}} tools cached': '{{count}} инструмента(ов) в кэше', - 'Tools:': 'Инструменты:', - 'Parameters:': 'Параметры:', - 'Prompts:': 'Промпты:', - Blocked: 'Заблокировано', - '💡 Tips:': '💡 Подсказки:', - Use: 'Используйте', - 'to show server and tool descriptions': - 'для показа описаний сервера и инструментов', - 'to show tool parameter schemas': 'для показа схем параметров инструментов', - 'to hide descriptions': 'для скрытия описаний', - 'to authenticate with OAuth-enabled servers': - 'для авторизации на серверах с поддержкой OAuth', - Press: 'Нажмите', - 'to toggle tool descriptions on/off': - 'для переключения описаний инструментов', + "No MCP servers configured.": "Не настроено MCP-серверов.", + "⏳ MCP servers are starting up ({{count}} initializing)...": + "⏳ MCP-серверы запускаются ({{count}} инициализируется)...", + "Note: First startup may take longer. Tool availability will update automatically.": + "Примечание: Первый запуск может занять больше времени. Доступность инструментов обновится автоматически.", + "Configured MCP servers:": "Настроенные MCP-серверы:", + Ready: "Готов", + "Starting... (first startup may take longer)": + "Запуск... (первый запуск может занять больше времени)", + Disconnected: "Отключен", + "{{count}} tool": "{{count}} инструмент", + "{{count}} tools": "{{count}} инструмента(ов)", + "{{count}} prompt": "{{count}} промпт", + "{{count}} prompts": "{{count}} промпта(ов)", + "(from {{extensionName}})": "(от {{extensionName}})", + OAuth: "OAuth", + "OAuth expired": "OAuth истек", + "OAuth not authenticated": "OAuth не авторизован", + "tools and prompts will appear when ready": "инструменты и промпты появятся, когда будут готовы", + "{{count}} tools cached": "{{count}} инструмента(ов) в кэше", + "Tools:": "Инструменты:", + "Parameters:": "Параметры:", + "Prompts:": "Промпты:", + Blocked: "Заблокировано", + "💡 Tips:": "💡 Подсказки:", + Use: "Используйте", + "to show server and tool descriptions": "для показа описаний сервера и инструментов", + "to show tool parameter schemas": "для показа схем параметров инструментов", + "to hide descriptions": "для скрытия описаний", + "to authenticate with OAuth-enabled servers": "для авторизации на серверах с поддержкой OAuth", + Press: "Нажмите", + "to toggle tool descriptions on/off": "для переключения описаний инструментов", "Starting OAuth authentication for MCP server '{{name}}'...": "Начало авторизации OAuth для MCP-сервера '{{name}}'...", - 'Restarting MCP servers...': 'Перезапуск MCP-серверов...', + "Restarting MCP servers...": "Перезапуск MCP-серверов...", // ============================================================================ // Подсказки при запуске // ============================================================================ - 'Tips for getting started:': 'Подсказки для начала работы:', - '1. Ask questions, edit files, or run commands.': - '1. Задавайте вопросы, редактируйте файлы или выполняйте команды.', - '2. Be specific for the best results.': - '2. Будьте конкретны для лучших результатов.', - 'files to customize your interactions with Qwen Code.': - 'файлы для настройки взаимодействия с Qwen Code.', - 'for more information.': 'для получения дополнительной информации.', + "Tips for getting started:": "Подсказки для начала работы:", + "1. Ask questions, edit files, or run commands.": + "1. Задавайте вопросы, редактируйте файлы или выполняйте команды.", + "2. Be specific for the best results.": "2. Будьте конкретны для лучших результатов.", + "files to customize your interactions with Qwen Code.": + "файлы для настройки взаимодействия с Qwen Code.", + "for more information.": "для получения дополнительной информации.", // ============================================================================ // Экран выхода / Статистика // ============================================================================ - 'Agent powering down. Goodbye!': 'Агент завершает работу. До свидания!', - 'To continue this session, run': 'Для продолжения этой сессии, выполните', - 'Interaction Summary': 'Сводка взаимодействия', - 'Session ID:': 'ID сессии:', - 'Tool Calls:': 'Вызовы инструментов:', - 'Success Rate:': 'Процент успеха:', - 'User Agreement:': 'Согласие пользователя:', - reviewed: 'проверено', - 'Code Changes:': 'Изменения кода:', - Performance: 'Производительность', - 'Wall Time:': 'Общее время:', - 'Agent Active:': 'Активность агента:', - 'API Time:': 'Время API:', - 'Tool Time:': 'Время инструментов:', - 'Session Stats': 'Статистика сессии', - 'Model Usage': 'Использование модели', - Reqs: 'Запросов', - 'Input Tokens': 'Входных токенов', - 'Output Tokens': 'Выходных токенов', - 'Savings Highlight:': 'Экономия:', - 'of input tokens were served from the cache, reducing costs.': - 'входных токенов обслужено из кэша, снижая затраты.', - 'Tip: For a full token breakdown, run `/stats model`.': - 'Подсказка: Для полной разбивки токенов выполните `/stats model`.', - 'Model Stats For Nerds': 'Статистика модели для гиков', - 'Tool Stats For Nerds': 'Статистика инструментов для гиков', - Metric: 'Метрика', - API: 'API', - Requests: 'Запросы', - Errors: 'Ошибки', - 'Avg Latency': 'Средняя задержка', - Tokens: 'Токены', - Total: 'Всего', - Prompt: 'Промпт', - Cached: 'Кэшировано', - Thoughts: 'Размышления', - Tool: 'Инструмент', - Output: 'Вывод', - 'No API calls have been made in this session.': - 'В этой сессии не было вызовов API.', - 'Tool Name': 'Имя инструмента', - Calls: 'Вызовы', - 'Success Rate': 'Процент успеха', - 'Avg Duration': 'Средняя длительность', - 'User Decision Summary': 'Сводка решений пользователя', - 'Total Reviewed Suggestions:': 'Всего проверено предложений:', - ' » Accepted:': ' » Принято:', - ' » Rejected:': ' » Отклонено:', - ' » Modified:': ' » Изменено:', - ' Overall Agreement Rate:': ' Общий процент согласия:', - 'No tool calls have been made in this session.': - 'В этой сессии не было вызовов инструментов.', - 'Session start time is unavailable, cannot calculate stats.': - 'Время начала сессии недоступно, невозможно рассчитать статистику.', + "Agent powering down. Goodbye!": "Агент завершает работу. До свидания!", + "To continue this session, run": "Для продолжения этой сессии, выполните", + "Interaction Summary": "Сводка взаимодействия", + "Session ID:": "ID сессии:", + "Tool Calls:": "Вызовы инструментов:", + "Success Rate:": "Процент успеха:", + "User Agreement:": "Согласие пользователя:", + reviewed: "проверено", + "Code Changes:": "Изменения кода:", + Performance: "Производительность", + "Wall Time:": "Общее время:", + "Agent Active:": "Активность агента:", + "API Time:": "Время API:", + "Tool Time:": "Время инструментов:", + "Session Stats": "Статистика сессии", + "Model Usage": "Использование модели", + Reqs: "Запросов", + "Input Tokens": "Входных токенов", + "Output Tokens": "Выходных токенов", + "Savings Highlight:": "Экономия:", + "of input tokens were served from the cache, reducing costs.": + "входных токенов обслужено из кэша, снижая затраты.", + "Tip: For a full token breakdown, run `/stats model`.": + "Подсказка: Для полной разбивки токенов выполните `/stats model`.", + "Model Stats For Nerds": "Статистика модели для гиков", + "Tool Stats For Nerds": "Статистика инструментов для гиков", + Metric: "Метрика", + API: "API", + Requests: "Запросы", + Errors: "Ошибки", + "Avg Latency": "Средняя задержка", + Tokens: "Токены", + Total: "Всего", + Prompt: "Промпт", + Cached: "Кэшировано", + Thoughts: "Размышления", + Tool: "Инструмент", + Output: "Вывод", + "No API calls have been made in this session.": "В этой сессии не было вызовов API.", + "Tool Name": "Имя инструмента", + Calls: "Вызовы", + "Success Rate": "Процент успеха", + "Avg Duration": "Средняя длительность", + "User Decision Summary": "Сводка решений пользователя", + "Total Reviewed Suggestions:": "Всего проверено предложений:", + " » Accepted:": " » Принято:", + " » Rejected:": " » Отклонено:", + " » Modified:": " » Изменено:", + " Overall Agreement Rate:": " Общий процент согласия:", + "No tool calls have been made in this session.": "В этой сессии не было вызовов инструментов.", + "Session start time is unavailable, cannot calculate stats.": + "Время начала сессии недоступно, невозможно рассчитать статистику.", // ============================================================================ // Command Format Migration // ============================================================================ - 'Command Format Migration': 'Миграция формата команд', - 'Found {{count}} TOML command file:': 'Найден {{count}} файл команд TOML:', - 'Found {{count}} TOML command files:': - 'Найдено {{count}} файлов команд TOML:', - '... and {{count}} more': '... и ещё {{count}}', - 'The TOML format is deprecated. Would you like to migrate them to Markdown format?': - 'Формат TOML устарел. Хотите перенести их в формат Markdown?', - '(Backups will be created and original files will be preserved)': - '(Будут созданы резервные копии, исходные файлы будут сохранены)', + "Command Format Migration": "Миграция формата команд", + "Found {{count}} TOML command file:": "Найден {{count}} файл команд TOML:", + "Found {{count}} TOML command files:": "Найдено {{count}} файлов команд TOML:", + "... and {{count}} more": "... и ещё {{count}}", + "The TOML format is deprecated. Would you like to migrate them to Markdown format?": + "Формат TOML устарел. Хотите перенести их в формат Markdown?", + "(Backups will be created and original files will be preserved)": + "(Будут созданы резервные копии, исходные файлы будут сохранены)", // ============================================================================ // Loading Phrases // ============================================================================ - 'Waiting for user confirmation...': - 'Ожидание подтверждения от пользователя...', - '(esc to cancel, {{time}})': '(esc для отмены, {{time}})', + "Waiting for user confirmation...": "Ожидание подтверждения от пользователя...", + "(esc to cancel, {{time}})": "(esc для отмены, {{time}})", // ============================================================================ @@ -1490,464 +1385,431 @@ export default { // Loading Phrases // ============================================================================ WITTY_LOADING_PHRASES: [ - 'Мне повезёт!', - 'Доставляем крутизну... ', - 'Рисуем засечки на буквах...', - 'Пробираемся через слизевиков..', - 'Советуемся с цифровыми духами...', - 'Сглаживание сплайнов...', - 'Разогреваем ИИ-хомячков...', - 'Спрашиваем волшебную ракушку...', - 'Генерируем остроумный ответ...', - 'Полируем алгоритмы...', - 'Не торопите совершенство (или мой код)...', - 'Завариваем свежие байты...', - 'Пересчитываем электроны...', - 'Задействуем когнитивные процессоры...', - 'Ищем синтаксические ошибки во вселенной...', - 'Секундочку, оптимизируем юмор...', - 'Перетасовываем панчлайны...', - 'Распутаваем нейросети...', - 'Компилируем гениальность...', - 'Загружаем yumor.exe...', - 'Призываем облако мудрости...', - 'Готовим остроумный ответ...', - 'Секунду, идёт отладка реальности...', - 'Запутываем варианты...', - 'Настраиваем космические частоты...', - 'Создаем ответ, достойный вашего терпения...', - 'Компилируем единички и нолики...', - 'Разрешаем зависимости... и экзистенциальные кризисы...', - 'Дефрагментация памяти... и оперативной, и личной...', - 'Перезагрузка модуля юмора...', - 'Кэшируем самое важное (в основном мемы с котиками)...', - 'Оптимизация для безумной скорости', - 'Меняем биты... только байтам не говорите...', - 'Сборка мусора... скоро вернусь...', - 'Сборка интернетов...', - 'Превращаем кофе в код...', - 'Обновляем синтаксис реальности...', - 'Переподключаем синапсы...', - 'Ищем лишнюю точку с запятой...', - 'Смазываем шестерёнки машины...', - 'Разогреваем серверы...', - 'Калибруем потоковый накопитель...', - 'Включаем двигатель невероятности...', - 'Направляем Силу...', - 'Выравниваем звёзды для оптимального ответа...', - 'Так скажем мы все...', - 'Загрузка следующей великой идеи...', - 'Минутку, я в потоке...', - 'Готовлюсь ослепить вас гениальностью...', - 'Секунду, полирую остроумие...', - 'Держитесь, создаю шедевр...', - 'Мигом, отлаживаю вселенную...', - 'Момент, выравниваю пиксели...', - 'Секунду, оптимизирую юмор...', - 'Момент, настраиваю алгоритмы...', - 'Варп-прыжок активирован...', - 'Добываем кристаллы дилития...', - 'Без паники...', - 'Следуем за белым кроликом...', - 'Истина где-то здесь... внутри...', - 'Продуваем картридж...', - 'Загрузка... Сделай бочку!', - 'Ждем респауна...', - 'Делаем Дугу Кесселя менее чем за 12 парсеков...', - 'Тортик — не ложь, он просто ещё грузится...', - 'Возимся с экраном создания персонажа...', - 'Минутку, ищу подходящий мем...', + "Мне повезёт!", + "Доставляем крутизну... ", + "Рисуем засечки на буквах...", + "Пробираемся через слизевиков..", + "Советуемся с цифровыми духами...", + "Сглаживание сплайнов...", + "Разогреваем ИИ-хомячков...", + "Спрашиваем волшебную ракушку...", + "Генерируем остроумный ответ...", + "Полируем алгоритмы...", + "Не торопите совершенство (или мой код)...", + "Завариваем свежие байты...", + "Пересчитываем электроны...", + "Задействуем когнитивные процессоры...", + "Ищем синтаксические ошибки во вселенной...", + "Секундочку, оптимизируем юмор...", + "Перетасовываем панчлайны...", + "Распутаваем нейросети...", + "Компилируем гениальность...", + "Загружаем yumor.exe...", + "Призываем облако мудрости...", + "Готовим остроумный ответ...", + "Секунду, идёт отладка реальности...", + "Запутываем варианты...", + "Настраиваем космические частоты...", + "Создаем ответ, достойный вашего терпения...", + "Компилируем единички и нолики...", + "Разрешаем зависимости... и экзистенциальные кризисы...", + "Дефрагментация памяти... и оперативной, и личной...", + "Перезагрузка модуля юмора...", + "Кэшируем самое важное (в основном мемы с котиками)...", + "Оптимизация для безумной скорости", + "Меняем биты... только байтам не говорите...", + "Сборка мусора... скоро вернусь...", + "Сборка интернетов...", + "Превращаем кофе в код...", + "Обновляем синтаксис реальности...", + "Переподключаем синапсы...", + "Ищем лишнюю точку с запятой...", + "Смазываем шестерёнки машины...", + "Разогреваем серверы...", + "Калибруем потоковый накопитель...", + "Включаем двигатель невероятности...", + "Направляем Силу...", + "Выравниваем звёзды для оптимального ответа...", + "Так скажем мы все...", + "Загрузка следующей великой идеи...", + "Минутку, я в потоке...", + "Готовлюсь ослепить вас гениальностью...", + "Секунду, полирую остроумие...", + "Держитесь, создаю шедевр...", + "Мигом, отлаживаю вселенную...", + "Момент, выравниваю пиксели...", + "Секунду, оптимизирую юмор...", + "Момент, настраиваю алгоритмы...", + "Варп-прыжок активирован...", + "Добываем кристаллы дилития...", + "Без паники...", + "Следуем за белым кроликом...", + "Истина где-то здесь... внутри...", + "Продуваем картридж...", + "Загрузка... Сделай бочку!", + "Ждем респауна...", + "Делаем Дугу Кесселя менее чем за 12 парсеков...", + "Тортик — не ложь, он просто ещё грузится...", + "Возимся с экраном создания персонажа...", + "Минутку, ищу подходящий мем...", "Нажимаем 'A' для продолжения...", - 'Пасём цифровых котов...', - 'Полируем пиксели...', - 'Ищем подходящий каламбур для экрана загрузки...', - 'Отвлекаем вас этой остроумной фразой...', - 'Почти готово... вроде...', - 'Наши хомячки работают изо всех сил...', - 'Гладим Облачко по голове...', - 'Гладим кота...', - 'Рикроллим начальника...', - 'Never gonna give you up, never gonna let you down...', - 'Лабаем бас-гитару...', - 'Пробуем снузберри на вкус...', - 'Иду до конца, иду на скорость...', - 'Is this the real life? Is this just fantasy?...', - 'У меня хорошее предчувствие...', - 'Дразним медведя... (Не лезь...)', - 'Изучаем свежие мемы...', - 'Думаем, как сделать это остроумнее...', - 'Хмм... дайте подумать...', - 'Как называется бумеранг, который не возвращается? Палка...', - 'Почему компьютер простудился? Потому что оставил окна открытыми...', - 'Почему программисты не любят гулять на улице? Там среда не настроена...', - 'Почему программисты предпочитают тёмную тему? Потому что в темноте не видно багов...', - 'Почему разработчик разорился? Потому что потратил весь свой кэш...', - 'Что можно делать со сломанным карандашом? Ничего — он тупой...', - 'Провожу настройку методом тыка...', - 'Ищем, какой стороной вставлять флешку...', - 'Следим, чтобы волшебный дым не вышел из проводов...', - 'Пытаемся выйти из Vim...', - 'Раскручиваем колесо для хомяка...', - 'Это не баг, а фича...', - 'Поехали!', - 'Я вернусь... с ответом.', - 'Мой другой процесс — это ТАРДИС...', - 'Общаемся с духом машины...', - 'Даем мыслям замариноваться...', - 'Только что вспомнил, куда положил ключи...', - 'Размышляю над сферой...', - 'Я видел такое, что вам, людям, и не снилось... пользователя, читающего эти сообщения.', - 'Инициируем задумчивый взгляд...', - 'Что сервер заказывает в баре? Пинг-коладу.', - 'Почему Java-разработчики не убираются дома? Они ждут сборщик мусора...', - 'Заряжаем лазер... пиу-пиу!', - 'Делим на ноль... шучу!', - 'Ищу взрослых для присмот... в смысле, обрабатываю.', - 'Делаем бип-буп.', - 'Буферизация... даже ИИ нужно время подумать.', - 'Запутываем квантовые частицы для быстрого ответа...', - 'Полируем хром... на алгоритмах.', - 'Вы ещё не развлеклись?! Разве вы не за этим сюда пришли?!', - 'Призываем гремлинов кода... для помощи, конечно же.', - 'Ждем, пока закончится звук dial-up модема...', - 'Перекалибровка юморометра.', - 'Мой другой экран загрузки ещё смешнее.', - 'Кажется, где-то по клавиатуре гуляет кот...', - 'Улучшаем... Ещё улучшаем... Всё ещё грузится.', - 'Это не баг, это фича... экрана загрузки.', - 'Пробовали выключить и включить снова? (Экран загрузки, не меня!)', - 'Нужно построить больше пилонов...', + "Пасём цифровых котов...", + "Полируем пиксели...", + "Ищем подходящий каламбур для экрана загрузки...", + "Отвлекаем вас этой остроумной фразой...", + "Почти готово... вроде...", + "Наши хомячки работают изо всех сил...", + "Гладим Облачко по голове...", + "Гладим кота...", + "Рикроллим начальника...", + "Never gonna give you up, never gonna let you down...", + "Лабаем бас-гитару...", + "Пробуем снузберри на вкус...", + "Иду до конца, иду на скорость...", + "Is this the real life? Is this just fantasy?...", + "У меня хорошее предчувствие...", + "Дразним медведя... (Не лезь...)", + "Изучаем свежие мемы...", + "Думаем, как сделать это остроумнее...", + "Хмм... дайте подумать...", + "Как называется бумеранг, который не возвращается? Палка...", + "Почему компьютер простудился? Потому что оставил окна открытыми...", + "Почему программисты не любят гулять на улице? Там среда не настроена...", + "Почему программисты предпочитают тёмную тему? Потому что в темноте не видно багов...", + "Почему разработчик разорился? Потому что потратил весь свой кэш...", + "Что можно делать со сломанным карандашом? Ничего — он тупой...", + "Провожу настройку методом тыка...", + "Ищем, какой стороной вставлять флешку...", + "Следим, чтобы волшебный дым не вышел из проводов...", + "Пытаемся выйти из Vim...", + "Раскручиваем колесо для хомяка...", + "Это не баг, а фича...", + "Поехали!", + "Я вернусь... с ответом.", + "Мой другой процесс — это ТАРДИС...", + "Общаемся с духом машины...", + "Даем мыслям замариноваться...", + "Только что вспомнил, куда положил ключи...", + "Размышляю над сферой...", + "Я видел такое, что вам, людям, и не снилось... пользователя, читающего эти сообщения.", + "Инициируем задумчивый взгляд...", + "Что сервер заказывает в баре? Пинг-коладу.", + "Почему Java-разработчики не убираются дома? Они ждут сборщик мусора...", + "Заряжаем лазер... пиу-пиу!", + "Делим на ноль... шучу!", + "Ищу взрослых для присмот... в смысле, обрабатываю.", + "Делаем бип-буп.", + "Буферизация... даже ИИ нужно время подумать.", + "Запутываем квантовые частицы для быстрого ответа...", + "Полируем хром... на алгоритмах.", + "Вы ещё не развлеклись?! Разве вы не за этим сюда пришли?!", + "Призываем гремлинов кода... для помощи, конечно же.", + "Ждем, пока закончится звук dial-up модема...", + "Перекалибровка юморометра.", + "Мой другой экран загрузки ещё смешнее.", + "Кажется, где-то по клавиатуре гуляет кот...", + "Улучшаем... Ещё улучшаем... Всё ещё грузится.", + "Это не баг, это фича... экрана загрузки.", + "Пробовали выключить и включить снова? (Экран загрузки, не меня!)", + "Нужно построить больше пилонов...", ], // ============================================================================ // Extension Settings Input // ============================================================================ - 'Enter value...': 'Введите значение...', - 'Enter sensitive value...': 'Введите секретное значение...', - 'Press Enter to submit, Escape to cancel': - 'Нажмите Enter для отправки, Escape для отмены', + "Enter value...": "Введите значение...", + "Enter sensitive value...": "Введите секретное значение...", + "Press Enter to submit, Escape to cancel": "Нажмите Enter для отправки, Escape для отмены", // ============================================================================ // Command Migration Tool // ============================================================================ - 'Markdown file already exists: {{filename}}': - 'Markdown-файл уже существует: {{filename}}', - 'TOML Command Format Deprecation Notice': - 'Уведомление об устаревании формата TOML', - 'Found {{count}} command file(s) in TOML format:': - 'Найдено {{count}} файл(ов) команд в формате TOML:', - 'The TOML format for commands is being deprecated in favor of Markdown format.': - 'Формат TOML для команд устаревает в пользу формата Markdown.', - 'Markdown format is more readable and easier to edit.': - 'Формат Markdown более читаемый и простой для редактирования.', - 'You can migrate these files automatically using:': - 'Вы можете автоматически мигрировать эти файлы с помощью:', - 'Or manually convert each file:': 'Или вручную конвертировать каждый файл:', - 'TOML: prompt = "..." / description = "..."': - 'TOML: prompt = "..." / description = "..."', - 'Markdown: YAML frontmatter + content': - 'Markdown: YAML frontmatter + содержимое', - 'The migration tool will:': 'Инструмент миграции:', - 'Convert TOML files to Markdown': 'Конвертирует TOML-файлы в Markdown', - 'Create backups of original files': 'Создаёт резервные копии исходных файлов', - 'Preserve all command functionality': 'Сохраняет всю функциональность команд', - 'TOML format will continue to work for now, but migration is recommended.': - 'Формат TOML пока продолжит работать, но миграция рекомендуется.', + "Markdown file already exists: {{filename}}": "Markdown-файл уже существует: {{filename}}", + "TOML Command Format Deprecation Notice": "Уведомление об устаревании формата TOML", + "Found {{count}} command file(s) in TOML format:": + "Найдено {{count}} файл(ов) команд в формате TOML:", + "The TOML format for commands is being deprecated in favor of Markdown format.": + "Формат TOML для команд устаревает в пользу формата Markdown.", + "Markdown format is more readable and easier to edit.": + "Формат Markdown более читаемый и простой для редактирования.", + "You can migrate these files automatically using:": + "Вы можете автоматически мигрировать эти файлы с помощью:", + "Or manually convert each file:": "Или вручную конвертировать каждый файл:", + 'TOML: prompt = "..." / description = "..."': 'TOML: prompt = "..." / description = "..."', + "Markdown: YAML frontmatter + content": "Markdown: YAML frontmatter + содержимое", + "The migration tool will:": "Инструмент миграции:", + "Convert TOML files to Markdown": "Конвертирует TOML-файлы в Markdown", + "Create backups of original files": "Создаёт резервные копии исходных файлов", + "Preserve all command functionality": "Сохраняет всю функциональность команд", + "TOML format will continue to work for now, but migration is recommended.": + "Формат TOML пока продолжит работать, но миграция рекомендуется.", // ============================================================================ // Extensions - Explore Command // ============================================================================ - 'Open extensions page in your browser': - 'Открыть страницу расширений в браузере', - 'Unknown extensions source: {{source}}.': - 'Неизвестный источник расширений: {{source}}.', - 'Would open extensions page in your browser: {{url}} (skipped in test environment)': - 'Страница расширений была бы открыта в браузере: {{url}} (пропущено в тестовой среде)', - 'View available extensions at {{url}}': - 'Посмотреть доступные расширения на {{url}}', - 'Opening extensions page in your browser: {{url}}': - 'Открываем страницу расширений в браузере: {{url}}', - 'Failed to open browser. Check out the extensions gallery at {{url}}': - 'Не удалось открыть браузер. Посетите галерею расширений по адресу {{url}}', - 'Use /compress when the conversation gets long to summarize history and free up context.': - 'Используйте /compress, когда разговор становится длинным, чтобы подвести итог и освободить контекст.', - 'Start a fresh idea with /clear or /new; the previous session stays available in history.': - 'Начните новую идею с /clear или /new; предыдущая сессия останется в истории.', - 'Use /bug to submit issues to the maintainers when something goes off.': - 'Используйте /bug, чтобы сообщить о проблемах разработчикам.', - 'Switch auth type quickly with /auth.': - 'Быстро переключите тип аутентификации с помощью /auth.', - 'You can run any shell commands from Qwen Code using ! (e.g. !ls).': - 'Вы можете выполнять любые shell-команды в Qwen Code с помощью ! (например, !ls).', - 'Type / to open the command popup; Tab autocompletes slash commands and saved prompts.': - 'Введите /, чтобы открыть меню команд; Tab автодополняет слэш-команды и сохранённые промпты.', - 'You can resume a previous conversation by running qwen --continue or qwen --resume.': - 'Вы можете продолжить предыдущий разговор, запустив qwen --continue или qwen --resume.', - 'You can switch permission mode quickly with Shift+Tab or /approval-mode.': - 'Вы можете быстро переключать режим разрешений с помощью Shift+Tab или /approval-mode.', - 'You can switch permission mode quickly with Tab or /approval-mode.': - 'Вы можете быстро переключать режим разрешений с помощью Tab или /approval-mode.', - 'Try /insight to generate personalized insights from your chat history.': - 'Попробуйте /insight, чтобы получить персонализированные выводы из истории чатов.', + "Open extensions page in your browser": "Открыть страницу расширений в браузере", + "Unknown extensions source: {{source}}.": "Неизвестный источник расширений: {{source}}.", + "Would open extensions page in your browser: {{url}} (skipped in test environment)": + "Страница расширений была бы открыта в браузере: {{url}} (пропущено в тестовой среде)", + "View available extensions at {{url}}": "Посмотреть доступные расширения на {{url}}", + "Opening extensions page in your browser: {{url}}": + "Открываем страницу расширений в браузере: {{url}}", + "Failed to open browser. Check out the extensions gallery at {{url}}": + "Не удалось открыть браузер. Посетите галерею расширений по адресу {{url}}", + "Use /compress when the conversation gets long to summarize history and free up context.": + "Используйте /compress, когда разговор становится длинным, чтобы подвести итог и освободить контекст.", + "Start a fresh idea with /clear or /new; the previous session stays available in history.": + "Начните новую идею с /clear или /new; предыдущая сессия останется в истории.", + "Use /bug to submit issues to the maintainers when something goes off.": + "Используйте /bug, чтобы сообщить о проблемах разработчикам.", + "Switch auth type quickly with /auth.": "Быстро переключите тип аутентификации с помощью /auth.", + "You can run any shell commands from Qwen Code using ! (e.g. !ls).": + "Вы можете выполнять любые shell-команды в Qwen Code с помощью ! (например, !ls).", + "Type / to open the command popup; Tab autocompletes slash commands and saved prompts.": + "Введите /, чтобы открыть меню команд; Tab автодополняет слэш-команды и сохранённые промпты.", + "You can resume a previous conversation by running qwen --continue or qwen --resume.": + "Вы можете продолжить предыдущий разговор, запустив qwen --continue или qwen --resume.", + "You can switch permission mode quickly with Shift+Tab or /approval-mode.": + "Вы можете быстро переключать режим разрешений с помощью Shift+Tab или /approval-mode.", + "You can switch permission mode quickly with Tab or /approval-mode.": + "Вы можете быстро переключать режим разрешений с помощью Tab или /approval-mode.", + "Try /insight to generate personalized insights from your chat history.": + "Попробуйте /insight, чтобы получить персонализированные выводы из истории чатов.", // ============================================================================ // Custom API Key Configuration // ============================================================================ - 'You can configure your API key and models in settings.json': - 'Вы можете настроить API-ключ и модели в settings.json', - 'Refer to the documentation for setup instructions': - 'Инструкции по настройке см. в документации', + "You can configure your API key and models in settings.json": + "Вы можете настроить API-ключ и модели в settings.json", + "Refer to the documentation for setup instructions": "Инструкции по настройке см. в документации", // ============================================================================ // Coding Plan Authentication // ============================================================================ - 'API key cannot be empty.': 'API-ключ не может быть пустым.', - 'You can get your Coding Plan API key here': - 'Вы можете получить API-ключ Coding Plan здесь', - 'New model configurations are available for Alibaba Cloud Coding Plan. Update now?': - 'Доступны новые конфигурации моделей для Alibaba Cloud Coding Plan. Обновить сейчас?', - 'Coding Plan configuration updated successfully. New models are now available.': - 'Конфигурация Coding Plan успешно обновлена. Новые модели теперь доступны.', - 'Coding Plan API key not found. Please re-authenticate with Coding Plan.': - 'API-ключ Coding Plan не найден. Пожалуйста, повторно авторизуйтесь с Coding Plan.', - 'Failed to update Coding Plan configuration: {{message}}': - 'Не удалось обновить конфигурацию Coding Plan: {{message}}', + "API key cannot be empty.": "API-ключ не может быть пустым.", + "You can get your Coding Plan API key here": "Вы можете получить API-ключ Coding Plan здесь", + "New model configurations are available for Alibaba Cloud Coding Plan. Update now?": + "Доступны новые конфигурации моделей для Alibaba Cloud Coding Plan. Обновить сейчас?", + "Coding Plan configuration updated successfully. New models are now available.": + "Конфигурация Coding Plan успешно обновлена. Новые модели теперь доступны.", + "Coding Plan API key not found. Please re-authenticate with Coding Plan.": + "API-ключ Coding Plan не найден. Пожалуйста, повторно авторизуйтесь с Coding Plan.", + "Failed to update Coding Plan configuration: {{message}}": + "Не удалось обновить конфигурацию Coding Plan: {{message}}", // ============================================================================ // Auth Dialog - View Titles and Labels // ============================================================================ - 'Coding Plan': 'Coding Plan', + "Coding Plan": "Coding Plan", "Paste your api key of ModelStudio Coding Plan and you're all set!": - 'Вставьте ваш API-ключ ModelStudio Coding Plan и всё готово!', - Custom: 'Пользовательский', - 'More instructions about configuring `modelProviders` manually.': - 'Дополнительные инструкции по ручной настройке `modelProviders`.', - 'Select API-KEY configuration mode:': 'Выберите режим конфигурации API-KEY:', - '(Press Escape to go back)': '(Нажмите Escape для возврата)', - '(Press Enter to submit, Escape to cancel)': - '(Нажмите Enter для отправки, Escape для отмены)', - 'More instructions please check:': 'Дополнительные инструкции см.:', - 'Select Region for Coding Plan': 'Выберите регион Coding Plan', - 'Choose based on where your account is registered': - 'Выберите в зависимости от места регистрации вашего аккаунта', - 'Enter Coding Plan API Key': 'Введите API-ключ Coding Plan', + "Вставьте ваш API-ключ ModelStudio Coding Plan и всё готово!", + Custom: "Пользовательский", + "More instructions about configuring `modelProviders` manually.": + "Дополнительные инструкции по ручной настройке `modelProviders`.", + "Select API-KEY configuration mode:": "Выберите режим конфигурации API-KEY:", + "(Press Escape to go back)": "(Нажмите Escape для возврата)", + "(Press Enter to submit, Escape to cancel)": "(Нажмите Enter для отправки, Escape для отмены)", + "More instructions please check:": "Дополнительные инструкции см.:", + "Select Region for Coding Plan": "Выберите регион Coding Plan", + "Choose based on where your account is registered": + "Выберите в зависимости от места регистрации вашего аккаунта", + "Enter Coding Plan API Key": "Введите API-ключ Coding Plan", // ============================================================================ // Coding Plan International Updates // ============================================================================ - 'New model configurations are available for {{region}}. Update now?': - 'Доступны новые конфигурации моделей для {{region}}. Обновить сейчас?', + "New model configurations are available for {{region}}. Update now?": + "Доступны новые конфигурации моделей для {{region}}. Обновить сейчас?", '{{region}} configuration updated successfully. Model switched to "{{model}}".': 'Конфигурация {{region}} успешно обновлена. Модель переключена на "{{model}}".', - 'Authenticated successfully with {{region}}. API key and model configs saved to settings.json (backed up).': - 'Успешная аутентификация с {{region}}. API-ключ и конфигурации моделей сохранены в settings.json (резервная копия создана).', + "Authenticated successfully with {{region}}. API key and model configs saved to settings.json (backed up).": + "Успешная аутентификация с {{region}}. API-ключ и конфигурации моделей сохранены в settings.json (резервная копия создана).", // ============================================================================ // Context Usage Component // ============================================================================ - 'Context Usage': 'Использование контекста', - 'No API response yet. Send a message to see actual usage.': - 'Пока нет ответа от API. Отправьте сообщение, чтобы увидеть фактическое использование.', - 'Estimated pre-conversation overhead': - 'Оценочные накладные расходы перед беседой', - 'Context window': 'Контекстное окно', - tokens: 'токенов', - Used: 'Использовано', - Free: 'Свободно', - 'Autocompact buffer': 'Буфер автоупаковки', - 'Usage by category': 'Использование по категориям', - 'System prompt': 'Системная подсказка', - 'Built-in tools': 'Встроенные инструменты', - 'MCP tools': 'Инструменты MCP', - 'Memory files': 'Файлы памяти', - Skills: 'Навыки', - Messages: 'Сообщения', - 'Show context window usage breakdown.': - 'Показать разбивку использования контекстного окна.', - 'Run /context detail for per-item breakdown.': - 'Выполните /context detail для детализации по элементам.', - active: 'активно', - 'body loaded': 'содержимое загружено', - memory: 'память', + "Context Usage": "Использование контекста", + "No API response yet. Send a message to see actual usage.": + "Пока нет ответа от API. Отправьте сообщение, чтобы увидеть фактическое использование.", + "Estimated pre-conversation overhead": "Оценочные накладные расходы перед беседой", + "Context window": "Контекстное окно", + tokens: "токенов", + Used: "Использовано", + Free: "Свободно", + "Autocompact buffer": "Буфер автоупаковки", + "Usage by category": "Использование по категориям", + "System prompt": "Системная подсказка", + "Built-in tools": "Встроенные инструменты", + "MCP tools": "Инструменты MCP", + "Memory files": "Файлы памяти", + Skills: "Навыки", + Messages: "Сообщения", + "Show context window usage breakdown.": "Показать разбивку использования контекстного окна.", + "Run /context detail for per-item breakdown.": + "Выполните /context detail для детализации по элементам.", + active: "активно", + "body loaded": "содержимое загружено", + memory: "память", // MCP Management Dialog // ============================================================================ - 'MCP Management': 'Управление MCP', - 'Server List': 'Список серверов', - 'Server Detail': 'Детали сервера', - 'Disable Server': 'Отключить сервер', - 'Tool List': 'Список инструментов', - 'Tool Detail': 'Детали инструмента', - 'Loading...': 'Загрузка...', - 'Unknown step': 'Неизвестный шаг', - 'Esc to back': 'Esc для возврата', - '↑↓ to navigate · Enter to select · Esc to close': - '↑↓ навигация · Enter выбрать · Esc закрыть', - '↑↓ to navigate · Enter to select · Esc to back': - '↑↓ навигация · Enter выбрать · Esc назад', - '↑↓ to navigate · Enter to confirm · Esc to back': - '↑↓ навигация · Enter подтвердить · Esc назад', - 'User Settings (global)': 'Настройки пользователя (глобальные)', - 'Workspace Settings (project-specific)': - 'Настройки рабочего пространства (проектные)', - 'Disable server:': 'Отключить сервер:', - 'Select where to add the server to the exclude list:': - 'Выберите, где добавить сервер в список исключений:', - 'Press Enter to confirm, Esc to cancel': - 'Enter для подтверждения, Esc для отмены', - 'Status:': 'Статус:', - 'Command:': 'Команда:', - 'Working Directory:': 'Рабочий каталог:', - 'Capabilities:': 'Возможности:', - 'No server selected': 'Сервер не выбран', + "MCP Management": "Управление MCP", + "Server List": "Список серверов", + "Server Detail": "Детали сервера", + "Disable Server": "Отключить сервер", + "Tool List": "Список инструментов", + "Tool Detail": "Детали инструмента", + "Loading...": "Загрузка...", + "Unknown step": "Неизвестный шаг", + "Esc to back": "Esc для возврата", + "↑↓ to navigate · Enter to select · Esc to close": "↑↓ навигация · Enter выбрать · Esc закрыть", + "↑↓ to navigate · Enter to select · Esc to back": "↑↓ навигация · Enter выбрать · Esc назад", + "↑↓ to navigate · Enter to confirm · Esc to back": "↑↓ навигация · Enter подтвердить · Esc назад", + "User Settings (global)": "Настройки пользователя (глобальные)", + "Workspace Settings (project-specific)": "Настройки рабочего пространства (проектные)", + "Disable server:": "Отключить сервер:", + "Select where to add the server to the exclude list:": + "Выберите, где добавить сервер в список исключений:", + "Press Enter to confirm, Esc to cancel": "Enter для подтверждения, Esc для отмены", + "Status:": "Статус:", + "Command:": "Команда:", + "Working Directory:": "Рабочий каталог:", + "Capabilities:": "Возможности:", + "No server selected": "Сервер не выбран", // MCP Server List - 'User MCPs': 'MCP пользователя', - 'Project MCPs': 'MCP проекта', - 'Extension MCPs': 'MCP расширений', - server: 'сервер', - servers: 'серверов', - 'Add MCP servers to your settings to get started.': - 'Добавьте серверы MCP в настройки, чтобы начать.', - 'Run qwen --debug to see error logs': - 'Запустите qwen --debug для просмотра журналов ошибок', + "User MCPs": "MCP пользователя", + "Project MCPs": "MCP проекта", + "Extension MCPs": "MCP расширений", + server: "сервер", + servers: "серверов", + "Add MCP servers to your settings to get started.": + "Добавьте серверы MCP в настройки, чтобы начать.", + "Run qwen --debug to see error logs": "Запустите qwen --debug для просмотра журналов ошибок", // MCP OAuth Authentication - 'OAuth Authentication': 'OAuth-аутентификация', - 'Press Enter to start authentication, Esc to go back': - 'Нажмите Enter для начала аутентификации, Esc для возврата', - 'Authenticating... Please complete the login in your browser.': - 'Аутентификация... Пожалуйста, завершите вход в браузере.', - 'Press Enter or Esc to go back': 'Нажмите Enter или Esc для возврата', + "OAuth Authentication": "OAuth-аутентификация", + "Press Enter to start authentication, Esc to go back": + "Нажмите Enter для начала аутентификации, Esc для возврата", + "Authenticating... Please complete the login in your browser.": + "Аутентификация... Пожалуйста, завершите вход в браузере.", + "Press Enter or Esc to go back": "Нажмите Enter или Esc для возврата", // MCP Tool List - 'No tools available for this server.': - 'Для этого сервера нет доступных инструментов.', - destructive: 'деструктивный', - 'read-only': 'только чтение', - 'open-world': 'открытый мир', - idempotent: 'идемпотентный', - 'Tools for {{name}}': 'Инструменты для {{name}}', - 'Tools for {{serverName}}': 'Инструменты для {{serverName}}', - '{{current}}/{{total}}': '{{current}}/{{total}}', + "No tools available for this server.": "Для этого сервера нет доступных инструментов.", + destructive: "деструктивный", + "read-only": "только чтение", + "open-world": "открытый мир", + idempotent: "идемпотентный", + "Tools for {{name}}": "Инструменты для {{name}}", + "Tools for {{serverName}}": "Инструменты для {{serverName}}", + "{{current}}/{{total}}": "{{current}}/{{total}}", // MCP Tool Detail - required: 'обязательный', - Type: 'Тип', - Enum: 'Перечисление', - Parameters: 'Параметры', - 'No tool selected': 'Инструмент не выбран', - Annotations: 'Аннотации', - Title: 'Заголовок', - 'Read Only': 'Только чтение', - Destructive: 'Деструктивный', - Idempotent: 'Идемпотентный', - 'Open World': 'Открытый мир', - Server: 'Сервер', - '{{region}} configuration updated successfully.': - 'Конфигурация {{region}} успешно обновлена.', - 'Authenticated successfully with {{region}}. API key and model configs saved to settings.json.': - 'Успешная аутентификация с {{region}}. API-ключ и конфигурации моделей сохранены в settings.json.', - 'Tip: Use /model to switch between available Coding Plan models.': - 'Совет: Используйте /model для переключения между доступными моделями Coding Plan.', + required: "обязательный", + Type: "Тип", + Enum: "Перечисление", + Parameters: "Параметры", + "No tool selected": "Инструмент не выбран", + Annotations: "Аннотации", + Title: "Заголовок", + "Read Only": "Только чтение", + Destructive: "Деструктивный", + Idempotent: "Идемпотентный", + "Open World": "Открытый мир", + Server: "Сервер", + "{{region}} configuration updated successfully.": "Конфигурация {{region}} успешно обновлена.", + "Authenticated successfully with {{region}}. API key and model configs saved to settings.json.": + "Успешная аутентификация с {{region}}. API-ключ и конфигурации моделей сохранены в settings.json.", + "Tip: Use /model to switch between available Coding Plan models.": + "Совет: Используйте /model для переключения между доступными моделями Coding Plan.", // ============================================================================ // Ask User Question Tool // ============================================================================ - 'Please answer the following question(s):': - 'Пожалуйста, ответьте на следующий(ие) вопрос(ы):', - 'Cannot ask user questions in non-interactive mode. Please run in interactive mode to use this tool.': - 'Невозможно задавать вопросы пользователю в неинтерактивном режиме. Пожалуйста, запустите в интерактивном режиме для использования этого инструмента.', - 'User declined to answer the questions.': - 'Пользователь отказался отвечать на вопросы.', - 'User has provided the following answers:': - 'Пользователь предоставил следующие ответы:', - 'Failed to process user answers:': - 'Не удалось обработать ответы пользователя:', - 'Type something...': 'Введите что-то...', - Submit: 'Отправить', - 'Submit answers': 'Отправить ответы', - Cancel: 'Отмена', - 'Your answers:': 'Ваши ответы:', - '(not answered)': '(не отвечено)', - 'Ready to submit your answers?': 'Готовы отправить свои ответы?', - '↑/↓: Navigate | ←/→: Switch tabs | Enter: Select': - '↑/↓: Навигация | ←/→: Переключение вкладок | Enter: Выбор', - '↑/↓: Navigate | ←/→: Switch tabs | Space/Enter: Toggle | Esc: Cancel': - '↑/↓: Навигация | ←/→: Переключение вкладок | Space/Enter: Переключить | Esc: Отмена', - '↑/↓: Navigate | Space/Enter: Toggle | Esc: Cancel': - '↑/↓: Навигация | Space/Enter: Переключить | Esc: Отмена', - '↑/↓: Navigate | Enter: Select | Esc: Cancel': - '↑/↓: Навигация | Enter: Выбор | Esc: Отмена', + "Please answer the following question(s):": "Пожалуйста, ответьте на следующий(ие) вопрос(ы):", + "Cannot ask user questions in non-interactive mode. Please run in interactive mode to use this tool.": + "Невозможно задавать вопросы пользователю в неинтерактивном режиме. Пожалуйста, запустите в интерактивном режиме для использования этого инструмента.", + "User declined to answer the questions.": "Пользователь отказался отвечать на вопросы.", + "User has provided the following answers:": "Пользователь предоставил следующие ответы:", + "Failed to process user answers:": "Не удалось обработать ответы пользователя:", + "Type something...": "Введите что-то...", + Submit: "Отправить", + "Submit answers": "Отправить ответы", + Cancel: "Отмена", + "Your answers:": "Ваши ответы:", + "(not answered)": "(не отвечено)", + "Ready to submit your answers?": "Готовы отправить свои ответы?", + "↑/↓: Navigate | ←/→: Switch tabs | Enter: Select": + "↑/↓: Навигация | ←/→: Переключение вкладок | Enter: Выбор", + "↑/↓: Navigate | ←/→: Switch tabs | Space/Enter: Toggle | Esc: Cancel": + "↑/↓: Навигация | ←/→: Переключение вкладок | Space/Enter: Переключить | Esc: Отмена", + "↑/↓: Navigate | Space/Enter: Toggle | Esc: Cancel": + "↑/↓: Навигация | Space/Enter: Переключить | Esc: Отмена", + "↑/↓: Navigate | Enter: Select | Esc: Cancel": "↑/↓: Навигация | Enter: Выбор | Esc: Отмена", // ============================================================================ // Commands - Auth // ============================================================================ - 'Authenticate using Alibaba Cloud Coding Plan': - 'Аутентификация через Alibaba Cloud Coding Plan', - 'Region for Coding Plan (china/global)': - 'Регион для Coding Plan (china/global)', - 'API key for Coding Plan': 'API-ключ для Coding Plan', - 'Show current authentication status': - 'Показать текущий статус аутентификации', - 'Authentication completed successfully.': 'Аутентификация успешно завершена.', - 'Processing Alibaba Cloud Coding Plan authentication...': - 'Обработка аутентификации Alibaba Cloud Coding Plan...', - 'Successfully authenticated with Alibaba Cloud Coding Plan.': - 'Успешная аутентификация через Alibaba Cloud Coding Plan.', - 'Failed to authenticate with Coding Plan: {{error}}': - 'Ошибка аутентификации через Coding Plan: {{error}}', - '中国 (China)': '中国 (China)', - '阿里云百炼 (aliyun.com)': '阿里云百炼 (aliyun.com)', - Global: 'Глобальный', - 'Alibaba Cloud (alibabacloud.com)': 'Alibaba Cloud (alibabacloud.com)', - 'Select region for Coding Plan:': 'Выберите регион для Coding Plan:', - 'Enter your Coding Plan API key: ': 'Введите ваш API-ключ Coding Plan: ', - 'Select authentication method:': 'Выберите метод аутентификации:', - '\n=== Authentication Status ===\n': '\n=== Статус аутентификации ===\n', - '⚠️ No authentication method configured.\n': - '⚠️ Метод аутентификации не настроен.\n', - 'Run one of the following commands to get started:\n': - 'Выполните одну из следующих команд для начала:\n', - ' qwen auth coding-plan - Authenticate with Alibaba Cloud Coding Plan\n': - ' qwen auth coding-plan - Аутентификация через Alibaba Cloud Coding Plan\n', - 'Or simply run:': 'Или просто выполните:', - ' qwen auth - Interactive authentication setup\n': - ' qwen auth - Интерактивная настройка аутентификации\n', - '✓ Authentication Method: Alibaba Cloud Coding Plan': - '✓ Метод аутентификации: Alibaba Cloud Coding Plan', - '中国 (China) - 阿里云百炼': '中国 (China) - 阿里云百炼', - 'Global - Alibaba Cloud': 'Глобальный - Alibaba Cloud', - ' Region: {{region}}': ' Регион: {{region}}', - ' Current Model: {{model}}': ' Текущая модель: {{model}}', - ' Config Version: {{version}}': ' Версия конфигурации: {{version}}', - ' Status: API key configured\n': ' Статус: API-ключ настроен\n', - '⚠️ Authentication Method: Alibaba Cloud Coding Plan (Incomplete)': - '⚠️ Метод аутентификации: Alibaba Cloud Coding Plan (Не завершён)', - ' Issue: API key not found in environment or settings\n': - ' Проблема: API-ключ не найден в окружении или настройках\n', - ' Run `qwen auth coding-plan` to re-configure.\n': - ' Выполните `qwen auth coding-plan` для повторной настройки.\n', - '✓ Authentication Method: {{type}}': '✓ Метод аутентификации: {{type}}', - ' Status: Configured\n': ' Статус: Настроено\n', - 'Failed to check authentication status: {{error}}': - 'Не удалось проверить статус аутентификации: {{error}}', - 'Select an option:': 'Выберите вариант:', - 'Raw mode not available. Please run in an interactive terminal.': - 'Raw-режим недоступен. Пожалуйста, запустите в интерактивном терминале.', - '(Use ↑ ↓ arrows to navigate, Enter to select, Ctrl+C to exit)\n': - '(↑ ↓ стрелки для навигации, Enter для выбора, Ctrl+C для выхода)\n', - verbose: 'подробный', - 'Show full tool output and thinking in verbose mode (toggle with Ctrl+O).': - 'Показывать полный вывод инструментов и процесс рассуждений в подробном режиме (переключить с помощью Ctrl+O).', - 'Press Ctrl+O to show full tool output': - 'Нажмите Ctrl+O для показа полного вывода инструментов', + "Authenticate using Alibaba Cloud Coding Plan": "Аутентификация через Alibaba Cloud Coding Plan", + "Region for Coding Plan (china/global)": "Регион для Coding Plan (china/global)", + "API key for Coding Plan": "API-ключ для Coding Plan", + "Show current authentication status": "Показать текущий статус аутентификации", + "Authentication completed successfully.": "Аутентификация успешно завершена.", + "Processing Alibaba Cloud Coding Plan authentication...": + "Обработка аутентификации Alibaba Cloud Coding Plan...", + "Successfully authenticated with Alibaba Cloud Coding Plan.": + "Успешная аутентификация через Alibaba Cloud Coding Plan.", + "Failed to authenticate with Coding Plan: {{error}}": + "Ошибка аутентификации через Coding Plan: {{error}}", + "中国 (China)": "中国 (China)", + "阿里云百炼 (aliyun.com)": "阿里云百炼 (aliyun.com)", + Global: "Глобальный", + "Alibaba Cloud (alibabacloud.com)": "Alibaba Cloud (alibabacloud.com)", + "Select region for Coding Plan:": "Выберите регион для Coding Plan:", + "Enter your Coding Plan API key: ": "Введите ваш API-ключ Coding Plan: ", + "Select authentication method:": "Выберите метод аутентификации:", + "\n=== Authentication Status ===\n": "\n=== Статус аутентификации ===\n", + "⚠️ No authentication method configured.\n": "⚠️ Метод аутентификации не настроен.\n", + "Run one of the following commands to get started:\n": + "Выполните одну из следующих команд для начала:\n", + " qwen auth coding-plan - Authenticate with Alibaba Cloud Coding Plan\n": + " qwen auth coding-plan - Аутентификация через Alibaba Cloud Coding Plan\n", + "Or simply run:": "Или просто выполните:", + " qwen auth - Interactive authentication setup\n": + " qwen auth - Интерактивная настройка аутентификации\n", + "✓ Authentication Method: Alibaba Cloud Coding Plan": + "✓ Метод аутентификации: Alibaba Cloud Coding Plan", + "中国 (China) - 阿里云百炼": "中国 (China) - 阿里云百炼", + "Global - Alibaba Cloud": "Глобальный - Alibaba Cloud", + " Region: {{region}}": " Регион: {{region}}", + " Current Model: {{model}}": " Текущая модель: {{model}}", + " Config Version: {{version}}": " Версия конфигурации: {{version}}", + " Status: API key configured\n": " Статус: API-ключ настроен\n", + "⚠️ Authentication Method: Alibaba Cloud Coding Plan (Incomplete)": + "⚠️ Метод аутентификации: Alibaba Cloud Coding Plan (Не завершён)", + " Issue: API key not found in environment or settings\n": + " Проблема: API-ключ не найден в окружении или настройках\n", + " Run `qwen auth coding-plan` to re-configure.\n": + " Выполните `qwen auth coding-plan` для повторной настройки.\n", + "✓ Authentication Method: {{type}}": "✓ Метод аутентификации: {{type}}", + " Status: Configured\n": " Статус: Настроено\n", + "Failed to check authentication status: {{error}}": + "Не удалось проверить статус аутентификации: {{error}}", + "Select an option:": "Выберите вариант:", + "Raw mode not available. Please run in an interactive terminal.": + "Raw-режим недоступен. Пожалуйста, запустите в интерактивном терминале.", + "(Use ↑ ↓ arrows to navigate, Enter to select, Ctrl+C to exit)\n": + "(↑ ↓ стрелки для навигации, Enter для выбора, Ctrl+C для выхода)\n", + verbose: "подробный", + "Show full tool output and thinking in verbose mode (toggle with Ctrl+O).": + "Показывать полный вывод инструментов и процесс рассуждений в подробном режиме (переключить с помощью Ctrl+O).", + "Press Ctrl+O to show full tool output": "Нажмите Ctrl+O для показа полного вывода инструментов", - 'Switch to plan mode or exit plan mode': - 'Switch to plan mode or exit plan mode', - 'Exited plan mode. Previous approval mode restored.': - 'Exited plan mode. Previous approval mode restored.', - 'Enabled plan mode. The agent will analyze and plan without executing tools.': - 'Enabled plan mode. The agent will analyze and plan without executing tools.', + "Switch to plan mode or exit plan mode": "Switch to plan mode or exit plan mode", + "Exited plan mode. Previous approval mode restored.": + "Exited plan mode. Previous approval mode restored.", + "Enabled plan mode. The agent will analyze and plan without executing tools.": + "Enabled plan mode. The agent will analyze and plan without executing tools.", 'Already in plan mode. Use "/plan exit" to exit plan mode.': 'Already in plan mode. Use "/plan exit" to exit plan mode.', 'Not in plan mode. Use "/plan" to enter plan mode first.': diff --git a/apps/airiscode-cli/src/i18n/locales/zh.js b/apps/airiscode-cli/src/i18n/locales/zh.js index a73f686bd..5413d1231 100644 --- a/apps/airiscode-cli/src/i18n/locales/zh.js +++ b/apps/airiscode-cli/src/i18n/locales/zh.js @@ -11,1172 +11,1066 @@ export default { // Help / UI Components // ============================================================================ // Attachment hints - '↑ to manage attachments': '↑ 管理附件', - '← → select, Delete to remove, ↓ to exit': '← → 选择,Delete 删除,↓ 退出', - 'Attachments: ': '附件:', + "↑ to manage attachments": "↑ 管理附件", + "← → select, Delete to remove, ↓ to exit": "← → 选择,Delete 删除,↓ 退出", + "Attachments: ": "附件:", - 'Basics:': '基础功能:', - 'Add context': '添加上下文', - 'Use {{symbol}} to specify files for context (e.g., {{example}}) to target specific files or folders.': - '使用 {{symbol}} 指定文件作为上下文(例如,{{example}}),用于定位特定文件或文件夹', - '@': '@', - '@src/myFile.ts': '@src/myFile.ts', - 'Shell mode': 'Shell 模式', - 'YOLO mode': 'YOLO 模式', - 'plan mode': '规划模式', - 'auto-accept edits': '自动接受编辑', - 'Accepting edits': '接受编辑', - '(shift + tab to cycle)': '(shift + tab 切换)', - '(tab to cycle)': '(按 tab 切换)', - 'Execute shell commands via {{symbol}} (e.g., {{example1}}) or use natural language (e.g., {{example2}}).': - '通过 {{symbol}} 执行 shell 命令(例如,{{example1}})或使用自然语言(例如,{{example2}})', - '!': '!', - '!npm run start': '!npm run start', - 'start server': 'start server', - 'Commands:': '命令:', - 'shell command': 'shell 命令', - 'Model Context Protocol command (from external servers)': - '模型上下文协议命令(来自外部服务器)', - 'Keyboard Shortcuts:': '键盘快捷键:', - 'Toggle this help display': '切换此帮助显示', - 'Toggle shell mode': '切换命令行模式', - 'Open command menu': '打开命令菜单', - 'Add file context': '添加文件上下文', - 'Accept suggestion / Autocomplete': '接受建议 / 自动补全', - 'Reverse search history': '反向搜索历史', - 'Press ? again to close': '再次按 ? 关闭', + "Basics:": "基础功能:", + "Add context": "添加上下文", + "Use {{symbol}} to specify files for context (e.g., {{example}}) to target specific files or folders.": + "使用 {{symbol}} 指定文件作为上下文(例如,{{example}}),用于定位特定文件或文件夹", + "@": "@", + "@src/myFile.ts": "@src/myFile.ts", + "Shell mode": "Shell 模式", + "YOLO mode": "YOLO 模式", + "plan mode": "规划模式", + "auto-accept edits": "自动接受编辑", + "Accepting edits": "接受编辑", + "(shift + tab to cycle)": "(shift + tab 切换)", + "(tab to cycle)": "(按 tab 切换)", + "Execute shell commands via {{symbol}} (e.g., {{example1}}) or use natural language (e.g., {{example2}}).": + "通过 {{symbol}} 执行 shell 命令(例如,{{example1}})或使用自然语言(例如,{{example2}})", + "!": "!", + "!npm run start": "!npm run start", + "start server": "start server", + "Commands:": "命令:", + "shell command": "shell 命令", + "Model Context Protocol command (from external servers)": "模型上下文协议命令(来自外部服务器)", + "Keyboard Shortcuts:": "键盘快捷键:", + "Toggle this help display": "切换此帮助显示", + "Toggle shell mode": "切换命令行模式", + "Open command menu": "打开命令菜单", + "Add file context": "添加文件上下文", + "Accept suggestion / Autocomplete": "接受建议 / 自动补全", + "Reverse search history": "反向搜索历史", + "Press ? again to close": "再次按 ? 关闭", // Keyboard shortcuts panel descriptions - 'for shell mode': '命令行模式', - 'for commands': '命令菜单', - 'for file paths': '文件路径', - 'to clear input': '清空输入', - 'to cycle approvals': '切换审批模式', - 'to quit': '退出', - 'for newline': '换行', - 'to clear screen': '清屏', - 'to search history': '搜索历史', - 'to paste images': '粘贴图片', - 'for external editor': '外部编辑器', - 'Jump through words in the input': '在输入中按单词跳转', - 'Close dialogs, cancel requests, or quit application': - '关闭对话框、取消请求或退出应用程序', - 'New line': '换行', - 'New line (Alt+Enter works for certain linux distros)': - '换行(某些 Linux 发行版支持 Alt+Enter)', - 'Clear the screen': '清屏', - 'Open input in external editor': '在外部编辑器中打开输入', - 'Send message': '发送消息', - 'Initializing...': '正在初始化...', - 'Connecting to MCP servers... ({{connected}}/{{total}})': - '正在连接到 MCP 服务器... ({{connected}}/{{total}})', - 'Type your message or @path/to/file': '输入您的消息或 @ 文件路径', - '? for shortcuts': '按 ? 查看快捷键', + "for shell mode": "命令行模式", + "for commands": "命令菜单", + "for file paths": "文件路径", + "to clear input": "清空输入", + "to cycle approvals": "切换审批模式", + "to quit": "退出", + "for newline": "换行", + "to clear screen": "清屏", + "to search history": "搜索历史", + "to paste images": "粘贴图片", + "for external editor": "外部编辑器", + "Jump through words in the input": "在输入中按单词跳转", + "Close dialogs, cancel requests, or quit application": "关闭对话框、取消请求或退出应用程序", + "New line": "换行", + "New line (Alt+Enter works for certain linux distros)": "换行(某些 Linux 发行版支持 Alt+Enter)", + "Clear the screen": "清屏", + "Open input in external editor": "在外部编辑器中打开输入", + "Send message": "发送消息", + "Initializing...": "正在初始化...", + "Connecting to MCP servers... ({{connected}}/{{total}})": + "正在连接到 MCP 服务器... ({{connected}}/{{total}})", + "Type your message or @path/to/file": "输入您的消息或 @ 文件路径", + "? for shortcuts": "按 ? 查看快捷键", "Press 'i' for INSERT mode and 'Esc' for NORMAL mode.": "按 'i' 进入插入模式,按 'Esc' 进入普通模式", - 'Cancel operation / Clear input (double press)': - '取消操作 / 清空输入(双击)', - 'Cycle approval modes': '循环切换审批模式', - 'Cycle through your prompt history': '循环浏览提示历史', - 'For a full list of shortcuts, see {{docPath}}': - '完整快捷键列表,请参阅 {{docPath}}', - 'docs/keyboard-shortcuts.md': 'docs/keyboard-shortcuts.md', - 'for help on Qwen Code': '获取 Qwen Code 帮助', - 'show version info': '显示版本信息', - 'submit a bug report': '提交错误报告', - 'About Qwen Code': '关于 Qwen Code', - Status: '状态', + "Cancel operation / Clear input (double press)": "取消操作 / 清空输入(双击)", + "Cycle approval modes": "循环切换审批模式", + "Cycle through your prompt history": "循环浏览提示历史", + "For a full list of shortcuts, see {{docPath}}": "完整快捷键列表,请参阅 {{docPath}}", + "docs/keyboard-shortcuts.md": "docs/keyboard-shortcuts.md", + "for help on Qwen Code": "获取 Qwen Code 帮助", + "show version info": "显示版本信息", + "submit a bug report": "提交错误报告", + "About Qwen Code": "关于 Qwen Code", + Status: "状态", // ============================================================================ // System Information Fields // ============================================================================ - 'Qwen Code': 'Qwen Code', - Runtime: '运行环境', - OS: '操作系统', - Auth: '认证', - 'CLI Version': 'CLI 版本', - 'Git Commit': 'Git 提交', - Model: '模型', - 'Fast Model': '快速模型', - Sandbox: '沙箱', - 'OS Platform': '操作系统平台', - 'OS Arch': '操作系统架构', - 'OS Release': '操作系统版本', - 'Node.js Version': 'Node.js 版本', - 'NPM Version': 'NPM 版本', - 'Session ID': '会话 ID', - 'Auth Method': '认证方式', - 'Base URL': '基础 URL', - Proxy: '代理', - 'Memory Usage': '内存使用', - 'IDE Client': 'IDE 客户端', + "Qwen Code": "Qwen Code", + Runtime: "运行环境", + OS: "操作系统", + Auth: "认证", + "CLI Version": "CLI 版本", + "Git Commit": "Git 提交", + Model: "模型", + "Fast Model": "快速模型", + Sandbox: "沙箱", + "OS Platform": "操作系统平台", + "OS Arch": "操作系统架构", + "OS Release": "操作系统版本", + "Node.js Version": "Node.js 版本", + "NPM Version": "NPM 版本", + "Session ID": "会话 ID", + "Auth Method": "认证方式", + "Base URL": "基础 URL", + Proxy: "代理", + "Memory Usage": "内存使用", + "IDE Client": "IDE 客户端", // ============================================================================ // Commands - General // ============================================================================ - 'Analyzes the project and creates a tailored QWEN.md file.': - '分析项目并创建定制的 QWEN.md 文件', - 'List available Qwen Code tools. Usage: /tools [desc]': - '列出可用的 Qwen Code 工具。用法:/tools [desc]', - 'List available skills.': '列出可用技能。', - 'Available Qwen Code CLI tools:': '可用的 Qwen Code CLI 工具:', - 'No tools available': '没有可用工具', - 'View or change the approval mode for tool usage': - '查看或更改工具使用的审批模式', + "Analyzes the project and creates a tailored QWEN.md file.": "分析项目并创建定制的 QWEN.md 文件", + "List available Qwen Code tools. Usage: /tools [desc]": + "列出可用的 Qwen Code 工具。用法:/tools [desc]", + "List available skills.": "列出可用技能。", + "Available Qwen Code CLI tools:": "可用的 Qwen Code CLI 工具:", + "No tools available": "没有可用工具", + "View or change the approval mode for tool usage": "查看或更改工具使用的审批模式", 'Invalid approval mode "{{arg}}". Valid modes: {{modes}}': '无效的审批模式 "{{arg}}"。有效模式:{{modes}}', 'Approval mode set to "{{mode}}"': '审批模式已设置为 "{{mode}}"', - 'View or change the language setting': '查看或更改语言设置', - 'change the theme': '更改主题', - 'Select Theme': '选择主题', - Preview: '预览', - '(Use Enter to select, Tab to configure scope)': - '(使用 Enter 选择,Tab 配置作用域)', - '(Use Enter to apply scope, Tab to go back)': - '(使用 Enter 应用作用域,Tab 返回)', - 'Theme configuration unavailable due to NO_COLOR env variable.': - '由于 NO_COLOR 环境变量,主题配置不可用。', + "View or change the language setting": "查看或更改语言设置", + "change the theme": "更改主题", + "Select Theme": "选择主题", + Preview: "预览", + "(Use Enter to select, Tab to configure scope)": "(使用 Enter 选择,Tab 配置作用域)", + "(Use Enter to apply scope, Tab to go back)": "(使用 Enter 应用作用域,Tab 返回)", + "Theme configuration unavailable due to NO_COLOR env variable.": + "由于 NO_COLOR 环境变量,主题配置不可用。", 'Theme "{{themeName}}" not found.': '未找到主题 "{{themeName}}"。', 'Theme "{{themeName}}" not found in selected scope.': '在所选作用域中未找到主题 "{{themeName}}"。', - 'Clear conversation history and free up context': '清除对话历史并释放上下文', - 'Compresses the context by replacing it with a summary.': - '通过摘要替换来压缩上下文', - 'open full Qwen Code documentation in your browser': - '在浏览器中打开完整的 Qwen Code 文档', - 'Configuration not available.': '配置不可用', - 'change the auth method': '更改认证方法', - 'Configure authentication information for login': '配置登录认证信息', - 'Copy the last result or code snippet to clipboard': - '将最后的结果或代码片段复制到剪贴板', + "Clear conversation history and free up context": "清除对话历史并释放上下文", + "Compresses the context by replacing it with a summary.": "通过摘要替换来压缩上下文", + "open full Qwen Code documentation in your browser": "在浏览器中打开完整的 Qwen Code 文档", + "Configuration not available.": "配置不可用", + "change the auth method": "更改认证方法", + "Configure authentication information for login": "配置登录认证信息", + "Copy the last result or code snippet to clipboard": "将最后的结果或代码片段复制到剪贴板", // ============================================================================ // Commands - Agents // ============================================================================ - 'Manage subagents for specialized task delegation.': - '管理用于专门任务委派的子智能体', - 'Manage existing subagents (view, edit, delete).': - '管理现有子智能体(查看、编辑、删除)', - 'Create a new subagent with guided setup.': '通过引导式设置创建新的子智能体', + "Manage subagents for specialized task delegation.": "管理用于专门任务委派的子智能体", + "Manage existing subagents (view, edit, delete).": "管理现有子智能体(查看、编辑、删除)", + "Create a new subagent with guided setup.": "通过引导式设置创建新的子智能体", // ============================================================================ // Agents - Management Dialog // ============================================================================ - Agents: '智能体', - 'Choose Action': '选择操作', - 'Edit {{name}}': '编辑 {{name}}', - 'Edit Tools: {{name}}': '编辑工具: {{name}}', - 'Edit Color: {{name}}': '编辑颜色: {{name}}', - 'Delete {{name}}': '删除 {{name}}', - 'Unknown Step': '未知步骤', - 'Esc to close': '按 Esc 关闭', - 'Enter to select, ↑↓ to navigate, Esc to close': - 'Enter 选择,↑↓ 导航,Esc 关闭', - 'Esc to go back': '按 Esc 返回', - 'Enter to confirm, Esc to cancel': 'Enter 确认,Esc 取消', - 'Enter to select, ↑↓ to navigate, Esc to go back': - 'Enter 选择,↑↓ 导航,Esc 返回', - 'Enter to submit, Esc to go back': 'Enter 提交,Esc 返回', - 'Invalid step: {{step}}': '无效步骤: {{step}}', - 'No subagents found.': '未找到子智能体。', + Agents: "智能体", + "Choose Action": "选择操作", + "Edit {{name}}": "编辑 {{name}}", + "Edit Tools: {{name}}": "编辑工具: {{name}}", + "Edit Color: {{name}}": "编辑颜色: {{name}}", + "Delete {{name}}": "删除 {{name}}", + "Unknown Step": "未知步骤", + "Esc to close": "按 Esc 关闭", + "Enter to select, ↑↓ to navigate, Esc to close": "Enter 选择,↑↓ 导航,Esc 关闭", + "Esc to go back": "按 Esc 返回", + "Enter to confirm, Esc to cancel": "Enter 确认,Esc 取消", + "Enter to select, ↑↓ to navigate, Esc to go back": "Enter 选择,↑↓ 导航,Esc 返回", + "Enter to submit, Esc to go back": "Enter 提交,Esc 返回", + "Invalid step: {{step}}": "无效步骤: {{step}}", + "No subagents found.": "未找到子智能体。", "Use '/agents create' to create your first subagent.": "使用 '/agents create' 创建您的第一个子智能体。", - '(built-in)': '(内置)', - '(overridden by project level agent)': '(已被项目级智能体覆盖)', - 'Project Level ({{path}})': '项目级 ({{path}})', - 'User Level ({{path}})': '用户级 ({{path}})', - 'Built-in Agents': '内置智能体', - 'Extension Agents': '扩展智能体', - 'Using: {{count}} agents': '使用中: {{count}} 个智能体', - 'View Agent': '查看智能体', - 'Edit Agent': '编辑智能体', - 'Delete Agent': '删除智能体', - Back: '返回', - 'No agent selected': '未选择智能体', - 'File Path: ': '文件路径: ', - 'Tools: ': '工具: ', - 'Color: ': '颜色: ', - 'Description:': '描述:', - 'System Prompt:': '系统提示:', - 'Open in editor': '在编辑器中打开', - 'Edit tools': '编辑工具', - 'Edit color': '编辑颜色', - '❌ Error:': '❌ 错误:', - 'Are you sure you want to delete agent "{{name}}"?': - '您确定要删除智能体 "{{name}}" 吗?', + "(built-in)": "(内置)", + "(overridden by project level agent)": "(已被项目级智能体覆盖)", + "Project Level ({{path}})": "项目级 ({{path}})", + "User Level ({{path}})": "用户级 ({{path}})", + "Built-in Agents": "内置智能体", + "Extension Agents": "扩展智能体", + "Using: {{count}} agents": "使用中: {{count}} 个智能体", + "View Agent": "查看智能体", + "Edit Agent": "编辑智能体", + "Delete Agent": "删除智能体", + Back: "返回", + "No agent selected": "未选择智能体", + "File Path: ": "文件路径: ", + "Tools: ": "工具: ", + "Color: ": "颜色: ", + "Description:": "描述:", + "System Prompt:": "系统提示:", + "Open in editor": "在编辑器中打开", + "Edit tools": "编辑工具", + "Edit color": "编辑颜色", + "❌ Error:": "❌ 错误:", + 'Are you sure you want to delete agent "{{name}}"?': '您确定要删除智能体 "{{name}}" 吗?', // ============================================================================ // Agents - Creation Wizard // ============================================================================ - 'Project Level (.qwen/agents/)': '项目级 (.qwen/agents/)', - 'User Level (~/.qwen/agents/)': '用户级 (~/.qwen/agents/)', - '✅ Subagent Created Successfully!': '✅ 子智能体创建成功!', + "Project Level (.qwen/agents/)": "项目级 (.qwen/agents/)", + "User Level (~/.qwen/agents/)": "用户级 (~/.qwen/agents/)", + "✅ Subagent Created Successfully!": "✅ 子智能体创建成功!", 'Subagent "{{name}}" has been saved to {{level}} level.': '子智能体 "{{name}}" 已保存到 {{level}} 级别。', - 'Name: ': '名称: ', - 'Location: ': '位置: ', - '❌ Error saving subagent:': '❌ 保存子智能体时出错:', - 'Warnings:': '警告:', + "Name: ": "名称: ", + "Location: ": "位置: ", + "❌ Error saving subagent:": "❌ 保存子智能体时出错:", + "Warnings:": "警告:", 'Name "{{name}}" already exists at {{level}} level - will overwrite existing subagent': '名称 "{{name}}" 在 {{level}} 级别已存在 - 将覆盖现有子智能体', 'Name "{{name}}" exists at user level - project level will take precedence': '名称 "{{name}}" 在用户级别存在 - 项目级别将优先', 'Name "{{name}}" exists at project level - existing subagent will take precedence': '名称 "{{name}}" 在项目级别存在 - 现有子智能体将优先', - 'Description is over {{length}} characters': '描述超过 {{length}} 个字符', - 'System prompt is over {{length}} characters': - '系统提示超过 {{length}} 个字符', + "Description is over {{length}} characters": "描述超过 {{length}} 个字符", + "System prompt is over {{length}} characters": "系统提示超过 {{length}} 个字符", // Agents - Creation Wizard Steps - 'Step {{n}}: Choose Location': '步骤 {{n}}: 选择位置', - 'Step {{n}}: Choose Generation Method': '步骤 {{n}}: 选择生成方式', - 'Generate with Qwen Code (Recommended)': '使用 Qwen Code 生成(推荐)', - 'Manual Creation': '手动创建', - 'Describe what this subagent should do and when it should be used. (Be comprehensive for best results)': - '描述此子智能体应该做什么以及何时使用它。(为了获得最佳效果,请全面描述)', - 'e.g., Expert code reviewer that reviews code based on best practices...': - '例如:专业的代码审查员,根据最佳实践审查代码...', - 'Generating subagent configuration...': '正在生成子智能体配置...', - 'Failed to generate subagent: {{error}}': '生成子智能体失败: {{error}}', - 'Step {{n}}: Describe Your Subagent': '步骤 {{n}}: 描述您的子智能体', - 'Step {{n}}: Enter Subagent Name': '步骤 {{n}}: 输入子智能体名称', - 'Step {{n}}: Enter System Prompt': '步骤 {{n}}: 输入系统提示', - 'Step {{n}}: Enter Description': '步骤 {{n}}: 输入描述', + "Step {{n}}: Choose Location": "步骤 {{n}}: 选择位置", + "Step {{n}}: Choose Generation Method": "步骤 {{n}}: 选择生成方式", + "Generate with Qwen Code (Recommended)": "使用 Qwen Code 生成(推荐)", + "Manual Creation": "手动创建", + "Describe what this subagent should do and when it should be used. (Be comprehensive for best results)": + "描述此子智能体应该做什么以及何时使用它。(为了获得最佳效果,请全面描述)", + "e.g., Expert code reviewer that reviews code based on best practices...": + "例如:专业的代码审查员,根据最佳实践审查代码...", + "Generating subagent configuration...": "正在生成子智能体配置...", + "Failed to generate subagent: {{error}}": "生成子智能体失败: {{error}}", + "Step {{n}}: Describe Your Subagent": "步骤 {{n}}: 描述您的子智能体", + "Step {{n}}: Enter Subagent Name": "步骤 {{n}}: 输入子智能体名称", + "Step {{n}}: Enter System Prompt": "步骤 {{n}}: 输入系统提示", + "Step {{n}}: Enter Description": "步骤 {{n}}: 输入描述", // Agents - Tool Selection - 'Step {{n}}: Select Tools': '步骤 {{n}}: 选择工具', - 'All Tools (Default)': '所有工具(默认)', - 'All Tools': '所有工具', - 'Read-only Tools': '只读工具', - 'Read & Edit Tools': '读取和编辑工具', - 'Read & Edit & Execution Tools': '读取、编辑和执行工具', - 'All tools selected, including MCP tools': '已选择所有工具,包括 MCP 工具', - 'Selected tools:': '已选择的工具:', - 'Read-only tools:': '只读工具:', - 'Edit tools:': '编辑工具:', - 'Execution tools:': '执行工具:', - 'Step {{n}}: Choose Background Color': '步骤 {{n}}: 选择背景颜色', - 'Step {{n}}: Confirm and Save': '步骤 {{n}}: 确认并保存', + "Step {{n}}: Select Tools": "步骤 {{n}}: 选择工具", + "All Tools (Default)": "所有工具(默认)", + "All Tools": "所有工具", + "Read-only Tools": "只读工具", + "Read & Edit Tools": "读取和编辑工具", + "Read & Edit & Execution Tools": "读取、编辑和执行工具", + "All tools selected, including MCP tools": "已选择所有工具,包括 MCP 工具", + "Selected tools:": "已选择的工具:", + "Read-only tools:": "只读工具:", + "Edit tools:": "编辑工具:", + "Execution tools:": "执行工具:", + "Step {{n}}: Choose Background Color": "步骤 {{n}}: 选择背景颜色", + "Step {{n}}: Confirm and Save": "步骤 {{n}}: 确认并保存", // Agents - Navigation & Instructions - 'Esc to cancel': '按 Esc 取消', - 'Press Enter to save, e to save and edit, Esc to go back': - '按 Enter 保存,e 保存并编辑,Esc 返回', - 'Press Enter to continue, {{navigation}}Esc to {{action}}': - '按 Enter 继续,{{navigation}}Esc {{action}}', - cancel: '取消', - 'go back': '返回', - '↑↓ to navigate, ': '↑↓ 导航,', - 'Enter a clear, unique name for this subagent.': - '为此子智能体输入一个清晰、唯一的名称。', - 'e.g., Code Reviewer': '例如:代码审查员', - 'Name cannot be empty.': '名称不能为空。', + "Esc to cancel": "按 Esc 取消", + "Press Enter to save, e to save and edit, Esc to go back": + "按 Enter 保存,e 保存并编辑,Esc 返回", + "Press Enter to continue, {{navigation}}Esc to {{action}}": + "按 Enter 继续,{{navigation}}Esc {{action}}", + cancel: "取消", + "go back": "返回", + "↑↓ to navigate, ": "↑↓ 导航,", + "Enter a clear, unique name for this subagent.": "为此子智能体输入一个清晰、唯一的名称。", + "e.g., Code Reviewer": "例如:代码审查员", + "Name cannot be empty.": "名称不能为空。", "Write the system prompt that defines this subagent's behavior. Be comprehensive for best results.": - '编写定义此子智能体行为的系统提示。为了获得最佳效果,请全面描述。', - 'e.g., You are an expert code reviewer...': - '例如:您是一位专业的代码审查员...', - 'System prompt cannot be empty.': '系统提示不能为空。', - 'Describe when and how this subagent should be used.': - '描述何时以及如何使用此子智能体。', - 'e.g., Reviews code for best practices and potential bugs.': - '例如:审查代码以查找最佳实践和潜在错误。', - 'Description cannot be empty.': '描述不能为空。', - 'Failed to launch editor: {{error}}': '启动编辑器失败: {{error}}', - 'Failed to save and edit subagent: {{error}}': - '保存并编辑子智能体失败: {{error}}', + "编写定义此子智能体行为的系统提示。为了获得最佳效果,请全面描述。", + "e.g., You are an expert code reviewer...": "例如:您是一位专业的代码审查员...", + "System prompt cannot be empty.": "系统提示不能为空。", + "Describe when and how this subagent should be used.": "描述何时以及如何使用此子智能体。", + "e.g., Reviews code for best practices and potential bugs.": + "例如:审查代码以查找最佳实践和潜在错误。", + "Description cannot be empty.": "描述不能为空。", + "Failed to launch editor: {{error}}": "启动编辑器失败: {{error}}", + "Failed to save and edit subagent: {{error}}": "保存并编辑子智能体失败: {{error}}", // ============================================================================ // Extensions - Management Dialog // ============================================================================ - 'Manage Extensions': '管理扩展', - 'Extension Details': '扩展详情', - 'View Extension': '查看扩展', - 'Update Extension': '更新扩展', - 'Disable Extension': '禁用扩展', - 'Enable Extension': '启用扩展', - 'Uninstall Extension': '卸载扩展', - 'Select Scope': '选择作用域', - 'User Scope': '用户作用域', - 'Workspace Scope': '工作区作用域', - 'No extensions found.': '未找到扩展。', - Active: '已启用', - Disabled: '已禁用', - 'Update available': '有可用更新', - 'Up to date': '已是最新', - 'Checking...': '检查中...', - 'Updating...': '更新中...', - Unknown: '未知', - Error: '错误', - 'Version:': '版本:', - 'Status:': '状态:', - 'Are you sure you want to uninstall extension "{{name}}"?': - '确定要卸载扩展 "{{name}}" 吗?', - 'This action cannot be undone.': '此操作无法撤销。', + "Manage Extensions": "管理扩展", + "Extension Details": "扩展详情", + "View Extension": "查看扩展", + "Update Extension": "更新扩展", + "Disable Extension": "禁用扩展", + "Enable Extension": "启用扩展", + "Uninstall Extension": "卸载扩展", + "Select Scope": "选择作用域", + "User Scope": "用户作用域", + "Workspace Scope": "工作区作用域", + "No extensions found.": "未找到扩展。", + Active: "已启用", + Disabled: "已禁用", + "Update available": "有可用更新", + "Up to date": "已是最新", + "Checking...": "检查中...", + "Updating...": "更新中...", + Unknown: "未知", + Error: "错误", + "Version:": "版本:", + "Status:": "状态:", + 'Are you sure you want to uninstall extension "{{name}}"?': '确定要卸载扩展 "{{name}}" 吗?', + "This action cannot be undone.": "此操作无法撤销。", 'Extension "{{name}}" disabled successfully.': '扩展 "{{name}}" 禁用成功。', 'Extension "{{name}}" enabled successfully.': '扩展 "{{name}}" 启用成功。', 'Extension "{{name}}" updated successfully.': '扩展 "{{name}}" 更新成功。', - 'Failed to update extension "{{name}}": {{error}}': - '更新扩展 "{{name}}" 失败:{{error}}', - 'Select the scope for this action:': '选择此操作的作用域:', - 'User - Applies to all projects': '用户 - 应用于所有项目', - 'Workspace - Applies to current project only': '工作区 - 仅应用于当前项目', + 'Failed to update extension "{{name}}": {{error}}': '更新扩展 "{{name}}" 失败:{{error}}', + "Select the scope for this action:": "选择此操作的作用域:", + "User - Applies to all projects": "用户 - 应用于所有项目", + "Workspace - Applies to current project only": "工作区 - 仅应用于当前项目", // Extension dialog - missing keys - 'Name:': '名称:', - 'MCP Servers:': 'MCP 服务器:', - 'Settings:': '设置:', - active: '已启用', - 'View Details': '查看详情', - 'Update failed:': '更新失败:', - 'Updating {{name}}...': '正在更新 {{name}}...', - 'Update complete!': '更新完成!', - 'User (global)': '用户(全局)', - 'Workspace (project-specific)': '工作区(项目特定)', + "Name:": "名称:", + "MCP Servers:": "MCP 服务器:", + "Settings:": "设置:", + active: "已启用", + "View Details": "查看详情", + "Update failed:": "更新失败:", + "Updating {{name}}...": "正在更新 {{name}}...", + "Update complete!": "更新完成!", + "User (global)": "用户(全局)", + "Workspace (project-specific)": "工作区(项目特定)", 'Disable "{{name}}" - Select Scope': '禁用 "{{name}}" - 选择作用域', 'Enable "{{name}}" - Select Scope': '启用 "{{name}}" - 选择作用域', - 'No extension selected': '未选择扩展', - 'Press Y/Enter to confirm, N/Esc to cancel': '按 Y/Enter 确认,N/Esc 取消', - 'Y/Enter to confirm, N/Esc to cancel': 'Y/Enter 确认,N/Esc 取消', - '{{count}} extensions installed': '已安装 {{count}} 个扩展', + "No extension selected": "未选择扩展", + "Press Y/Enter to confirm, N/Esc to cancel": "按 Y/Enter 确认,N/Esc 取消", + "Y/Enter to confirm, N/Esc to cancel": "Y/Enter 确认,N/Esc 取消", + "{{count}} extensions installed": "已安装 {{count}} 个扩展", "Use '/extensions install' to install your first extension.": "使用 '/extensions install' 安装您的第一个扩展。", // Update status values - 'up to date': '已是最新', - 'update available': '有可用更新', - 'checking...': '检查中...', - 'not updatable': '不可更新', - error: '错误', + "up to date": "已是最新", + "update available": "有可用更新", + "checking...": "检查中...", + "not updatable": "不可更新", + error: "错误", // ============================================================================ // Commands - General (continued) // ============================================================================ - 'View and edit Qwen Code settings': '查看和编辑 Qwen Code 设置', - Settings: '设置', - 'To see changes, Qwen Code must be restarted. Press r to exit and apply changes now.': - '要查看更改,必须重启 Qwen Code。按 r 退出并立即应用更改。', + "View and edit Qwen Code settings": "查看和编辑 Qwen Code 设置", + Settings: "设置", + "To see changes, Qwen Code must be restarted. Press r to exit and apply changes now.": + "要查看更改,必须重启 Qwen Code。按 r 退出并立即应用更改。", 'The command "/{{command}}" is not supported in non-interactive mode.': '不支持在非交互模式下使用命令 "/{{command}}"。', // ============================================================================ // Settings Labels // ============================================================================ - 'Vim Mode': 'Vim 模式', - 'Disable Auto Update': '禁用自动更新', - 'Attribution: commit': '署名:提交', - 'Terminal Bell Notification': '终端响铃通知', - 'Enable Usage Statistics': '启用使用统计', - Theme: '主题', - 'Preferred Editor': '首选编辑器', - 'Auto-connect to IDE': '自动连接到 IDE', - 'Enable Prompt Completion': '启用提示补全', - 'Debug Keystroke Logging': '调试按键记录', - 'Language: UI': '语言:界面', - 'Language: Model': '语言:模型', - 'Output Format': '输出格式', - 'Hide Window Title': '隐藏窗口标题', - 'Show Status in Title': '在标题中显示状态', - 'Hide Tips': '隐藏提示', - 'Show Line Numbers in Code': '在代码中显示行号', - 'Show Citations': '显示引用', - 'Custom Witty Phrases': '自定义诙谐短语', - 'Show Welcome Back Dialog': '显示欢迎回来对话框', - 'Enable User Feedback': '启用用户反馈', - 'How is Qwen doing this session? (optional)': 'Qwen 这次表现如何?(可选)', - Bad: '不满意', - Fine: '还行', - Good: '满意', - Dismiss: '忽略', - 'Not Sure Yet': '暂不评价', - 'Any other key': '任意其他键', - 'Disable Loading Phrases': '禁用加载短语', - 'Screen Reader Mode': '屏幕阅读器模式', - 'IDE Mode': 'IDE 模式', - 'Max Session Turns': '最大会话轮次', - 'Skip Next Speaker Check': '跳过下一个说话者检查', - 'Skip Loop Detection': '跳过循环检测', - 'Skip Startup Context': '跳过启动上下文', - 'Enable OpenAI Logging': '启用 OpenAI 日志', - 'OpenAI Logging Directory': 'OpenAI 日志目录', - Timeout: '超时', - 'Max Retries': '最大重试次数', - 'Disable Cache Control': '禁用缓存控制', - 'Memory Discovery Max Dirs': '内存发现最大目录数', - 'Load Memory From Include Directories': '从包含目录加载内存', - 'Respect .gitignore': '遵守 .gitignore', - 'Respect .qwenignore': '遵守 .qwenignore', - 'Enable Recursive File Search': '启用递归文件搜索', - 'Disable Fuzzy Search': '禁用模糊搜索', - 'Interactive Shell (PTY)': '交互式 Shell (PTY)', - 'Show Color': '显示颜色', - 'Auto Accept': '自动接受', - 'Use Ripgrep': '使用 Ripgrep', - 'Use Builtin Ripgrep': '使用内置 Ripgrep', - 'Enable Tool Output Truncation': '启用工具输出截断', - 'Tool Output Truncation Threshold': '工具输出截断阈值', - 'Tool Output Truncation Lines': '工具输出截断行数', - 'Folder Trust': '文件夹信任', - 'Vision Model Preview': '视觉模型预览', - 'Tool Schema Compliance': '工具 Schema 兼容性', + "Vim Mode": "Vim 模式", + "Disable Auto Update": "禁用自动更新", + "Attribution: commit": "署名:提交", + "Terminal Bell Notification": "终端响铃通知", + "Enable Usage Statistics": "启用使用统计", + Theme: "主题", + "Preferred Editor": "首选编辑器", + "Auto-connect to IDE": "自动连接到 IDE", + "Enable Prompt Completion": "启用提示补全", + "Debug Keystroke Logging": "调试按键记录", + "Language: UI": "语言:界面", + "Language: Model": "语言:模型", + "Output Format": "输出格式", + "Hide Window Title": "隐藏窗口标题", + "Show Status in Title": "在标题中显示状态", + "Hide Tips": "隐藏提示", + "Show Line Numbers in Code": "在代码中显示行号", + "Show Citations": "显示引用", + "Custom Witty Phrases": "自定义诙谐短语", + "Show Welcome Back Dialog": "显示欢迎回来对话框", + "Enable User Feedback": "启用用户反馈", + "How is Qwen doing this session? (optional)": "Qwen 这次表现如何?(可选)", + Bad: "不满意", + Fine: "还行", + Good: "满意", + Dismiss: "忽略", + "Not Sure Yet": "暂不评价", + "Any other key": "任意其他键", + "Disable Loading Phrases": "禁用加载短语", + "Screen Reader Mode": "屏幕阅读器模式", + "IDE Mode": "IDE 模式", + "Max Session Turns": "最大会话轮次", + "Skip Next Speaker Check": "跳过下一个说话者检查", + "Skip Loop Detection": "跳过循环检测", + "Skip Startup Context": "跳过启动上下文", + "Enable OpenAI Logging": "启用 OpenAI 日志", + "OpenAI Logging Directory": "OpenAI 日志目录", + Timeout: "超时", + "Max Retries": "最大重试次数", + "Disable Cache Control": "禁用缓存控制", + "Memory Discovery Max Dirs": "内存发现最大目录数", + "Load Memory From Include Directories": "从包含目录加载内存", + "Respect .gitignore": "遵守 .gitignore", + "Respect .qwenignore": "遵守 .qwenignore", + "Enable Recursive File Search": "启用递归文件搜索", + "Disable Fuzzy Search": "禁用模糊搜索", + "Interactive Shell (PTY)": "交互式 Shell (PTY)", + "Show Color": "显示颜色", + "Auto Accept": "自动接受", + "Use Ripgrep": "使用 Ripgrep", + "Use Builtin Ripgrep": "使用内置 Ripgrep", + "Enable Tool Output Truncation": "启用工具输出截断", + "Tool Output Truncation Threshold": "工具输出截断阈值", + "Tool Output Truncation Lines": "工具输出截断行数", + "Folder Trust": "文件夹信任", + "Vision Model Preview": "视觉模型预览", + "Tool Schema Compliance": "工具 Schema 兼容性", // Settings enum options - 'Auto (detect from system)': '自动(从系统检测)', - Text: '文本', - JSON: 'JSON', - Plan: '规划', - Default: '默认', - 'Auto Edit': '自动编辑', - YOLO: 'YOLO', - 'toggle vim mode on/off': '切换 vim 模式开关', - 'check session stats. Usage: /stats [model|tools]': - '检查会话统计信息。用法:/stats [model|tools]', - 'Show model-specific usage statistics.': '显示模型相关的使用统计信息', - 'Show tool-specific usage statistics.': '显示工具相关的使用统计信息', - 'exit the cli': '退出命令行界面', - 'Open MCP management dialog, or authenticate with OAuth-enabled servers': - '打开 MCP 管理对话框,或在支持 OAuth 的服务器上进行身份验证', - 'List configured MCP servers and tools, or authenticate with OAuth-enabled servers': - '列出已配置的 MCP 服务器和工具,或使用支持 OAuth 的服务器进行身份验证', - 'Manage workspace directories': '管理工作区目录', - 'Add directories to the workspace. Use comma to separate multiple paths': - '将目录添加到工作区。使用逗号分隔多个路径', - 'Show all directories in the workspace': '显示工作区中的所有目录', - 'set external editor preference': '设置外部编辑器首选项', - 'Select Editor': '选择编辑器', - 'Editor Preference': '编辑器首选项', - 'These editors are currently supported. Please note that some editors cannot be used in sandbox mode.': - '当前支持以下编辑器。请注意,某些编辑器无法在沙箱模式下使用。', - 'Your preferred editor is:': '您的首选编辑器是:', - 'Manage extensions': '管理扩展', - 'Manage installed extensions': '管理已安装的扩展', - 'List active extensions': '列出活动扩展', - 'Update extensions. Usage: update |--all': - '更新扩展。用法:update |--all', - 'Disable an extension': '禁用扩展', - 'Enable an extension': '启用扩展', - 'Install an extension from a git repo or local path': - '从 Git 仓库或本地路径安装扩展', - 'Uninstall an extension': '卸载扩展', - 'No extensions installed.': '未安装扩展。', - 'Usage: /extensions update |--all': - '用法:/extensions update <扩展名>|--all', + "Auto (detect from system)": "自动(从系统检测)", + Text: "文本", + JSON: "JSON", + Plan: "规划", + Default: "默认", + "Auto Edit": "自动编辑", + YOLO: "YOLO", + "toggle vim mode on/off": "切换 vim 模式开关", + "check session stats. Usage: /stats [model|tools]": + "检查会话统计信息。用法:/stats [model|tools]", + "Show model-specific usage statistics.": "显示模型相关的使用统计信息", + "Show tool-specific usage statistics.": "显示工具相关的使用统计信息", + "exit the cli": "退出命令行界面", + "Open MCP management dialog, or authenticate with OAuth-enabled servers": + "打开 MCP 管理对话框,或在支持 OAuth 的服务器上进行身份验证", + "List configured MCP servers and tools, or authenticate with OAuth-enabled servers": + "列出已配置的 MCP 服务器和工具,或使用支持 OAuth 的服务器进行身份验证", + "Manage workspace directories": "管理工作区目录", + "Add directories to the workspace. Use comma to separate multiple paths": + "将目录添加到工作区。使用逗号分隔多个路径", + "Show all directories in the workspace": "显示工作区中的所有目录", + "set external editor preference": "设置外部编辑器首选项", + "Select Editor": "选择编辑器", + "Editor Preference": "编辑器首选项", + "These editors are currently supported. Please note that some editors cannot be used in sandbox mode.": + "当前支持以下编辑器。请注意,某些编辑器无法在沙箱模式下使用。", + "Your preferred editor is:": "您的首选编辑器是:", + "Manage extensions": "管理扩展", + "Manage installed extensions": "管理已安装的扩展", + "List active extensions": "列出活动扩展", + "Update extensions. Usage: update |--all": + "更新扩展。用法:update |--all", + "Disable an extension": "禁用扩展", + "Enable an extension": "启用扩展", + "Install an extension from a git repo or local path": "从 Git 仓库或本地路径安装扩展", + "Uninstall an extension": "卸载扩展", + "No extensions installed.": "未安装扩展。", + "Usage: /extensions update |--all": "用法:/extensions update <扩展名>|--all", 'Extension "{{name}}" not found.': '未找到扩展 "{{name}}"。', - 'No extensions to update.': '没有可更新的扩展。', - 'Usage: /extensions install ': '用法:/extensions install <来源>', - 'Installing extension from "{{source}}"...': - '正在从 "{{source}}" 安装扩展...', + "No extensions to update.": "没有可更新的扩展。", + "Usage: /extensions install ": "用法:/extensions install <来源>", + 'Installing extension from "{{source}}"...': '正在从 "{{source}}" 安装扩展...', 'Extension "{{name}}" installed successfully.': '扩展 "{{name}}" 安装成功。', 'Failed to install extension from "{{source}}": {{error}}': '从 "{{source}}" 安装扩展失败:{{error}}', - 'Usage: /extensions uninstall ': - '用法:/extensions uninstall <扩展名>', + "Usage: /extensions uninstall ": "用法:/extensions uninstall <扩展名>", 'Uninstalling extension "{{name}}"...': '正在卸载扩展 "{{name}}"...', - 'Extension "{{name}}" uninstalled successfully.': - '扩展 "{{name}}" 卸载成功。', - 'Failed to uninstall extension "{{name}}": {{error}}': - '卸载扩展 "{{name}}" 失败:{{error}}', - 'Usage: /extensions {{command}} [--scope=]': - '用法:/extensions {{command}} <扩展> [--scope=]', + 'Extension "{{name}}" uninstalled successfully.': '扩展 "{{name}}" 卸载成功。', + 'Failed to uninstall extension "{{name}}": {{error}}': '卸载扩展 "{{name}}" 失败:{{error}}', + "Usage: /extensions {{command}} [--scope=]": + "用法:/extensions {{command}} <扩展> [--scope=]", 'Unsupported scope "{{scope}}", should be one of "user" or "workspace"': '不支持的作用域 "{{scope}}",应为 "user" 或 "workspace"', 'Extension "{{name}}" disabled for scope "{{scope}}"': '扩展 "{{name}}" 已在作用域 "{{scope}}" 中禁用', 'Extension "{{name}}" enabled for scope "{{scope}}"': '扩展 "{{name}}" 已在作用域 "{{scope}}" 中启用', - 'Do you want to continue? [Y/n]: ': '是否继续?[Y/n]:', - 'Do you want to continue?': '是否继续?', + "Do you want to continue? [Y/n]: ": "是否继续?[Y/n]:", + "Do you want to continue?": "是否继续?", 'Installing extension "{{name}}".': '正在安装扩展 "{{name}}"。', - '**Extensions may introduce unexpected behavior. Ensure you have investigated the extension source and trust the author.**': - '**扩展可能会引入意外行为。请确保您已调查过扩展源并信任作者。**', - 'This extension will run the following MCP servers:': - '此扩展将运行以下 MCP 服务器:', - local: '本地', - remote: '远程', - 'This extension will add the following commands: {{commands}}.': - '此扩展将添加以下命令:{{commands}}。', - 'This extension will append info to your QWEN.md context using {{fileName}}': - '此扩展将使用 {{fileName}} 向您的 QWEN.md 上下文追加信息', - 'This extension will exclude the following core tools: {{tools}}': - '此扩展将排除以下核心工具:{{tools}}', - 'This extension will install the following skills:': '此扩展将安装以下技能:', - 'This extension will install the following subagents:': - '此扩展将安装以下子智能体:', + "**Extensions may introduce unexpected behavior. Ensure you have investigated the extension source and trust the author.**": + "**扩展可能会引入意外行为。请确保您已调查过扩展源并信任作者。**", + "This extension will run the following MCP servers:": "此扩展将运行以下 MCP 服务器:", + local: "本地", + remote: "远程", + "This extension will add the following commands: {{commands}}.": + "此扩展将添加以下命令:{{commands}}。", + "This extension will append info to your QWEN.md context using {{fileName}}": + "此扩展将使用 {{fileName}} 向您的 QWEN.md 上下文追加信息", + "This extension will exclude the following core tools: {{tools}}": + "此扩展将排除以下核心工具:{{tools}}", + "This extension will install the following skills:": "此扩展将安装以下技能:", + "This extension will install the following subagents:": "此扩展将安装以下子智能体:", 'Installation cancelled for "{{name}}".': '已取消安装 "{{name}}"。', - 'You are installing an extension from {{originSource}}. Some features may not work perfectly with Qwen Code.': - '您正在安装来自 {{originSource}} 的扩展。某些功能可能无法完美兼容 Qwen Code。', - '--ref and --auto-update are not applicable for marketplace extensions.': - '--ref 和 --auto-update 不适用于市场扩展。', - 'Extension "{{name}}" installed successfully and enabled.': - '扩展 "{{name}}" 安装成功并已启用。', - 'Installs an extension from a git repository URL, local path, or claude marketplace (marketplace-url:plugin-name).': - '从 Git 仓库 URL、本地路径或 Claude 市场(marketplace-url:plugin-name)安装扩展。', - 'The github URL, local path, or marketplace source (marketplace-url:plugin-name) of the extension to install.': - '要安装的扩展的 GitHub URL、本地路径或市场源(marketplace-url:plugin-name)。', - 'The git ref to install from.': '要安装的 Git 引用。', - 'Enable auto-update for this extension.': '为此扩展启用自动更新。', - 'Enable pre-release versions for this extension.': '为此扩展启用预发布版本。', - 'Acknowledge the security risks of installing an extension and skip the confirmation prompt.': - '确认安装扩展的安全风险并跳过确认提示。', - 'The source argument must be provided.': '必须提供来源参数。', - 'Extension "{{name}}" successfully uninstalled.': - '扩展 "{{name}}" 卸载成功。', - 'Uninstalls an extension.': '卸载扩展。', - 'The name or source path of the extension to uninstall.': - '要卸载的扩展的名称或源路径。', - 'Please include the name of the extension to uninstall as a positional argument.': - '请将要卸载的扩展名称作为位置参数。', - 'Enables an extension.': '启用扩展。', - 'The name of the extension to enable.': '要启用的扩展名称。', - 'The scope to enable the extenison in. If not set, will be enabled in all scopes.': - '启用扩展的作用域。如果未设置,将在所有作用域中启用。', + "You are installing an extension from {{originSource}}. Some features may not work perfectly with Qwen Code.": + "您正在安装来自 {{originSource}} 的扩展。某些功能可能无法完美兼容 Qwen Code。", + "--ref and --auto-update are not applicable for marketplace extensions.": + "--ref 和 --auto-update 不适用于市场扩展。", + 'Extension "{{name}}" installed successfully and enabled.': '扩展 "{{name}}" 安装成功并已启用。', + "Installs an extension from a git repository URL, local path, or claude marketplace (marketplace-url:plugin-name).": + "从 Git 仓库 URL、本地路径或 Claude 市场(marketplace-url:plugin-name)安装扩展。", + "The github URL, local path, or marketplace source (marketplace-url:plugin-name) of the extension to install.": + "要安装的扩展的 GitHub URL、本地路径或市场源(marketplace-url:plugin-name)。", + "The git ref to install from.": "要安装的 Git 引用。", + "Enable auto-update for this extension.": "为此扩展启用自动更新。", + "Enable pre-release versions for this extension.": "为此扩展启用预发布版本。", + "Acknowledge the security risks of installing an extension and skip the confirmation prompt.": + "确认安装扩展的安全风险并跳过确认提示。", + "The source argument must be provided.": "必须提供来源参数。", + 'Extension "{{name}}" successfully uninstalled.': '扩展 "{{name}}" 卸载成功。', + "Uninstalls an extension.": "卸载扩展。", + "The name or source path of the extension to uninstall.": "要卸载的扩展的名称或源路径。", + "Please include the name of the extension to uninstall as a positional argument.": + "请将要卸载的扩展名称作为位置参数。", + "Enables an extension.": "启用扩展。", + "The name of the extension to enable.": "要启用的扩展名称。", + "The scope to enable the extenison in. If not set, will be enabled in all scopes.": + "启用扩展的作用域。如果未设置,将在所有作用域中启用。", 'Extension "{{name}}" successfully enabled for scope "{{scope}}".': '扩展 "{{name}}" 已在作用域 "{{scope}}" 中启用。', 'Extension "{{name}}" successfully enabled in all scopes.': '扩展 "{{name}}" 已在所有作用域中启用。', - 'Invalid scope: {{scope}}. Please use one of {{scopes}}.': - '无效的作用域:{{scope}}。请使用 {{scopes}} 之一。', - 'Disables an extension.': '禁用扩展。', - 'The name of the extension to disable.': '要禁用的扩展名称。', - 'The scope to disable the extenison in.': '禁用扩展的作用域。', + "Invalid scope: {{scope}}. Please use one of {{scopes}}.": + "无效的作用域:{{scope}}。请使用 {{scopes}} 之一。", + "Disables an extension.": "禁用扩展。", + "The name of the extension to disable.": "要禁用的扩展名称。", + "The scope to disable the extenison in.": "禁用扩展的作用域。", 'Extension "{{name}}" successfully disabled for scope "{{scope}}".': '扩展 "{{name}}" 已在作用域 "{{scope}}" 中禁用。', 'Extension "{{name}}" successfully updated: {{oldVersion}} → {{newVersion}}.': '扩展 "{{name}}" 更新成功:{{oldVersion}} → {{newVersion}}。', 'Unable to install extension "{{name}}" due to missing install metadata': '由于缺少安装元数据,无法安装扩展 "{{name}}"', - 'Extension "{{name}}" is already up to date.': - '扩展 "{{name}}" 已是最新版本。', - 'Updates all extensions or a named extension to the latest version.': - '将所有扩展或指定扩展更新到最新版本。', - 'The name of the extension to update.': '要更新的扩展名称。', - 'Update all extensions.': '更新所有扩展。', - 'Either an extension name or --all must be provided': - '必须提供扩展名称或 --all', - 'Lists installed extensions.': '列出已安装的扩展。', - 'Path:': '路径:', - 'Source:': '来源:', - 'Type:': '类型:', - 'Ref:': '引用:', - 'Release tag:': '发布标签:', - 'Enabled (User):': '已启用(用户):', - 'Enabled (Workspace):': '已启用(工作区):', - 'Context files:': '上下文文件:', - 'Skills:': '技能:', - 'Agents:': '智能体:', - 'MCP servers:': 'MCP 服务器:', - 'Link extension failed to install.': '链接扩展安装失败。', - 'Extension "{{name}}" linked successfully and enabled.': - '扩展 "{{name}}" 链接成功并已启用。', - 'Links an extension from a local path. Updates made to the local path will always be reflected.': - '从本地路径链接扩展。对本地路径的更新将始终反映。', - 'The name of the extension to link.': '要链接的扩展名称。', - 'Set a specific setting for an extension.': '为扩展设置特定配置。', - 'Name of the extension to configure.': '要配置的扩展名称。', - 'The setting to configure (name or env var).': - '要配置的设置(名称或环境变量)。', - 'The scope to set the setting in.': '设置配置的作用域。', - 'List all settings for an extension.': '列出扩展的所有设置。', - 'Name of the extension.': '扩展名称。', - 'Extension "{{name}}" has no settings to configure.': - '扩展 "{{name}}" 没有可配置的设置。', + 'Extension "{{name}}" is already up to date.': '扩展 "{{name}}" 已是最新版本。', + "Updates all extensions or a named extension to the latest version.": + "将所有扩展或指定扩展更新到最新版本。", + "The name of the extension to update.": "要更新的扩展名称。", + "Update all extensions.": "更新所有扩展。", + "Either an extension name or --all must be provided": "必须提供扩展名称或 --all", + "Lists installed extensions.": "列出已安装的扩展。", + "Path:": "路径:", + "Source:": "来源:", + "Type:": "类型:", + "Ref:": "引用:", + "Release tag:": "发布标签:", + "Enabled (User):": "已启用(用户):", + "Enabled (Workspace):": "已启用(工作区):", + "Context files:": "上下文文件:", + "Skills:": "技能:", + "Agents:": "智能体:", + "MCP servers:": "MCP 服务器:", + "Link extension failed to install.": "链接扩展安装失败。", + 'Extension "{{name}}" linked successfully and enabled.': '扩展 "{{name}}" 链接成功并已启用。', + "Links an extension from a local path. Updates made to the local path will always be reflected.": + "从本地路径链接扩展。对本地路径的更新将始终反映。", + "The name of the extension to link.": "要链接的扩展名称。", + "Set a specific setting for an extension.": "为扩展设置特定配置。", + "Name of the extension to configure.": "要配置的扩展名称。", + "The setting to configure (name or env var).": "要配置的设置(名称或环境变量)。", + "The scope to set the setting in.": "设置配置的作用域。", + "List all settings for an extension.": "列出扩展的所有设置。", + "Name of the extension.": "扩展名称。", + 'Extension "{{name}}" has no settings to configure.': '扩展 "{{name}}" 没有可配置的设置。', 'Settings for "{{name}}":': '"{{name}}" 的设置:', - '(workspace)': '(工作区)', - '(user)': '(用户)', - '[not set]': '[未设置]', - '[value stored in keychain]': '[值存储在钥匙串中]', - 'Manage extension settings.': '管理扩展设置。', - 'You need to specify a command (set or list).': - '您需要指定命令(set 或 list)。', + "(workspace)": "(工作区)", + "(user)": "(用户)", + "[not set]": "[未设置]", + "[value stored in keychain]": "[值存储在钥匙串中]", + "Manage extension settings.": "管理扩展设置。", + "You need to specify a command (set or list).": "您需要指定命令(set 或 list)。", // ============================================================================ // Plugin Choice / Marketplace // ============================================================================ - 'No plugins available in this marketplace.': '此市场中没有可用的插件。', + "No plugins available in this marketplace.": "此市场中没有可用的插件。", 'Select a plugin to install from marketplace "{{name}}":': '从市场 "{{name}}" 中选择要安装的插件:', - 'Plugin selection cancelled.': '插件选择已取消。', + "Plugin selection cancelled.": "插件选择已取消。", 'Select a plugin from "{{name}}"': '从 "{{name}}" 中选择插件', - 'Use ↑↓ or j/k to navigate, Enter to select, Escape to cancel': - '使用 ↑↓ 或 j/k 导航,回车选择,Esc 取消', - '{{count}} more above': '上方还有 {{count}} 项', - '{{count}} more below': '下方还有 {{count}} 项', - 'manage IDE integration': '管理 IDE 集成', - 'check status of IDE integration': '检查 IDE 集成状态', - 'install required IDE companion for {{ideName}}': - '安装 {{ideName}} 所需的 IDE 配套工具', - 'enable IDE integration': '启用 IDE 集成', - 'disable IDE integration': '禁用 IDE 集成', - '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.': - '您当前环境不支持 IDE 集成。要使用此功能,请在以下支持的 IDE 之一中运行 Qwen Code:VS Code 或 VS Code 分支版本。', - 'Set up GitHub Actions': '设置 GitHub Actions', - 'Configure terminal keybindings for multiline input (VS Code, Cursor, Windsurf, Trae)': - '配置终端按键绑定以支持多行输入(VS Code、Cursor、Windsurf、Trae)', - 'Please restart your terminal for the changes to take effect.': - '请重启终端以使更改生效。', - 'Failed to configure terminal: {{error}}': '配置终端失败:{{error}}', - 'Could not determine {{terminalName}} config path on Windows: APPDATA environment variable is not set.': - '无法确定 {{terminalName}} 在 Windows 上的配置路径:未设置 APPDATA 环境变量。', - '{{terminalName}} keybindings.json exists but is not a valid JSON array. Please fix the file manually or delete it to allow automatic configuration.': - '{{terminalName}} keybindings.json 存在但不是有效的 JSON 数组。请手动修复文件或删除它以允许自动配置。', - 'File: {{file}}': '文件:{{file}}', - 'Failed to parse {{terminalName}} keybindings.json. The file contains invalid JSON. Please fix the file manually or delete it to allow automatic configuration.': - '解析 {{terminalName}} keybindings.json 失败。文件包含无效的 JSON。请手动修复文件或删除它以允许自动配置。', - 'Error: {{error}}': '错误:{{error}}', - 'Shift+Enter binding already exists': 'Shift+Enter 绑定已存在', - 'Ctrl+Enter binding already exists': 'Ctrl+Enter 绑定已存在', - 'Existing keybindings detected. Will not modify to avoid conflicts.': - '检测到现有按键绑定。为避免冲突,不会修改。', - 'Please check and modify manually if needed: {{file}}': - '如有需要,请手动检查并修改:{{file}}', - 'Added Shift+Enter and Ctrl+Enter keybindings to {{terminalName}}.': - '已为 {{terminalName}} 添加 Shift+Enter 和 Ctrl+Enter 按键绑定。', - 'Modified: {{file}}': '已修改:{{file}}', - '{{terminalName}} keybindings already configured.': - '{{terminalName}} 按键绑定已配置。', - 'Failed to configure {{terminalName}}.': '配置 {{terminalName}} 失败。', - 'Your terminal is already configured for an optimal experience with multiline input (Shift+Enter and Ctrl+Enter).': - '您的终端已配置为支持多行输入(Shift+Enter 和 Ctrl+Enter)的最佳体验。', + "Use ↑↓ or j/k to navigate, Enter to select, Escape to cancel": + "使用 ↑↓ 或 j/k 导航,回车选择,Esc 取消", + "{{count}} more above": "上方还有 {{count}} 项", + "{{count}} more below": "下方还有 {{count}} 项", + "manage IDE integration": "管理 IDE 集成", + "check status of IDE integration": "检查 IDE 集成状态", + "install required IDE companion for {{ideName}}": "安装 {{ideName}} 所需的 IDE 配套工具", + "enable IDE integration": "启用 IDE 集成", + "disable IDE integration": "禁用 IDE 集成", + "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.": + "您当前环境不支持 IDE 集成。要使用此功能,请在以下支持的 IDE 之一中运行 Qwen Code:VS Code 或 VS Code 分支版本。", + "Set up GitHub Actions": "设置 GitHub Actions", + "Configure terminal keybindings for multiline input (VS Code, Cursor, Windsurf, Trae)": + "配置终端按键绑定以支持多行输入(VS Code、Cursor、Windsurf、Trae)", + "Please restart your terminal for the changes to take effect.": "请重启终端以使更改生效。", + "Failed to configure terminal: {{error}}": "配置终端失败:{{error}}", + "Could not determine {{terminalName}} config path on Windows: APPDATA environment variable is not set.": + "无法确定 {{terminalName}} 在 Windows 上的配置路径:未设置 APPDATA 环境变量。", + "{{terminalName}} keybindings.json exists but is not a valid JSON array. Please fix the file manually or delete it to allow automatic configuration.": + "{{terminalName}} keybindings.json 存在但不是有效的 JSON 数组。请手动修复文件或删除它以允许自动配置。", + "File: {{file}}": "文件:{{file}}", + "Failed to parse {{terminalName}} keybindings.json. The file contains invalid JSON. Please fix the file manually or delete it to allow automatic configuration.": + "解析 {{terminalName}} keybindings.json 失败。文件包含无效的 JSON。请手动修复文件或删除它以允许自动配置。", + "Error: {{error}}": "错误:{{error}}", + "Shift+Enter binding already exists": "Shift+Enter 绑定已存在", + "Ctrl+Enter binding already exists": "Ctrl+Enter 绑定已存在", + "Existing keybindings detected. Will not modify to avoid conflicts.": + "检测到现有按键绑定。为避免冲突,不会修改。", + "Please check and modify manually if needed: {{file}}": "如有需要,请手动检查并修改:{{file}}", + "Added Shift+Enter and Ctrl+Enter keybindings to {{terminalName}}.": + "已为 {{terminalName}} 添加 Shift+Enter 和 Ctrl+Enter 按键绑定。", + "Modified: {{file}}": "已修改:{{file}}", + "{{terminalName}} keybindings already configured.": "{{terminalName}} 按键绑定已配置。", + "Failed to configure {{terminalName}}.": "配置 {{terminalName}} 失败。", + "Your terminal is already configured for an optimal experience with multiline input (Shift+Enter and Ctrl+Enter).": + "您的终端已配置为支持多行输入(Shift+Enter 和 Ctrl+Enter)的最佳体验。", // ============================================================================ // Commands - Hooks // ============================================================================ - 'Manage Qwen Code hooks': '管理 Qwen Code Hook', - 'List all configured hooks': '列出所有已配置的 Hook', - 'Enable a disabled hook': '启用已禁用的 Hook', - 'Disable an active hook': '禁用已启用的 Hook', + "Manage Qwen Code hooks": "管理 Qwen Code Hook", + "List all configured hooks": "列出所有已配置的 Hook", + "Enable a disabled hook": "启用已禁用的 Hook", + "Disable an active hook": "禁用已启用的 Hook", // Hooks - Dialog - Hooks: 'Hook', - 'Loading hooks...': '正在加载 Hook...', - 'Error loading hooks:': '加载 Hook 出错:', - 'Press Escape to close': '按 Escape 关闭', - 'Press Escape, Ctrl+C, or Ctrl+D to cancel': - '按 Escape、Ctrl+C 或 Ctrl+D 取消', - 'Press Space, Enter, or Escape to dismiss': '按空格、回车或 Escape 关闭', - 'No hook selected': '未选择 Hook', + Hooks: "Hook", + "Loading hooks...": "正在加载 Hook...", + "Error loading hooks:": "加载 Hook 出错:", + "Press Escape to close": "按 Escape 关闭", + "Press Escape, Ctrl+C, or Ctrl+D to cancel": "按 Escape、Ctrl+C 或 Ctrl+D 取消", + "Press Space, Enter, or Escape to dismiss": "按空格、回车或 Escape 关闭", + "No hook selected": "未选择 Hook", // Hooks - List Step - 'No hook events found.': '未找到 Hook 事件。', - '{{count}} hook configured': '{{count}} 个 Hook 已配置', - '{{count}} hooks configured': '{{count}} 个 Hook 已配置', - 'This menu is read-only. To add or modify hooks, edit settings.json directly or ask Qwen Code.': - '此菜单为只读。要添加或修改 Hook,请直接编辑 settings.json 或询问 Qwen Code。', - 'Enter to select · Esc to cancel': 'Enter 选择 · Esc 取消', + "No hook events found.": "未找到 Hook 事件。", + "{{count}} hook configured": "{{count}} 个 Hook 已配置", + "{{count}} hooks configured": "{{count}} 个 Hook 已配置", + "This menu is read-only. To add or modify hooks, edit settings.json directly or ask Qwen Code.": + "此菜单为只读。要添加或修改 Hook,请直接编辑 settings.json 或询问 Qwen Code。", + "Enter to select · Esc to cancel": "Enter 选择 · Esc 取消", // Hooks - Detail Step - 'Exit codes:': '退出码:', - 'Configured hooks:': '已配置的 Hook:', - 'No hooks configured for this event.': '此事件未配置 Hook。', - 'To add hooks, edit settings.json directly or ask Qwen.': - '要添加 Hook,请直接编辑 settings.json 或询问 Qwen。', - 'Enter to select · Esc to go back': 'Enter 选择 · Esc 返回', + "Exit codes:": "退出码:", + "Configured hooks:": "已配置的 Hook:", + "No hooks configured for this event.": "此事件未配置 Hook。", + "To add hooks, edit settings.json directly or ask Qwen.": + "要添加 Hook,请直接编辑 settings.json 或询问 Qwen。", + "Enter to select · Esc to go back": "Enter 选择 · Esc 返回", // Hooks - Config Detail Step - 'Hook details': 'Hook 详情', - 'Event:': '事件:', - 'Extension:': '扩展:', - 'Desc:': '描述:', - 'No hook config selected': '未选择 Hook 配置', - 'To modify or remove this hook, edit settings.json directly or ask Qwen to help.': - '要修改或删除此 Hook,请直接编辑 settings.json 或询问 Qwen。', + "Hook details": "Hook 详情", + "Event:": "事件:", + "Extension:": "扩展:", + "Desc:": "描述:", + "No hook config selected": "未选择 Hook 配置", + "To modify or remove this hook, edit settings.json directly or ask Qwen to help.": + "要修改或删除此 Hook,请直接编辑 settings.json 或询问 Qwen。", // Hooks - Disabled Step - 'Hook Configuration - Disabled': 'Hook 配置 - 已禁用', - 'All hooks are currently disabled. You have {{count}} that are not running.': - '所有 Hook 当前已禁用。您有 {{count}} 未运行。', - '{{count}} configured hook': '{{count}} 个已配置的 Hook', - '{{count}} configured hooks': '{{count}} 个已配置的 Hook', - 'When hooks are disabled:': '当 Hook 被禁用时:', - 'No hook commands will execute': '不会执行任何 Hook 命令', - 'StatusLine will not be displayed': '不会显示状态栏', - 'Tool operations will proceed without hook validation': - '工具操作将在没有 Hook 验证的情况下继续', + "Hook Configuration - Disabled": "Hook 配置 - 已禁用", + "All hooks are currently disabled. You have {{count}} that are not running.": + "所有 Hook 当前已禁用。您有 {{count}} 未运行。", + "{{count}} configured hook": "{{count}} 个已配置的 Hook", + "{{count}} configured hooks": "{{count}} 个已配置的 Hook", + "When hooks are disabled:": "当 Hook 被禁用时:", + "No hook commands will execute": "不会执行任何 Hook 命令", + "StatusLine will not be displayed": "不会显示状态栏", + "Tool operations will proceed without hook validation": "工具操作将在没有 Hook 验证的情况下继续", 'To re-enable hooks, remove "disableAllHooks" from settings.json or ask Qwen Code.': '要重新启用 Hook,请从 settings.json 中删除 "disableAllHooks" 或询问 Qwen Code。', // Hooks - Source - Project: '项目', - User: '用户', - System: '系统', - Extension: '扩展', - 'Local Settings': '本地设置', - 'User Settings': '用户设置', - 'System Settings': '系统设置', - Extensions: '扩展', + Project: "项目", + User: "用户", + System: "系统", + Extension: "扩展", + "Local Settings": "本地设置", + "User Settings": "用户设置", + "System Settings": "系统设置", + Extensions: "扩展", // Hooks - Status - '✓ Enabled': '✓ 已启用', - '✗ Disabled': '✗ 已禁用', + "✓ Enabled": "✓ 已启用", + "✗ Disabled": "✗ 已禁用", // Hooks - Event Descriptions (short) - 'Before tool execution': '工具执行前', - 'After tool execution': '工具执行后', - 'After tool execution fails': '工具执行失败后', - 'When notifications are sent': '发送通知时', - 'When the user submits a prompt': '用户提交提示时', - 'When a new session is started': '新会话开始时', - 'Right before Qwen Code concludes its response': 'Qwen Code 结束响应之前', - 'When a subagent (Agent tool call) is started': - '子智能体(Agent 工具调用)启动时', - 'Right before a subagent concludes its response': '子智能体结束响应之前', - 'Before conversation compaction': '对话压缩前', - 'When a session is ending': '会话结束时', - 'When a permission dialog is displayed': '显示权限对话框时', + "Before tool execution": "工具执行前", + "After tool execution": "工具执行后", + "After tool execution fails": "工具执行失败后", + "When notifications are sent": "发送通知时", + "When the user submits a prompt": "用户提交提示时", + "When a new session is started": "新会话开始时", + "Right before Qwen Code concludes its response": "Qwen Code 结束响应之前", + "When a subagent (Agent tool call) is started": "子智能体(Agent 工具调用)启动时", + "Right before a subagent concludes its response": "子智能体结束响应之前", + "Before conversation compaction": "对话压缩前", + "When a session is ending": "会话结束时", + "When a permission dialog is displayed": "显示权限对话框时", // Hooks - Event Descriptions (detailed) - 'Input to command is JSON of tool call arguments.': - '命令输入为工具调用参数的 JSON。', + "Input to command is JSON of tool call arguments.": "命令输入为工具调用参数的 JSON。", 'Input to command is JSON with fields "inputs" (tool call arguments) and "response" (tool call response).': '命令输入为包含 "inputs"(工具调用参数)和 "response"(工具调用响应)字段的 JSON。', - 'Input to command is JSON with tool_name, tool_input, tool_use_id, error, error_type, is_interrupt, and is_timeout.': - '命令输入为包含 tool_name、tool_input、tool_use_id、error、error_type、is_interrupt 和 is_timeout 的 JSON。', - 'Input to command is JSON with notification message and type.': - '命令输入为包含通知消息和类型的 JSON。', - 'Input to command is JSON with original user prompt text.': - '命令输入为包含原始用户提示文本的 JSON。', - 'Input to command is JSON with session start source.': - '命令输入为包含会话启动来源的 JSON。', - 'Input to command is JSON with session end reason.': - '命令输入为包含会话结束原因的 JSON。', - 'Input to command is JSON with agent_id and agent_type.': - '命令输入为包含 agent_id 和 agent_type 的 JSON。', - 'Input to command is JSON with agent_id, agent_type, and agent_transcript_path.': - '命令输入为包含 agent_id、agent_type 和 agent_transcript_path 的 JSON。', - 'Input to command is JSON with compaction details.': - '命令输入为包含压缩详情的 JSON。', - 'Input to command is JSON with tool_name, tool_input, and tool_use_id. Output JSON with hookSpecificOutput containing decision to allow or deny.': - '命令输入为包含 tool_name、tool_input 和 tool_use_id 的 JSON。输出包含 hookSpecificOutput 的 JSON,其中包含允许或拒绝的决定。', + "Input to command is JSON with tool_name, tool_input, tool_use_id, error, error_type, is_interrupt, and is_timeout.": + "命令输入为包含 tool_name、tool_input、tool_use_id、error、error_type、is_interrupt 和 is_timeout 的 JSON。", + "Input to command is JSON with notification message and type.": + "命令输入为包含通知消息和类型的 JSON。", + "Input to command is JSON with original user prompt text.": + "命令输入为包含原始用户提示文本的 JSON。", + "Input to command is JSON with session start source.": "命令输入为包含会话启动来源的 JSON。", + "Input to command is JSON with session end reason.": "命令输入为包含会话结束原因的 JSON。", + "Input to command is JSON with agent_id and agent_type.": + "命令输入为包含 agent_id 和 agent_type 的 JSON。", + "Input to command is JSON with agent_id, agent_type, and agent_transcript_path.": + "命令输入为包含 agent_id、agent_type 和 agent_transcript_path 的 JSON。", + "Input to command is JSON with compaction details.": "命令输入为包含压缩详情的 JSON。", + "Input to command is JSON with tool_name, tool_input, and tool_use_id. Output JSON with hookSpecificOutput containing decision to allow or deny.": + "命令输入为包含 tool_name、tool_input 和 tool_use_id 的 JSON。输出包含 hookSpecificOutput 的 JSON,其中包含允许或拒绝的决定。", // Hooks - Exit Code Descriptions - 'stdout/stderr not shown': 'stdout/stderr 不显示', - 'show stderr to model and continue conversation': - '向模型显示 stderr 并继续对话', - 'show stderr to user only': '仅向用户显示 stderr', - 'stdout shown in transcript mode (ctrl+o)': 'stdout 以转录模式显示 (ctrl+o)', - 'show stderr to model immediately': '立即向模型显示 stderr', - 'show stderr to user only but continue with tool call': - '仅向用户显示 stderr 但继续工具调用', - 'block processing, erase original prompt, and show stderr to user only': - '阻止处理,擦除原始提示,仅向用户显示 stderr', - 'stdout shown to Qwen': '向 Qwen 显示 stdout', - 'show stderr to user only (blocking errors ignored)': - '仅向用户显示 stderr(忽略阻塞错误)', - 'command completes successfully': '命令成功完成', - 'stdout shown to subagent': '向子智能体显示 stdout', - 'show stderr to subagent and continue having it run': - '向子智能体显示 stderr 并继续运行', - 'stdout appended as custom compact instructions': - 'stdout 作为自定义压缩指令追加', - 'block compaction': '阻止压缩', - 'show stderr to user only but continue with compaction': - '仅向用户显示 stderr 但继续压缩', - 'use hook decision if provided': '如果提供则使用 Hook 决定', + "stdout/stderr not shown": "stdout/stderr 不显示", + "show stderr to model and continue conversation": "向模型显示 stderr 并继续对话", + "show stderr to user only": "仅向用户显示 stderr", + "stdout shown in transcript mode (ctrl+o)": "stdout 以转录模式显示 (ctrl+o)", + "show stderr to model immediately": "立即向模型显示 stderr", + "show stderr to user only but continue with tool call": "仅向用户显示 stderr 但继续工具调用", + "block processing, erase original prompt, and show stderr to user only": + "阻止处理,擦除原始提示,仅向用户显示 stderr", + "stdout shown to Qwen": "向 Qwen 显示 stdout", + "show stderr to user only (blocking errors ignored)": "仅向用户显示 stderr(忽略阻塞错误)", + "command completes successfully": "命令成功完成", + "stdout shown to subagent": "向子智能体显示 stdout", + "show stderr to subagent and continue having it run": "向子智能体显示 stderr 并继续运行", + "stdout appended as custom compact instructions": "stdout 作为自定义压缩指令追加", + "block compaction": "阻止压缩", + "show stderr to user only but continue with compaction": "仅向用户显示 stderr 但继续压缩", + "use hook decision if provided": "如果提供则使用 Hook 决定", // Hooks - Messages - 'Config not loaded.': '配置未加载。', - 'Hooks are not enabled. Enable hooks in settings to use this feature.': - 'Hook 未启用。请在设置中启用 Hook 以使用此功能。', - 'No hooks configured. Add hooks in your settings.json file.': - '未配置 Hook。请在 settings.json 文件中添加 Hook。', - 'Configured Hooks ({{count}} total)': '已配置的 Hook(共 {{count}} 个)', + "Config not loaded.": "配置未加载。", + "Hooks are not enabled. Enable hooks in settings to use this feature.": + "Hook 未启用。请在设置中启用 Hook 以使用此功能。", + "No hooks configured. Add hooks in your settings.json file.": + "未配置 Hook。请在 settings.json 文件中添加 Hook。", + "Configured Hooks ({{count}} total)": "已配置的 Hook(共 {{count}} 个)", // ============================================================================ // Commands - Session Export // ============================================================================ - 'Export current session message history to a file': - '将当前会话的消息记录导出到文件', - 'Export session to HTML format': '将会话导出为 HTML 文件', - 'Export session to JSON format': '将会话导出为 JSON 文件', - 'Export session to JSONL format (one message per line)': - '将会话导出为 JSONL 文件(每行一条消息)', - 'Export session to markdown format': '将会话导出为 Markdown 文件', + "Export current session message history to a file": "将当前会话的消息记录导出到文件", + "Export session to HTML format": "将会话导出为 HTML 文件", + "Export session to JSON format": "将会话导出为 JSON 文件", + "Export session to JSONL format (one message per line)": + "将会话导出为 JSONL 文件(每行一条消息)", + "Export session to markdown format": "将会话导出为 Markdown 文件", // ============================================================================ // Commands - Insights // ============================================================================ - 'generate personalized programming insights from your chat history': - '根据你的聊天记录生成个性化编程洞察', + "generate personalized programming insights from your chat history": + "根据你的聊天记录生成个性化编程洞察", // ============================================================================ // Commands - Session History // ============================================================================ - 'Resume a previous session': '恢复先前会话', - 'Restore a tool call. This will reset the conversation and file history to the state it was in when the tool call was suggested': - '恢复某次工具调用。这将把对话与文件历史重置到提出该工具调用建议时的状态', - 'Could not detect terminal type. Supported terminals: VS Code, Cursor, Windsurf, and Trae.': - '无法检测终端类型。支持的终端:VS Code、Cursor、Windsurf 和 Trae。', - 'Terminal "{{terminal}}" is not supported yet.': - '终端 "{{terminal}}" 尚未支持。', + "Resume a previous session": "恢复先前会话", + "Restore a tool call. This will reset the conversation and file history to the state it was in when the tool call was suggested": + "恢复某次工具调用。这将把对话与文件历史重置到提出该工具调用建议时的状态", + "Could not detect terminal type. Supported terminals: VS Code, Cursor, Windsurf, and Trae.": + "无法检测终端类型。支持的终端:VS Code、Cursor、Windsurf 和 Trae。", + 'Terminal "{{terminal}}" is not supported yet.': '终端 "{{terminal}}" 尚未支持。', // ============================================================================ // Commands - Language // ============================================================================ - 'Invalid language. Available: {{options}}': - '无效的语言。可用选项:{{options}}', - 'Language subcommands do not accept additional arguments.': - '语言子命令不接受额外参数', - 'Current UI language: {{lang}}': '当前 UI 语言:{{lang}}', - 'Current LLM output language: {{lang}}': '当前 LLM 输出语言:{{lang}}', - 'LLM output language not set': '未设置 LLM 输出语言', - 'Set UI language': '设置 UI 语言', - 'Set LLM output language': '设置 LLM 输出语言', - 'Usage: /language ui [{{options}}]': '用法:/language ui [{{options}}]', - 'Usage: /language output ': '用法:/language output <语言>', - 'Example: /language output 中文': '示例:/language output 中文', - 'Example: /language output English': '示例:/language output English', - 'Example: /language output 日本語': '示例:/language output 日本語', - 'Example: /language output Português': '示例:/language output Português', - 'UI language changed to {{lang}}': 'UI 语言已更改为 {{lang}}', - 'LLM output language set to {{lang}}': 'LLM 输出语言已设置为 {{lang}}', - 'LLM output language rule file generated at {{path}}': - 'LLM 输出语言规则文件已生成于 {{path}}', - 'Please restart the application for the changes to take effect.': - '请重启应用程序以使更改生效。', - 'Failed to generate LLM output language rule file: {{error}}': - '生成 LLM 输出语言规则文件失败:{{error}}', - 'Invalid command. Available subcommands:': '无效的命令。可用的子命令:', - 'Available subcommands:': '可用的子命令:', - 'To request additional UI language packs, please open an issue on GitHub.': - '如需请求其他 UI 语言包,请在 GitHub 上提交 issue', - 'Available options:': '可用选项:', - 'Set UI language to {{name}}': '将 UI 语言设置为 {{name}}', + "Invalid language. Available: {{options}}": "无效的语言。可用选项:{{options}}", + "Language subcommands do not accept additional arguments.": "语言子命令不接受额外参数", + "Current UI language: {{lang}}": "当前 UI 语言:{{lang}}", + "Current LLM output language: {{lang}}": "当前 LLM 输出语言:{{lang}}", + "LLM output language not set": "未设置 LLM 输出语言", + "Set UI language": "设置 UI 语言", + "Set LLM output language": "设置 LLM 输出语言", + "Usage: /language ui [{{options}}]": "用法:/language ui [{{options}}]", + "Usage: /language output ": "用法:/language output <语言>", + "Example: /language output 中文": "示例:/language output 中文", + "Example: /language output English": "示例:/language output English", + "Example: /language output 日本語": "示例:/language output 日本語", + "Example: /language output Português": "示例:/language output Português", + "UI language changed to {{lang}}": "UI 语言已更改为 {{lang}}", + "LLM output language set to {{lang}}": "LLM 输出语言已设置为 {{lang}}", + "LLM output language rule file generated at {{path}}": "LLM 输出语言规则文件已生成于 {{path}}", + "Please restart the application for the changes to take effect.": "请重启应用程序以使更改生效。", + "Failed to generate LLM output language rule file: {{error}}": + "生成 LLM 输出语言规则文件失败:{{error}}", + "Invalid command. Available subcommands:": "无效的命令。可用的子命令:", + "Available subcommands:": "可用的子命令:", + "To request additional UI language packs, please open an issue on GitHub.": + "如需请求其他 UI 语言包,请在 GitHub 上提交 issue", + "Available options:": "可用选项:", + "Set UI language to {{name}}": "将 UI 语言设置为 {{name}}", // ============================================================================ // Commands - Approval Mode // ============================================================================ - 'Tool Approval Mode': '工具审批模式', - 'Current approval mode: {{mode}}': '当前审批模式:{{mode}}', - 'Available approval modes:': '可用的审批模式:', - 'Approval mode changed to: {{mode}}': '审批模式已更改为:{{mode}}', - 'Approval mode changed to: {{mode}} (saved to {{scope}} settings{{location}})': - '审批模式已更改为:{{mode}}(已保存到{{scope}}设置{{location}})', - 'Usage: /approval-mode [--session|--user|--project]': - '用法:/approval-mode [--session|--user|--project]', + "Tool Approval Mode": "工具审批模式", + "Current approval mode: {{mode}}": "当前审批模式:{{mode}}", + "Available approval modes:": "可用的审批模式:", + "Approval mode changed to: {{mode}}": "审批模式已更改为:{{mode}}", + "Approval mode changed to: {{mode}} (saved to {{scope}} settings{{location}})": + "审批模式已更改为:{{mode}}(已保存到{{scope}}设置{{location}})", + "Usage: /approval-mode [--session|--user|--project]": + "用法:/approval-mode [--session|--user|--project]", - 'Scope subcommands do not accept additional arguments.': - '作用域子命令不接受额外参数', - 'Plan mode - Analyze only, do not modify files or execute commands': - '规划模式 - 仅分析,不修改文件或执行命令', - 'Default mode - Require approval for file edits or shell commands': - '默认模式 - 需要批准文件编辑或 shell 命令', - 'Auto-edit mode - Automatically approve file edits': - '自动编辑模式 - 自动批准文件编辑', - 'YOLO mode - Automatically approve all tools': 'YOLO 模式 - 自动批准所有工具', - '{{mode}} mode': '{{mode}} 模式', - 'Settings service is not available; unable to persist the approval mode.': - '设置服务不可用;无法持久化审批模式。', - 'Failed to save approval mode: {{error}}': '保存审批模式失败:{{error}}', - 'Failed to change approval mode: {{error}}': '更改审批模式失败:{{error}}', - 'Apply to current session only (temporary)': '仅应用于当前会话(临时)', - 'Persist for this project/workspace': '持久化到此项目/工作区', - 'Persist for this user on this machine': '持久化到此机器上的此用户', - 'Analyze only, do not modify files or execute commands': - '仅分析,不修改文件或执行命令', - 'Require approval for file edits or shell commands': - '需要批准文件编辑或 shell 命令', - 'Automatically approve file edits': '自动批准文件编辑', - 'Automatically approve all tools': '自动批准所有工具', - 'Workspace approval mode exists and takes priority. User-level change will have no effect.': - '工作区审批模式已存在并具有优先级。用户级别的更改将无效。', - 'Apply To': '应用于', - 'Workspace Settings': '工作区设置', + "Scope subcommands do not accept additional arguments.": "作用域子命令不接受额外参数", + "Plan mode - Analyze only, do not modify files or execute commands": + "规划模式 - 仅分析,不修改文件或执行命令", + "Default mode - Require approval for file edits or shell commands": + "默认模式 - 需要批准文件编辑或 shell 命令", + "Auto-edit mode - Automatically approve file edits": "自动编辑模式 - 自动批准文件编辑", + "YOLO mode - Automatically approve all tools": "YOLO 模式 - 自动批准所有工具", + "{{mode}} mode": "{{mode}} 模式", + "Settings service is not available; unable to persist the approval mode.": + "设置服务不可用;无法持久化审批模式。", + "Failed to save approval mode: {{error}}": "保存审批模式失败:{{error}}", + "Failed to change approval mode: {{error}}": "更改审批模式失败:{{error}}", + "Apply to current session only (temporary)": "仅应用于当前会话(临时)", + "Persist for this project/workspace": "持久化到此项目/工作区", + "Persist for this user on this machine": "持久化到此机器上的此用户", + "Analyze only, do not modify files or execute commands": "仅分析,不修改文件或执行命令", + "Require approval for file edits or shell commands": "需要批准文件编辑或 shell 命令", + "Automatically approve file edits": "自动批准文件编辑", + "Automatically approve all tools": "自动批准所有工具", + "Workspace approval mode exists and takes priority. User-level change will have no effect.": + "工作区审批模式已存在并具有优先级。用户级别的更改将无效。", + "Apply To": "应用于", + "Workspace Settings": "工作区设置", // ============================================================================ // Commands - Memory // ============================================================================ - 'Commands for interacting with memory.': '用于与记忆交互的命令', - 'Show the current memory contents.': '显示当前记忆内容', - 'Show project-level memory contents.': '显示项目级记忆内容', - 'Show global memory contents.': '显示全局记忆内容', - 'Add content to project-level memory.': '添加内容到项目级记忆', - 'Add content to global memory.': '添加内容到全局记忆', - 'Refresh the memory from the source.': '从源刷新记忆', - 'Usage: /memory add --project ': - '用法:/memory add --project <要记住的文本>', - 'Usage: /memory add --global ': - '用法:/memory add --global <要记住的文本>', - 'Attempting to save to project memory: "{{text}}"': - '正在尝试保存到项目记忆:"{{text}}"', - 'Attempting to save to global memory: "{{text}}"': - '正在尝试保存到全局记忆:"{{text}}"', - 'Current memory content from {{count}} file(s):': - '来自 {{count}} 个文件的当前记忆内容:', - 'Memory is currently empty.': '记忆当前为空', - 'Project memory file not found or is currently empty.': - '项目记忆文件未找到或当前为空', - 'Global memory file not found or is currently empty.': - '全局记忆文件未找到或当前为空', - 'Global memory is currently empty.': '全局记忆当前为空', - 'Global memory content:\n\n---\n{{content}}\n---': - '全局记忆内容:\n\n---\n{{content}}\n---', - 'Project memory content from {{path}}:\n\n---\n{{content}}\n---': - '项目记忆内容来自 {{path}}:\n\n---\n{{content}}\n---', - 'Project memory is currently empty.': '项目记忆当前为空', - 'Refreshing memory from source files...': '正在从源文件刷新记忆...', - 'Add content to the memory. Use --global for global memory or --project for project memory.': - '添加内容到记忆。使用 --global 表示全局记忆,使用 --project 表示项目记忆', - 'Usage: /memory add [--global|--project] ': - '用法:/memory add [--global|--project] <要记住的文本>', - 'Attempting to save to memory {{scope}}: "{{fact}}"': - '正在尝试保存到记忆 {{scope}}:"{{fact}}"', + "Commands for interacting with memory.": "用于与记忆交互的命令", + "Show the current memory contents.": "显示当前记忆内容", + "Show project-level memory contents.": "显示项目级记忆内容", + "Show global memory contents.": "显示全局记忆内容", + "Add content to project-level memory.": "添加内容到项目级记忆", + "Add content to global memory.": "添加内容到全局记忆", + "Refresh the memory from the source.": "从源刷新记忆", + "Usage: /memory add --project ": "用法:/memory add --project <要记住的文本>", + "Usage: /memory add --global ": "用法:/memory add --global <要记住的文本>", + 'Attempting to save to project memory: "{{text}}"': '正在尝试保存到项目记忆:"{{text}}"', + 'Attempting to save to global memory: "{{text}}"': '正在尝试保存到全局记忆:"{{text}}"', + "Current memory content from {{count}} file(s):": "来自 {{count}} 个文件的当前记忆内容:", + "Memory is currently empty.": "记忆当前为空", + "Project memory file not found or is currently empty.": "项目记忆文件未找到或当前为空", + "Global memory file not found or is currently empty.": "全局记忆文件未找到或当前为空", + "Global memory is currently empty.": "全局记忆当前为空", + "Global memory content:\n\n---\n{{content}}\n---": "全局记忆内容:\n\n---\n{{content}}\n---", + "Project memory content from {{path}}:\n\n---\n{{content}}\n---": + "项目记忆内容来自 {{path}}:\n\n---\n{{content}}\n---", + "Project memory is currently empty.": "项目记忆当前为空", + "Refreshing memory from source files...": "正在从源文件刷新记忆...", + "Add content to the memory. Use --global for global memory or --project for project memory.": + "添加内容到记忆。使用 --global 表示全局记忆,使用 --project 表示项目记忆", + "Usage: /memory add [--global|--project] ": + "用法:/memory add [--global|--project] <要记住的文本>", + 'Attempting to save to memory {{scope}}: "{{fact}}"': '正在尝试保存到记忆 {{scope}}:"{{fact}}"', // ============================================================================ // Commands - MCP // ============================================================================ - 'Authenticate with an OAuth-enabled MCP server': - '使用支持 OAuth 的 MCP 服务器进行认证', - 'List configured MCP servers and tools': '列出已配置的 MCP 服务器和工具', - 'Restarts MCP servers.': '重启 MCP 服务器', - 'Open MCP management dialog': '打开 MCP 管理对话框', - 'Could not retrieve tool registry.': '无法检索工具注册表', - 'No MCP servers configured with OAuth authentication.': - '未配置支持 OAuth 认证的 MCP 服务器', - 'MCP servers with OAuth authentication:': '支持 OAuth 认证的 MCP 服务器:', - 'Use /mcp auth to authenticate.': - '使用 /mcp auth 进行认证', + "Authenticate with an OAuth-enabled MCP server": "使用支持 OAuth 的 MCP 服务器进行认证", + "List configured MCP servers and tools": "列出已配置的 MCP 服务器和工具", + "Restarts MCP servers.": "重启 MCP 服务器", + "Open MCP management dialog": "打开 MCP 管理对话框", + "Could not retrieve tool registry.": "无法检索工具注册表", + "No MCP servers configured with OAuth authentication.": "未配置支持 OAuth 认证的 MCP 服务器", + "MCP servers with OAuth authentication:": "支持 OAuth 认证的 MCP 服务器:", + "Use /mcp auth to authenticate.": "使用 /mcp auth 进行认证", "MCP server '{{name}}' not found.": "未找到 MCP 服务器 '{{name}}'", "Successfully authenticated and refreshed tools for '{{name}}'.": "成功认证并刷新了 '{{name}}' 的工具", "Failed to authenticate with MCP server '{{name}}': {{error}}": "认证 MCP 服务器 '{{name}}' 失败:{{error}}", - "Re-discovering tools from '{{name}}'...": - "正在重新发现 '{{name}}' 的工具...", - "Discovered {{count}} tool(s) from '{{name}}'.": - "从 '{{name}}' 发现了 {{count}} 个工具。", - 'Authentication complete. Returning to server details...': - '认证完成,正在返回服务器详情...', - 'Authentication successful.': '认证成功。', - 'If the browser does not open, copy and paste this URL into your browser:': - '如果浏览器未自动打开,请复制以下 URL 并粘贴到浏览器中:', - 'Make sure to copy the COMPLETE URL - it may wrap across multiple lines.': - '⚠️ 请确保复制完整的 URL —— 它可能跨越多行。', + "Re-discovering tools from '{{name}}'...": "正在重新发现 '{{name}}' 的工具...", + "Discovered {{count}} tool(s) from '{{name}}'.": "从 '{{name}}' 发现了 {{count}} 个工具。", + "Authentication complete. Returning to server details...": "认证完成,正在返回服务器详情...", + "Authentication successful.": "认证成功。", + "If the browser does not open, copy and paste this URL into your browser:": + "如果浏览器未自动打开,请复制以下 URL 并粘贴到浏览器中:", + "Make sure to copy the COMPLETE URL - it may wrap across multiple lines.": + "⚠️ 请确保复制完整的 URL —— 它可能跨越多行。", // ============================================================================ // MCP Management Dialog // ============================================================================ - 'Manage MCP servers': '管理 MCP 服务器', - 'Server Detail': '服务器详情', - 'Disable Server': '禁用服务器', - Tools: '工具', - 'Tool Detail': '工具详情', - 'MCP Management': 'MCP 管理', - 'Loading...': '加载中...', - 'Unknown step': '未知步骤', - 'Esc to back': 'Esc 返回', - '↑↓ to navigate · Enter to select · Esc to close': - '↑↓ 导航 · Enter 选择 · Esc 关闭', - '↑↓ to navigate · Enter to select · Esc to back': - '↑↓ 导航 · Enter 选择 · Esc 返回', - '↑↓ to navigate · Enter to confirm · Esc to back': - '↑↓ 导航 · Enter 确认 · Esc 返回', - 'User Settings (global)': '用户设置(全局)', - 'Workspace Settings (project-specific)': '工作区设置(项目级)', - 'Disable server:': '禁用服务器:', - 'Select where to add the server to the exclude list:': - '选择将服务器添加到排除列表的位置:', - 'Press Enter to confirm, Esc to cancel': '按 Enter 确认,Esc 取消', - 'View tools': '查看工具', - Reconnect: '重新连接', - Enable: '启用', - Disable: '禁用', - Authenticate: '认证', - 'Re-authenticate': '重新认证', - 'Clear Authentication': '清空认证', - disabled: '已禁用', - 'Server:': '服务器:', - '(disabled)': '(已禁用)', - 'Error:': '错误:', - tool: '工具', - tools: '个工具', - connected: '已连接', - connecting: '连接中', - disconnected: '已断开', + "Manage MCP servers": "管理 MCP 服务器", + "Server Detail": "服务器详情", + "Disable Server": "禁用服务器", + Tools: "工具", + "Tool Detail": "工具详情", + "MCP Management": "MCP 管理", + "Loading...": "加载中...", + "Unknown step": "未知步骤", + "Esc to back": "Esc 返回", + "↑↓ to navigate · Enter to select · Esc to close": "↑↓ 导航 · Enter 选择 · Esc 关闭", + "↑↓ to navigate · Enter to select · Esc to back": "↑↓ 导航 · Enter 选择 · Esc 返回", + "↑↓ to navigate · Enter to confirm · Esc to back": "↑↓ 导航 · Enter 确认 · Esc 返回", + "User Settings (global)": "用户设置(全局)", + "Workspace Settings (project-specific)": "工作区设置(项目级)", + "Disable server:": "禁用服务器:", + "Select where to add the server to the exclude list:": "选择将服务器添加到排除列表的位置:", + "Press Enter to confirm, Esc to cancel": "按 Enter 确认,Esc 取消", + "View tools": "查看工具", + Reconnect: "重新连接", + Enable: "启用", + Disable: "禁用", + Authenticate: "认证", + "Re-authenticate": "重新认证", + "Clear Authentication": "清空认证", + disabled: "已禁用", + "Server:": "服务器:", + "(disabled)": "(已禁用)", + "Error:": "错误:", + tool: "工具", + tools: "个工具", + connected: "已连接", + connecting: "连接中", + disconnected: "已断开", // MCP Server List - 'User MCPs': '用户 MCP', - 'Project MCPs': '项目 MCP', - 'Extension MCPs': '扩展 MCP', - server: '个服务器', - servers: '个服务器', - 'Add MCP servers to your settings to get started.': - '请在设置中添加 MCP 服务器以开始使用。', - 'Run qwen --debug to see error logs': '运行 qwen --debug 查看错误日志', + "User MCPs": "用户 MCP", + "Project MCPs": "项目 MCP", + "Extension MCPs": "扩展 MCP", + server: "个服务器", + servers: "个服务器", + "Add MCP servers to your settings to get started.": "请在设置中添加 MCP 服务器以开始使用。", + "Run qwen --debug to see error logs": "运行 qwen --debug 查看错误日志", // MCP OAuth Authentication - 'OAuth Authentication': 'OAuth 认证', - 'Press Enter to start authentication, Esc to go back': - '按 Enter 开始认证,Esc 返回', - 'Authenticating... Please complete the login in your browser.': - '认证中... 请在浏览器中完成登录。', - 'Press Enter or Esc to go back': '按 Enter 或 Esc 返回', + "OAuth Authentication": "OAuth 认证", + "Press Enter to start authentication, Esc to go back": "按 Enter 开始认证,Esc 返回", + "Authenticating... Please complete the login in your browser.": + "认证中... 请在浏览器中完成登录。", + "Press Enter or Esc to go back": "按 Enter 或 Esc 返回", // MCP Server Detail - 'Command:': '命令:', - 'Working Directory:': '工作目录:', - 'Capabilities:': '功能:', + "Command:": "命令:", + "Working Directory:": "工作目录:", + "Capabilities:": "功能:", // MCP Tool List - 'No tools available for this server.': '此服务器没有可用工具。', - destructive: '破坏性', - 'read-only': '只读', - 'open-world': '开放世界', - idempotent: '幂等', - 'Tools for {{name}}': '{{name}} 的工具', - 'Tools for {{serverName}}': '{{serverName}} 的工具', - '{{current}}/{{total}}': '{{current}}/{{total}}', + "No tools available for this server.": "此服务器没有可用工具。", + destructive: "破坏性", + "read-only": "只读", + "open-world": "开放世界", + idempotent: "幂等", + "Tools for {{name}}": "{{name}} 的工具", + "Tools for {{serverName}}": "{{serverName}} 的工具", + "{{current}}/{{total}}": "{{current}}/{{total}}", // MCP Tool Detail - Type: '类型', - Parameters: '参数', - 'No tool selected': '未选择工具', - Annotations: '注解', - Title: '标题', - 'Read Only': '只读', - Destructive: '破坏性', - Idempotent: '幂等', - 'Open World': '开放世界', - Server: '服务器', + Type: "类型", + Parameters: "参数", + "No tool selected": "未选择工具", + Annotations: "注解", + Title: "标题", + "Read Only": "只读", + Destructive: "破坏性", + Idempotent: "幂等", + "Open World": "开放世界", + Server: "服务器", // Invalid tool related translations - '{{count}} invalid tools': '{{count}} 个无效工具', - invalid: '无效', - 'invalid: {{reason}}': '无效:{{reason}}', - 'missing name': '缺少名称', - 'missing description': '缺少描述', - '(unnamed)': '(未命名)', - 'Warning: This tool cannot be called by the LLM': - '警告:此工具无法被 LLM 调用', - Reason: '原因', - 'Tools must have both name and description to be used by the LLM.': - '工具必须同时具有名称和描述才能被 LLM 使用。', + "{{count}} invalid tools": "{{count}} 个无效工具", + invalid: "无效", + "invalid: {{reason}}": "无效:{{reason}}", + "missing name": "缺少名称", + "missing description": "缺少描述", + "(unnamed)": "(未命名)", + "Warning: This tool cannot be called by the LLM": "警告:此工具无法被 LLM 调用", + Reason: "原因", + "Tools must have both name and description to be used by the LLM.": + "工具必须同时具有名称和描述才能被 LLM 使用。", // ============================================================================ // Commands - Chat // ============================================================================ - 'Manage conversation history.': '管理对话历史', - 'List saved conversation checkpoints': '列出已保存的对话检查点', - 'No saved conversation checkpoints found.': '未找到已保存的对话检查点', - 'List of saved conversations:': '已保存的对话列表:', - 'Note: Newest last, oldest first': '注意:最新的在最后,最旧的在最前', - 'Save the current conversation as a checkpoint. Usage: /chat save ': - '将当前对话保存为检查点。用法:/chat save ', - 'Missing tag. Usage: /chat save ': '缺少标签。用法:/chat save ', - 'Delete a conversation checkpoint. Usage: /chat delete ': - '删除对话检查点。用法:/chat delete ', - 'Missing tag. Usage: /chat delete ': - '缺少标签。用法:/chat delete ', - "Conversation checkpoint '{{tag}}' has been deleted.": - "对话检查点 '{{tag}}' 已删除", - "Error: No checkpoint found with tag '{{tag}}'.": - "错误:未找到标签为 '{{tag}}' 的检查点", - 'Resume a conversation from a checkpoint. Usage: /chat resume ': - '从检查点恢复对话。用法:/chat resume ', - 'Missing tag. Usage: /chat resume ': - '缺少标签。用法:/chat resume ', - 'No saved checkpoint found with tag: {{tag}}.': - '未找到标签为 {{tag}} 的已保存检查点', - 'A checkpoint with the tag {{tag}} already exists. Do you want to overwrite it?': - '标签为 {{tag}} 的检查点已存在。您要覆盖它吗?', - 'No chat client available to save conversation.': - '没有可用的聊天客户端来保存对话', - 'Conversation checkpoint saved with tag: {{tag}}.': - '对话检查点已保存,标签:{{tag}}', - 'No conversation found to save.': '未找到要保存的对话', - 'No chat client available to share conversation.': - '没有可用的聊天客户端来分享对话', - 'Invalid file format. Only .md and .json are supported.': - '无效的文件格式。仅支持 .md 和 .json 文件', - 'Error sharing conversation: {{error}}': '分享对话时出错:{{error}}', - 'Conversation shared to {{filePath}}': '对话已分享到 {{filePath}}', - 'No conversation found to share.': '未找到要分享的对话', - 'Share the current conversation to a markdown or json file. Usage: /chat share ': - '将当前对话分享到 markdown 或 json 文件。用法:/chat share ', + "Manage conversation history.": "管理对话历史", + "List saved conversation checkpoints": "列出已保存的对话检查点", + "No saved conversation checkpoints found.": "未找到已保存的对话检查点", + "List of saved conversations:": "已保存的对话列表:", + "Note: Newest last, oldest first": "注意:最新的在最后,最旧的在最前", + "Save the current conversation as a checkpoint. Usage: /chat save ": + "将当前对话保存为检查点。用法:/chat save ", + "Missing tag. Usage: /chat save ": "缺少标签。用法:/chat save ", + "Delete a conversation checkpoint. Usage: /chat delete ": + "删除对话检查点。用法:/chat delete ", + "Missing tag. Usage: /chat delete ": "缺少标签。用法:/chat delete ", + "Conversation checkpoint '{{tag}}' has been deleted.": "对话检查点 '{{tag}}' 已删除", + "Error: No checkpoint found with tag '{{tag}}'.": "错误:未找到标签为 '{{tag}}' 的检查点", + "Resume a conversation from a checkpoint. Usage: /chat resume ": + "从检查点恢复对话。用法:/chat resume ", + "Missing tag. Usage: /chat resume ": "缺少标签。用法:/chat resume ", + "No saved checkpoint found with tag: {{tag}}.": "未找到标签为 {{tag}} 的已保存检查点", + "A checkpoint with the tag {{tag}} already exists. Do you want to overwrite it?": + "标签为 {{tag}} 的检查点已存在。您要覆盖它吗?", + "No chat client available to save conversation.": "没有可用的聊天客户端来保存对话", + "Conversation checkpoint saved with tag: {{tag}}.": "对话检查点已保存,标签:{{tag}}", + "No conversation found to save.": "未找到要保存的对话", + "No chat client available to share conversation.": "没有可用的聊天客户端来分享对话", + "Invalid file format. Only .md and .json are supported.": + "无效的文件格式。仅支持 .md 和 .json 文件", + "Error sharing conversation: {{error}}": "分享对话时出错:{{error}}", + "Conversation shared to {{filePath}}": "对话已分享到 {{filePath}}", + "No conversation found to share.": "未找到要分享的对话", + "Share the current conversation to a markdown or json file. Usage: /chat share ": + "将当前对话分享到 markdown 或 json 文件。用法:/chat share ", // ============================================================================ // Commands - Summary // ============================================================================ - 'Generate a project summary and save it to .qwen/PROJECT_SUMMARY.md': - '生成项目摘要并保存到 .qwen/PROJECT_SUMMARY.md', - 'No chat client available to generate summary.': - '没有可用的聊天客户端来生成摘要', - 'Already generating summary, wait for previous request to complete': - '正在生成摘要,请等待上一个请求完成', - 'No conversation found to summarize.': '未找到要总结的对话', - 'Failed to generate project context summary: {{error}}': - '生成项目上下文摘要失败:{{error}}', - 'Saved project summary to {{filePathForDisplay}}.': - '项目摘要已保存到 {{filePathForDisplay}}', - 'Saving project summary...': '正在保存项目摘要...', - 'Generating project summary...': '正在生成项目摘要...', - 'Failed to generate summary - no text content received from LLM response': - '生成摘要失败 - 未从 LLM 响应中接收到文本内容', + "Generate a project summary and save it to .qwen/PROJECT_SUMMARY.md": + "生成项目摘要并保存到 .qwen/PROJECT_SUMMARY.md", + "No chat client available to generate summary.": "没有可用的聊天客户端来生成摘要", + "Already generating summary, wait for previous request to complete": + "正在生成摘要,请等待上一个请求完成", + "No conversation found to summarize.": "未找到要总结的对话", + "Failed to generate project context summary: {{error}}": "生成项目上下文摘要失败:{{error}}", + "Saved project summary to {{filePathForDisplay}}.": "项目摘要已保存到 {{filePathForDisplay}}", + "Saving project summary...": "正在保存项目摘要...", + "Generating project summary...": "正在生成项目摘要...", + "Failed to generate summary - no text content received from LLM response": + "生成摘要失败 - 未从 LLM 响应中接收到文本内容", // ============================================================================ // Commands - Model // ============================================================================ - 'Switch the model for this session': '切换此会话的模型', - 'Set fast model for background tasks': '设置后台任务的快速模型', - 'Content generator configuration not available.': '内容生成器配置不可用', - 'Authentication type not available.': '认证类型不可用', - 'No models available for the current authentication type ({{authType}}).': - '当前认证类型 ({{authType}}) 没有可用的模型', + "Switch the model for this session": "切换此会话的模型", + "Set fast model for background tasks": "设置后台任务的快速模型", + "Content generator configuration not available.": "内容生成器配置不可用", + "Authentication type not available.": "认证类型不可用", + "No models available for the current authentication type ({{authType}}).": + "当前认证类型 ({{authType}}) 没有可用的模型", // ============================================================================ // Commands - Clear // ============================================================================ - 'Starting a new session, resetting chat, and clearing terminal.': - '正在开始新会话,重置聊天并清屏。', - 'Starting a new session and clearing.': '正在开始新会话并清屏。', + "Starting a new session, resetting chat, and clearing terminal.": + "正在开始新会话,重置聊天并清屏。", + "Starting a new session and clearing.": "正在开始新会话并清屏。", // ============================================================================ // Commands - Compress // ============================================================================ - 'Already compressing, wait for previous request to complete': - '正在压缩中,请等待上一个请求完成', - 'Failed to compress chat history.': '压缩聊天历史失败', - 'Failed to compress chat history: {{error}}': '压缩聊天历史失败:{{error}}', - 'Compressing chat history': '正在压缩聊天历史', - 'Chat history compressed from {{originalTokens}} to {{newTokens}} tokens.': - '聊天历史已从 {{originalTokens}} 个 token 压缩到 {{newTokens}} 个 token。', - 'Compression was not beneficial for this history size.': - '对于此历史记录大小,压缩没有益处。', - 'Chat history compression did not reduce size. This may indicate issues with the compression prompt.': - '聊天历史压缩未能减小大小。这可能表明压缩提示存在问题。', - 'Could not compress chat history due to a token counting error.': - '由于 token 计数错误,无法压缩聊天历史。', - 'Chat history is already compressed.': '聊天历史已经压缩。', + "Already compressing, wait for previous request to complete": "正在压缩中,请等待上一个请求完成", + "Failed to compress chat history.": "压缩聊天历史失败", + "Failed to compress chat history: {{error}}": "压缩聊天历史失败:{{error}}", + "Compressing chat history": "正在压缩聊天历史", + "Chat history compressed from {{originalTokens}} to {{newTokens}} tokens.": + "聊天历史已从 {{originalTokens}} 个 token 压缩到 {{newTokens}} 个 token。", + "Compression was not beneficial for this history size.": "对于此历史记录大小,压缩没有益处。", + "Chat history compression did not reduce size. This may indicate issues with the compression prompt.": + "聊天历史压缩未能减小大小。这可能表明压缩提示存在问题。", + "Could not compress chat history due to a token counting error.": + "由于 token 计数错误,无法压缩聊天历史。", + "Chat history is already compressed.": "聊天历史已经压缩。", // ============================================================================ // Commands - Directory // ============================================================================ - 'Configuration is not available.': '配置不可用。', - 'Please provide at least one path to add.': '请提供至少一个要添加的路径。', - 'The /directory add command is not supported in restrictive sandbox profiles. Please use --include-directories when starting the session instead.': - '/directory add 命令在限制性沙箱配置文件中不受支持。请改为在启动会话时使用 --include-directories。', + "Configuration is not available.": "配置不可用。", + "Please provide at least one path to add.": "请提供至少一个要添加的路径。", + "The /directory add command is not supported in restrictive sandbox profiles. Please use --include-directories when starting the session instead.": + "/directory add 命令在限制性沙箱配置文件中不受支持。请改为在启动会话时使用 --include-directories。", "Error adding '{{path}}': {{error}}": "添加 '{{path}}' 时出错:{{error}}", - 'Successfully added QWEN.md files from the following directories if there are:\n- {{directories}}': - '如果存在,已成功从以下目录添加 QWEN.md 文件:\n- {{directories}}', - 'Error refreshing memory: {{error}}': '刷新内存时出错:{{error}}', - 'Successfully added directories:\n- {{directories}}': - '成功添加目录:\n- {{directories}}', - 'Current workspace directories:\n{{directories}}': - '当前工作区目录:\n{{directories}}', + "Successfully added QWEN.md files from the following directories if there are:\n- {{directories}}": + "如果存在,已成功从以下目录添加 QWEN.md 文件:\n- {{directories}}", + "Error refreshing memory: {{error}}": "刷新内存时出错:{{error}}", + "Successfully added directories:\n- {{directories}}": "成功添加目录:\n- {{directories}}", + "Current workspace directories:\n{{directories}}": "当前工作区目录:\n{{directories}}", // ============================================================================ // Commands - Docs // ============================================================================ - 'Please open the following URL in your browser to view the documentation:\n{{url}}': - '请在浏览器中打开以下 URL 以查看文档:\n{{url}}', - 'Opening documentation in your browser: {{url}}': - '正在浏览器中打开文档:{{url}}', + "Please open the following URL in your browser to view the documentation:\n{{url}}": + "请在浏览器中打开以下 URL 以查看文档:\n{{url}}", + "Opening documentation in your browser: {{url}}": "正在浏览器中打开文档:{{url}}", // ============================================================================ // Dialogs - Tool Confirmation // ============================================================================ - 'Do you want to proceed?': '是否继续?', - 'Yes, allow once': '是,允许一次', - 'Allow always': '总是允许', - Yes: '是', - No: '否', - 'No (esc)': '否 (esc)', - 'Yes, allow always for this session': '是,本次会话总是允许', - 'Modify in progress:': '正在修改:', - 'Save and close external editor to continue': '保存并关闭外部编辑器以继续', - 'Apply this change?': '是否应用此更改?', - 'Yes, allow always': '是,总是允许', - 'Modify with external editor': '使用外部编辑器修改', - 'No, suggest changes (esc)': '否,建议更改 (esc)', + "Do you want to proceed?": "是否继续?", + "Yes, allow once": "是,允许一次", + "Allow always": "总是允许", + Yes: "是", + No: "否", + "No (esc)": "否 (esc)", + "Yes, allow always for this session": "是,本次会话总是允许", + "Modify in progress:": "正在修改:", + "Save and close external editor to continue": "保存并关闭外部编辑器以继续", + "Apply this change?": "是否应用此更改?", + "Yes, allow always": "是,总是允许", + "Modify with external editor": "使用外部编辑器修改", + "No, suggest changes (esc)": "否,建议更改 (esc)", "Allow execution of: '{{command}}'?": "允许执行:'{{command}}'?", - 'Yes, allow always ...': '是,总是允许 ...', - 'Always allow in this project': '在本项目中总是允许', - 'Always allow {{action}} in this project': '在本项目中总是允许{{action}}', - 'Always allow for this user': '对该用户总是允许', - 'Always allow {{action}} for this user': '对该用户总是允许{{action}}', - 'Yes, restore previous mode ({{mode}})': '是,恢复之前的模式 ({{mode}})', - 'Yes, and auto-accept edits': '是,并自动接受编辑', - 'Yes, and manually approve edits': '是,并手动批准编辑', - 'No, keep planning (esc)': '否,继续规划 (esc)', - 'URLs to fetch:': '要获取的 URL:', - 'MCP Server: {{server}}': 'MCP 服务器:{{server}}', - 'Tool: {{tool}}': '工具:{{tool}}', + "Yes, allow always ...": "是,总是允许 ...", + "Always allow in this project": "在本项目中总是允许", + "Always allow {{action}} in this project": "在本项目中总是允许{{action}}", + "Always allow for this user": "对该用户总是允许", + "Always allow {{action}} for this user": "对该用户总是允许{{action}}", + "Yes, restore previous mode ({{mode}})": "是,恢复之前的模式 ({{mode}})", + "Yes, and auto-accept edits": "是,并自动接受编辑", + "Yes, and manually approve edits": "是,并手动批准编辑", + "No, keep planning (esc)": "否,继续规划 (esc)", + "URLs to fetch:": "要获取的 URL:", + "MCP Server: {{server}}": "MCP 服务器:{{server}}", + "Tool: {{tool}}": "工具:{{tool}}", 'Allow execution of MCP tool "{{tool}}" from server "{{server}}"?': '允许执行来自服务器 "{{server}}" 的 MCP 工具 "{{tool}}"?', 'Yes, always allow tool "{{tool}}" from server "{{server}}"': @@ -1187,619 +1081,581 @@ export default { // ============================================================================ // Dialogs - Shell Confirmation // ============================================================================ - 'Shell Command Execution': 'Shell 命令执行', - 'A custom command wants to run the following shell commands:': - '自定义命令想要运行以下 shell 命令:', + "Shell Command Execution": "Shell 命令执行", + "A custom command wants to run the following shell commands:": + "自定义命令想要运行以下 shell 命令:", // ============================================================================ // Dialogs - Pro Quota // ============================================================================ - 'Pro quota limit reached for {{model}}.': '{{model}} 的 Pro 配额已达到上限', - 'Change auth (executes the /auth command)': '更改认证(执行 /auth 命令)', - 'Continue with {{model}}': '使用 {{model}} 继续', + "Pro quota limit reached for {{model}}.": "{{model}} 的 Pro 配额已达到上限", + "Change auth (executes the /auth command)": "更改认证(执行 /auth 命令)", + "Continue with {{model}}": "使用 {{model}} 继续", // ============================================================================ // Dialogs - Welcome Back // ============================================================================ - 'Current Plan:': '当前计划:', - 'Progress: {{done}}/{{total}} tasks completed': - '进度:已完成 {{done}}/{{total}} 个任务', - ', {{inProgress}} in progress': ',{{inProgress}} 个进行中', - 'Pending Tasks:': '待处理任务:', - 'What would you like to do?': '您想要做什么?', - 'Choose how to proceed with your session:': '选择如何继续您的会话:', - 'Start new chat session': '开始新的聊天会话', - 'Continue previous conversation': '继续之前的对话', - '👋 Welcome back! (Last updated: {{timeAgo}})': - '👋 欢迎回来!(最后更新:{{timeAgo}})', - '🎯 Overall Goal:': '🎯 总体目标:', + "Current Plan:": "当前计划:", + "Progress: {{done}}/{{total}} tasks completed": "进度:已完成 {{done}}/{{total}} 个任务", + ", {{inProgress}} in progress": ",{{inProgress}} 个进行中", + "Pending Tasks:": "待处理任务:", + "What would you like to do?": "您想要做什么?", + "Choose how to proceed with your session:": "选择如何继续您的会话:", + "Start new chat session": "开始新的聊天会话", + "Continue previous conversation": "继续之前的对话", + "👋 Welcome back! (Last updated: {{timeAgo}})": "👋 欢迎回来!(最后更新:{{timeAgo}})", + "🎯 Overall Goal:": "🎯 总体目标:", // ============================================================================ // Dialogs - Auth // ============================================================================ - 'Get started': '开始使用', - 'Select Authentication Method': '选择认证方式', - 'OpenAI API key is required to use OpenAI authentication.': - '使用 OpenAI 认证需要 OpenAI API 密钥', - 'You must select an auth method to proceed. Press Ctrl+C again to exit.': - '您必须选择认证方法才能继续。再次按 Ctrl+C 退出', - 'Terms of Services and Privacy Notice': '服务条款和隐私声明', - 'Paid \u00B7 Up to 6,000 requests/5 hrs \u00B7 All Alibaba Cloud Coding Plan Models': - '付费 \u00B7 每 5 小时最多 6,000 次请求 \u00B7 支持阿里云百炼 Coding Plan 全部模型', - 'Alibaba Cloud Coding Plan': '阿里云百炼 Coding Plan', - 'Bring your own API key': '使用自己的 API 密钥', - 'Use coding plan credentials or your own api-keys/providers.': - '使用 Coding Plan 凭证或您自己的 API 密钥/提供商。', - OpenAI: 'OpenAI', - 'Failed to login. Message: {{message}}': '登录失败。消息:{{message}}', - 'Authentication is enforced to be {{enforcedType}}, but you are currently using {{currentType}}.': - '认证方式被强制设置为 {{enforcedType}},但您当前使用的是 {{currentType}}', - 'Please visit this URL to authorize:': '请访问此 URL 进行授权:', - 'Or scan the QR code below:': '或扫描下方的二维码:', - 'Waiting for authorization': '等待授权中', - 'Time remaining:': '剩余时间:', - '(Press ESC or CTRL+C to cancel)': '(按 ESC 或 CTRL+C 取消)', - 'OAuth token expired (over {{seconds}} seconds). Please select authentication method again.': - 'OAuth 令牌已过期(超过 {{seconds}} 秒)。请重新选择认证方法', - 'Press any key to return to authentication type selection.': - '按任意键返回认证类型选择', - 'Authentication timed out. Please try again.': '认证超时。请重试。', - 'Waiting for auth... (Press ESC or CTRL+C to cancel)': - '正在等待认证...(按 ESC 或 CTRL+C 取消)', - 'Missing API key for OpenAI-compatible auth. Set settings.security.auth.apiKey, or set the {{envKeyHint}} environment variable.': - '缺少 OpenAI 兼容认证的 API 密钥。请设置 settings.security.auth.apiKey 或设置 {{envKeyHint}} 环境变量。', - '{{envKeyHint}} environment variable not found.': - '未找到 {{envKeyHint}} 环境变量。', - '{{envKeyHint}} environment variable not found. Please set it in your .env file or environment variables.': - '未找到 {{envKeyHint}} 环境变量。请在 .env 文件或系统环境变量中进行设置。', - '{{envKeyHint}} environment variable not found (or set settings.security.auth.apiKey). Please set it in your .env file or environment variables.': - '未找到 {{envKeyHint}} 环境变量(或设置 settings.security.auth.apiKey)。请在 .env 文件或系统环境变量中进行设置。', - 'Missing API key for OpenAI-compatible auth. Set the {{envKeyHint}} environment variable.': - '缺少 OpenAI 兼容认证的 API 密钥。请设置 {{envKeyHint}} 环境变量。', - 'Anthropic provider missing required baseUrl in modelProviders[].baseUrl.': - 'Anthropic 提供商缺少必需的 baseUrl,请在 modelProviders[].baseUrl 中配置。', - 'ANTHROPIC_BASE_URL environment variable not found.': - '未找到 ANTHROPIC_BASE_URL 环境变量。', - 'Invalid auth method selected.': '选择了无效的认证方式。', - 'Failed to authenticate. Message: {{message}}': '认证失败。消息:{{message}}', - 'Authenticated successfully with {{authType}} credentials.': - '使用 {{authType}} 凭据成功认证。', + "Get started": "开始使用", + "Select Authentication Method": "选择认证方式", + "OpenAI API key is required to use OpenAI authentication.": + "使用 OpenAI 认证需要 OpenAI API 密钥", + "You must select an auth method to proceed. Press Ctrl+C again to exit.": + "您必须选择认证方法才能继续。再次按 Ctrl+C 退出", + "Terms of Services and Privacy Notice": "服务条款和隐私声明", + "Paid \u00B7 Up to 6,000 requests/5 hrs \u00B7 All Alibaba Cloud Coding Plan Models": + "付费 \u00B7 每 5 小时最多 6,000 次请求 \u00B7 支持阿里云百炼 Coding Plan 全部模型", + "Alibaba Cloud Coding Plan": "阿里云百炼 Coding Plan", + "Bring your own API key": "使用自己的 API 密钥", + "Use coding plan credentials or your own api-keys/providers.": + "使用 Coding Plan 凭证或您自己的 API 密钥/提供商。", + OpenAI: "OpenAI", + "Failed to login. Message: {{message}}": "登录失败。消息:{{message}}", + "Authentication is enforced to be {{enforcedType}}, but you are currently using {{currentType}}.": + "认证方式被强制设置为 {{enforcedType}},但您当前使用的是 {{currentType}}", + "Please visit this URL to authorize:": "请访问此 URL 进行授权:", + "Or scan the QR code below:": "或扫描下方的二维码:", + "Waiting for authorization": "等待授权中", + "Time remaining:": "剩余时间:", + "(Press ESC or CTRL+C to cancel)": "(按 ESC 或 CTRL+C 取消)", + "OAuth token expired (over {{seconds}} seconds). Please select authentication method again.": + "OAuth 令牌已过期(超过 {{seconds}} 秒)。请重新选择认证方法", + "Press any key to return to authentication type selection.": "按任意键返回认证类型选择", + "Authentication timed out. Please try again.": "认证超时。请重试。", + "Waiting for auth... (Press ESC or CTRL+C to cancel)": "正在等待认证...(按 ESC 或 CTRL+C 取消)", + "Missing API key for OpenAI-compatible auth. Set settings.security.auth.apiKey, or set the {{envKeyHint}} environment variable.": + "缺少 OpenAI 兼容认证的 API 密钥。请设置 settings.security.auth.apiKey 或设置 {{envKeyHint}} 环境变量。", + "{{envKeyHint}} environment variable not found.": "未找到 {{envKeyHint}} 环境变量。", + "{{envKeyHint}} environment variable not found. Please set it in your .env file or environment variables.": + "未找到 {{envKeyHint}} 环境变量。请在 .env 文件或系统环境变量中进行设置。", + "{{envKeyHint}} environment variable not found (or set settings.security.auth.apiKey). Please set it in your .env file or environment variables.": + "未找到 {{envKeyHint}} 环境变量(或设置 settings.security.auth.apiKey)。请在 .env 文件或系统环境变量中进行设置。", + "Missing API key for OpenAI-compatible auth. Set the {{envKeyHint}} environment variable.": + "缺少 OpenAI 兼容认证的 API 密钥。请设置 {{envKeyHint}} 环境变量。", + "Anthropic provider missing required baseUrl in modelProviders[].baseUrl.": + "Anthropic 提供商缺少必需的 baseUrl,请在 modelProviders[].baseUrl 中配置。", + "ANTHROPIC_BASE_URL environment variable not found.": "未找到 ANTHROPIC_BASE_URL 环境变量。", + "Invalid auth method selected.": "选择了无效的认证方式。", + "Failed to authenticate. Message: {{message}}": "认证失败。消息:{{message}}", + "Authenticated successfully with {{authType}} credentials.": "使用 {{authType}} 凭据成功认证。", 'Invalid QWEN_DEFAULT_AUTH_TYPE value: "{{value}}". Valid values are: {{validValues}}': '无效的 QWEN_DEFAULT_AUTH_TYPE 值:"{{value}}"。有效值为:{{validValues}}', - 'OpenAI Configuration Required': '需要配置 OpenAI', - 'Please enter your OpenAI configuration. You can get an API key from': - '请输入您的 OpenAI 配置。您可以从以下地址获取 API 密钥:', - 'API Key:': 'API 密钥:', - 'Invalid credentials: {{errorMessage}}': '凭据无效:{{errorMessage}}', - 'Failed to validate credentials': '验证凭据失败', - 'Press Enter to continue, Tab/↑↓ to navigate, Esc to cancel': - '按 Enter 继续,Tab/↑↓ 导航,Esc 取消', + "OpenAI Configuration Required": "需要配置 OpenAI", + "Please enter your OpenAI configuration. You can get an API key from": + "请输入您的 OpenAI 配置。您可以从以下地址获取 API 密钥:", + "API Key:": "API 密钥:", + "Invalid credentials: {{errorMessage}}": "凭据无效:{{errorMessage}}", + "Failed to validate credentials": "验证凭据失败", + "Press Enter to continue, Tab/↑↓ to navigate, Esc to cancel": + "按 Enter 继续,Tab/↑↓ 导航,Esc 取消", // ============================================================================ // Dialogs - Model // ============================================================================ - 'Select Model': '选择模型', - '(Press Esc to close)': '(按 Esc 关闭)', - 'Current (effective) configuration': '当前(实际生效)配置', - AuthType: '认证方式', - 'API Key': 'API 密钥', - unset: '未设置', - '(default)': '(默认)', - '(set)': '(已设置)', - '(not set)': '(未设置)', - Modality: '模态', - 'Context Window': '上下文窗口', - text: '文本', - 'text-only': '纯文本', - image: '图像', - pdf: 'PDF', - audio: '音频', - video: '视频', - 'not set': '未设置', - none: '无', - unknown: '未知', + "Select Model": "选择模型", + "(Press Esc to close)": "(按 Esc 关闭)", + "Current (effective) configuration": "当前(实际生效)配置", + AuthType: "认证方式", + "API Key": "API 密钥", + unset: "未设置", + "(default)": "(默认)", + "(set)": "(已设置)", + "(not set)": "(未设置)", + Modality: "模态", + "Context Window": "上下文窗口", + text: "文本", + "text-only": "纯文本", + image: "图像", + pdf: "PDF", + audio: "音频", + video: "视频", + "not set": "未设置", + none: "无", + unknown: "未知", "Failed to switch model to '{{modelId}}'.\n\n{{error}}": "无法切换到模型 '{{modelId}}'.\n\n{{error}}", - 'Qwen 3.6 Plus — efficient hybrid model with leading coding performance': - 'Qwen 3.6 Plus — 高效混合架构,编程性能业界领先', - 'The latest Qwen Vision model from Alibaba Cloud ModelStudio (version: qwen3-vl-plus-2025-09-23)': - '来自阿里云 ModelStudio 的最新 Qwen Vision 模型(版本:qwen3-vl-plus-2025-09-23)', + "Qwen 3.6 Plus — efficient hybrid model with leading coding performance": + "Qwen 3.6 Plus — 高效混合架构,编程性能业界领先", + "The latest Qwen Vision model from Alibaba Cloud ModelStudio (version: qwen3-vl-plus-2025-09-23)": + "来自阿里云 ModelStudio 的最新 Qwen Vision 模型(版本:qwen3-vl-plus-2025-09-23)", // ============================================================================ // Dialogs - Permissions // ============================================================================ - 'Manage folder trust settings': '管理文件夹信任设置', - 'Manage permission rules': '管理权限规则', - Allow: '允许', - Ask: '询问', - Deny: '拒绝', - Workspace: '工作区', - "Qwen Code won't ask before using allowed tools.": - 'Qwen Code 使用已允许的工具前不会询问。', - 'Qwen Code will ask before using these tools.': - 'Qwen Code 使用这些工具前会先询问。', - 'Qwen Code is not allowed to use denied tools.': - 'Qwen Code 不允许使用被拒绝的工具。', - 'Manage trusted directories for this workspace.': - '管理此工作区的受信任目录。', - 'Any use of the {{tool}} tool': '{{tool}} 工具的任何使用', - "{{tool}} commands matching '{{pattern}}'": - "匹配 '{{pattern}}' 的 {{tool}} 命令", - 'From user settings': '来自用户设置', - 'From project settings': '来自项目设置', - 'From session': '来自会话', - 'Project settings (local)': '项目设置(本地)', - 'Saved in .qwen/settings.local.json': '保存在 .qwen/settings.local.json', - 'Project settings': '项目设置', - 'Checked in at .qwen/settings.json': '保存在 .qwen/settings.json', - 'User settings': '用户设置', - 'Saved in at ~/.qwen/settings.json': '保存在 ~/.qwen/settings.json', - 'Add a new rule…': '添加新规则…', - 'Add {{type}} permission rule': '添加{{type}}权限规则', - 'Permission rules are a tool name, optionally followed by a specifier in parentheses.': - '权限规则是一个工具名称,可选地后跟括号中的限定符。', - 'e.g.,': '例如', - or: '或', - 'Enter permission rule…': '输入权限规则…', - 'Enter to submit · Esc to cancel': '回车提交 · Esc 取消', - 'Where should this rule be saved?': '此规则应保存在哪里?', - 'Enter to confirm · Esc to cancel': '回车确认 · Esc 取消', - 'Delete {{type}} rule?': '删除{{type}}规则?', - 'Are you sure you want to delete this permission rule?': - '确定要删除此权限规则吗?', - 'Permissions:': '权限:', - '(←/→ or tab to cycle)': '(←/→ 或 tab 切换)', - 'Press ↑↓ to navigate · Enter to select · Type to search · Esc to cancel': - '按 ↑↓ 导航 · 回车选择 · 输入搜索 · Esc 取消', - 'Search…': '搜索…', - 'Use /trust to manage folder trust settings for this workspace.': - '使用 /trust 管理此工作区的文件夹信任设置。', + "Manage folder trust settings": "管理文件夹信任设置", + "Manage permission rules": "管理权限规则", + Allow: "允许", + Ask: "询问", + Deny: "拒绝", + Workspace: "工作区", + "Qwen Code won't ask before using allowed tools.": "Qwen Code 使用已允许的工具前不会询问。", + "Qwen Code will ask before using these tools.": "Qwen Code 使用这些工具前会先询问。", + "Qwen Code is not allowed to use denied tools.": "Qwen Code 不允许使用被拒绝的工具。", + "Manage trusted directories for this workspace.": "管理此工作区的受信任目录。", + "Any use of the {{tool}} tool": "{{tool}} 工具的任何使用", + "{{tool}} commands matching '{{pattern}}'": "匹配 '{{pattern}}' 的 {{tool}} 命令", + "From user settings": "来自用户设置", + "From project settings": "来自项目设置", + "From session": "来自会话", + "Project settings (local)": "项目设置(本地)", + "Saved in .qwen/settings.local.json": "保存在 .qwen/settings.local.json", + "Project settings": "项目设置", + "Checked in at .qwen/settings.json": "保存在 .qwen/settings.json", + "User settings": "用户设置", + "Saved in at ~/.qwen/settings.json": "保存在 ~/.qwen/settings.json", + "Add a new rule…": "添加新规则…", + "Add {{type}} permission rule": "添加{{type}}权限规则", + "Permission rules are a tool name, optionally followed by a specifier in parentheses.": + "权限规则是一个工具名称,可选地后跟括号中的限定符。", + "e.g.,": "例如", + or: "或", + "Enter permission rule…": "输入权限规则…", + "Enter to submit · Esc to cancel": "回车提交 · Esc 取消", + "Where should this rule be saved?": "此规则应保存在哪里?", + "Enter to confirm · Esc to cancel": "回车确认 · Esc 取消", + "Delete {{type}} rule?": "删除{{type}}规则?", + "Are you sure you want to delete this permission rule?": "确定要删除此权限规则吗?", + "Permissions:": "权限:", + "(←/→ or tab to cycle)": "(←/→ 或 tab 切换)", + "Press ↑↓ to navigate · Enter to select · Type to search · Esc to cancel": + "按 ↑↓ 导航 · 回车选择 · 输入搜索 · Esc 取消", + "Search…": "搜索…", + "Use /trust to manage folder trust settings for this workspace.": + "使用 /trust 管理此工作区的文件夹信任设置。", // Workspace directory management - 'Add directory…': '添加目录…', - 'Add directory to workspace': '添加工作区目录', - 'Qwen Code can read files in the workspace, and make edits when auto-accept edits is on.': - 'Qwen Code 可以读取工作区中的文件,并在自动接受编辑模式开启时进行编辑。', - 'Qwen Code will be able to read files in this directory and make edits when auto-accept edits is on.': - 'Qwen Code 将能够读取此目录中的文件,并在自动接受编辑模式开启时进行编辑。', - 'Enter the path to the directory:': '输入目录路径:', - 'Enter directory path…': '输入目录路径…', - 'Tab to complete · Enter to add · Esc to cancel': - 'Tab 补全 · 回车添加 · Esc 取消', - 'Remove directory?': '删除目录?', - 'Are you sure you want to remove this directory from the workspace?': - '确定要将此目录从工作区中移除吗?', - ' (Original working directory)': ' (原始工作目录)', - ' (from settings)': ' (来自设置)', - 'Directory does not exist.': '目录不存在。', - 'Path is not a directory.': '路径不是目录。', - 'This directory is already in the workspace.': '此目录已在工作区中。', - 'Already covered by existing directory: {{dir}}': '已被现有目录覆盖:{{dir}}', + "Add directory…": "添加目录…", + "Add directory to workspace": "添加工作区目录", + "Qwen Code can read files in the workspace, and make edits when auto-accept edits is on.": + "Qwen Code 可以读取工作区中的文件,并在自动接受编辑模式开启时进行编辑。", + "Qwen Code will be able to read files in this directory and make edits when auto-accept edits is on.": + "Qwen Code 将能够读取此目录中的文件,并在自动接受编辑模式开启时进行编辑。", + "Enter the path to the directory:": "输入目录路径:", + "Enter directory path…": "输入目录路径…", + "Tab to complete · Enter to add · Esc to cancel": "Tab 补全 · 回车添加 · Esc 取消", + "Remove directory?": "删除目录?", + "Are you sure you want to remove this directory from the workspace?": + "确定要将此目录从工作区中移除吗?", + " (Original working directory)": " (原始工作目录)", + " (from settings)": " (来自设置)", + "Directory does not exist.": "目录不存在。", + "Path is not a directory.": "路径不是目录。", + "This directory is already in the workspace.": "此目录已在工作区中。", + "Already covered by existing directory: {{dir}}": "已被现有目录覆盖:{{dir}}", // ============================================================================ // Status Bar // ============================================================================ - 'Using:': '已加载: ', - '{{count}} open file': '{{count}} 个打开的文件', - '{{count}} open files': '{{count}} 个打开的文件', - '(ctrl+g to view)': '(按 ctrl+g 查看)', - '{{count}} {{name}} file': '{{count}} 个 {{name}} 文件', - '{{count}} {{name}} files': '{{count}} 个 {{name}} 文件', - '{{count}} MCP server': '{{count}} 个 MCP 服务器', - '{{count}} MCP servers': '{{count}} 个 MCP 服务器', - '{{count}} Blocked': '{{count}} 个已阻止', - '(ctrl+t to view)': '(按 ctrl+t 查看)', - '(ctrl+t to toggle)': '(按 ctrl+t 切换)', - 'Press Ctrl+C again to exit.': '再次按 Ctrl+C 退出', - 'Press Ctrl+D again to exit.': '再次按 Ctrl+D 退出', - 'Press Esc again to clear.': '再次按 Esc 清除', + "Using:": "已加载: ", + "{{count}} open file": "{{count}} 个打开的文件", + "{{count}} open files": "{{count}} 个打开的文件", + "(ctrl+g to view)": "(按 ctrl+g 查看)", + "{{count}} {{name}} file": "{{count}} 个 {{name}} 文件", + "{{count}} {{name}} files": "{{count}} 个 {{name}} 文件", + "{{count}} MCP server": "{{count}} 个 MCP 服务器", + "{{count}} MCP servers": "{{count}} 个 MCP 服务器", + "{{count}} Blocked": "{{count}} 个已阻止", + "(ctrl+t to view)": "(按 ctrl+t 查看)", + "(ctrl+t to toggle)": "(按 ctrl+t 切换)", + "Press Ctrl+C again to exit.": "再次按 Ctrl+C 退出", + "Press Ctrl+D again to exit.": "再次按 Ctrl+D 退出", + "Press Esc again to clear.": "再次按 Esc 清除", // ============================================================================ // MCP Status // ============================================================================ - 'No MCP servers configured.': '未配置 MCP 服务器', - '⏳ MCP servers are starting up ({{count}} initializing)...': - '⏳ MCP 服务器正在启动({{count}} 个正在初始化)...', - 'Note: First startup may take longer. Tool availability will update automatically.': - '注意:首次启动可能需要更长时间。工具可用性将自动更新', - 'Configured MCP servers:': '已配置的 MCP 服务器:', - Ready: '就绪', - 'Starting... (first startup may take longer)': - '正在启动...(首次启动可能需要更长时间)', - Disconnected: '已断开连接', - '{{count}} tool': '{{count}} 个工具', - '{{count}} tools': '{{count}} 个工具', - '{{count}} prompt': '{{count}} 个提示', - '{{count}} prompts': '{{count}} 个提示', - '(from {{extensionName}})': '(来自 {{extensionName}})', - OAuth: 'OAuth', - 'OAuth expired': 'OAuth 已过期', - 'OAuth not authenticated': 'OAuth 未认证', - 'tools and prompts will appear when ready': '工具和提示将在就绪时显示', - '{{count}} tools cached': '{{count}} 个工具已缓存', - 'Tools:': '工具:', - 'Parameters:': '参数:', - 'Prompts:': '提示:', - Blocked: '已阻止', - '💡 Tips:': '💡 提示:', - Use: '使用', - 'to show server and tool descriptions': '显示服务器和工具描述', - 'to show tool parameter schemas': '显示工具参数架构', - 'to hide descriptions': '隐藏描述', - 'to authenticate with OAuth-enabled servers': - '使用支持 OAuth 的服务器进行认证', - Press: '按', - 'to toggle tool descriptions on/off': '切换工具描述开关', + "No MCP servers configured.": "未配置 MCP 服务器", + "⏳ MCP servers are starting up ({{count}} initializing)...": + "⏳ MCP 服务器正在启动({{count}} 个正在初始化)...", + "Note: First startup may take longer. Tool availability will update automatically.": + "注意:首次启动可能需要更长时间。工具可用性将自动更新", + "Configured MCP servers:": "已配置的 MCP 服务器:", + Ready: "就绪", + "Starting... (first startup may take longer)": "正在启动...(首次启动可能需要更长时间)", + Disconnected: "已断开连接", + "{{count}} tool": "{{count}} 个工具", + "{{count}} tools": "{{count}} 个工具", + "{{count}} prompt": "{{count}} 个提示", + "{{count}} prompts": "{{count}} 个提示", + "(from {{extensionName}})": "(来自 {{extensionName}})", + OAuth: "OAuth", + "OAuth expired": "OAuth 已过期", + "OAuth not authenticated": "OAuth 未认证", + "tools and prompts will appear when ready": "工具和提示将在就绪时显示", + "{{count}} tools cached": "{{count}} 个工具已缓存", + "Tools:": "工具:", + "Parameters:": "参数:", + "Prompts:": "提示:", + Blocked: "已阻止", + "💡 Tips:": "💡 提示:", + Use: "使用", + "to show server and tool descriptions": "显示服务器和工具描述", + "to show tool parameter schemas": "显示工具参数架构", + "to hide descriptions": "隐藏描述", + "to authenticate with OAuth-enabled servers": "使用支持 OAuth 的服务器进行认证", + Press: "按", + "to toggle tool descriptions on/off": "切换工具描述开关", "Starting OAuth authentication for MCP server '{{name}}'...": "正在为 MCP 服务器 '{{name}}' 启动 OAuth 认证...", - 'Restarting MCP servers...': '正在重启 MCP 服务器...', + "Restarting MCP servers...": "正在重启 MCP 服务器...", // ============================================================================ // Startup Tips // ============================================================================ - 'Tips:': '提示:', - 'Use /compress when the conversation gets long to summarize history and free up context.': - '对话变长时用 /compress,总结历史并释放上下文。', - 'Start a fresh idea with /clear or /new; the previous session stays available in history.': - '用 /clear 或 /new 开启新思路;之前的会话会保留在历史记录中。', - 'Use /bug to submit issues to the maintainers when something goes off.': - '遇到问题时,用 /bug 将问题提交给维护者。', - 'Switch auth type quickly with /auth.': '用 /auth 快速切换认证方式。', - 'You can run any shell commands from Qwen Code using ! (e.g. !ls).': - '在 Qwen Code 中使用 ! 可运行任意 shell 命令(例如 !ls)。', - 'Type / to open the command popup; Tab autocompletes slash commands and saved prompts.': - '输入 / 打开命令弹窗;按 Tab 自动补全斜杠命令和保存的提示词。', - 'You can resume a previous conversation by running qwen --continue or qwen --resume.': - '运行 qwen --continue 或 qwen --resume 可继续之前的会话。', - 'You can switch permission mode quickly with Shift+Tab or /approval-mode.': - '按 Shift+Tab 或输入 /approval-mode 可快速切换权限模式。', - 'You can switch permission mode quickly with Tab or /approval-mode.': - '按 Tab 或输入 /approval-mode 可快速切换权限模式。', - 'Try /insight to generate personalized insights from your chat history.': - '试试 /insight,从聊天记录中生成个性化洞察。', + "Tips:": "提示:", + "Use /compress when the conversation gets long to summarize history and free up context.": + "对话变长时用 /compress,总结历史并释放上下文。", + "Start a fresh idea with /clear or /new; the previous session stays available in history.": + "用 /clear 或 /new 开启新思路;之前的会话会保留在历史记录中。", + "Use /bug to submit issues to the maintainers when something goes off.": + "遇到问题时,用 /bug 将问题提交给维护者。", + "Switch auth type quickly with /auth.": "用 /auth 快速切换认证方式。", + "You can run any shell commands from Qwen Code using ! (e.g. !ls).": + "在 Qwen Code 中使用 ! 可运行任意 shell 命令(例如 !ls)。", + "Type / to open the command popup; Tab autocompletes slash commands and saved prompts.": + "输入 / 打开命令弹窗;按 Tab 自动补全斜杠命令和保存的提示词。", + "You can resume a previous conversation by running qwen --continue or qwen --resume.": + "运行 qwen --continue 或 qwen --resume 可继续之前的会话。", + "You can switch permission mode quickly with Shift+Tab or /approval-mode.": + "按 Shift+Tab 或输入 /approval-mode 可快速切换权限模式。", + "You can switch permission mode quickly with Tab or /approval-mode.": + "按 Tab 或输入 /approval-mode 可快速切换权限模式。", + "Try /insight to generate personalized insights from your chat history.": + "试试 /insight,从聊天记录中生成个性化洞察。", // ============================================================================ // Exit Screen / Stats // ============================================================================ - 'Agent powering down. Goodbye!': 'Qwen Code 正在关闭,再见!', - 'To continue this session, run': '要继续此会话,请运行', - 'Interaction Summary': '交互摘要', - 'Session ID:': '会话 ID:', - 'Tool Calls:': '工具调用:', - 'Success Rate:': '成功率:', - 'User Agreement:': '用户同意率:', - reviewed: '已审核', - 'Code Changes:': '代码变更:', - Performance: '性能', - 'Wall Time:': '总耗时:', - 'Agent Active:': '智能体活跃时间:', - 'API Time:': 'API 时间:', - 'Tool Time:': '工具时间:', - 'Session Stats': '会话统计', - 'Model Usage': '模型使用情况', - Reqs: '请求数', - 'Input Tokens': '输入 token 数', - 'Output Tokens': '输出 token 数', - 'Savings Highlight:': '节省亮点:', - 'of input tokens were served from the cache, reducing costs.': - '从缓存载入 token ,降低了成本', - 'Tip: For a full token breakdown, run `/stats model`.': - '提示:要查看完整的令牌明细,请运行 `/stats model`', - 'Model Stats For Nerds': '模型统计(技术细节)', - 'Tool Stats For Nerds': '工具统计(技术细节)', - Metric: '指标', - API: 'API', - Requests: '请求数', - Errors: '错误数', - 'Avg Latency': '平均延迟', - Tokens: '令牌', - Total: '总计', - Prompt: '提示', - Cached: '缓存', - Thoughts: '思考', - Tool: '工具', - Output: '输出', - 'No API calls have been made in this session.': - '本次会话中未进行任何 API 调用', - 'Tool Name': '工具名称', - Calls: '调用次数', - 'Success Rate': '成功率', - 'Avg Duration': '平均耗时', - 'User Decision Summary': '用户决策摘要', - 'Total Reviewed Suggestions:': '已审核建议总数:', - ' » Accepted:': ' » 已接受:', - ' » Rejected:': ' » 已拒绝:', - ' » Modified:': ' » 已修改:', - ' Overall Agreement Rate:': ' 总体同意率:', - 'No tool calls have been made in this session.': - '本次会话中未进行任何工具调用', - 'Session start time is unavailable, cannot calculate stats.': - '会话开始时间不可用,无法计算统计信息', + "Agent powering down. Goodbye!": "Qwen Code 正在关闭,再见!", + "To continue this session, run": "要继续此会话,请运行", + "Interaction Summary": "交互摘要", + "Session ID:": "会话 ID:", + "Tool Calls:": "工具调用:", + "Success Rate:": "成功率:", + "User Agreement:": "用户同意率:", + reviewed: "已审核", + "Code Changes:": "代码变更:", + Performance: "性能", + "Wall Time:": "总耗时:", + "Agent Active:": "智能体活跃时间:", + "API Time:": "API 时间:", + "Tool Time:": "工具时间:", + "Session Stats": "会话统计", + "Model Usage": "模型使用情况", + Reqs: "请求数", + "Input Tokens": "输入 token 数", + "Output Tokens": "输出 token 数", + "Savings Highlight:": "节省亮点:", + "of input tokens were served from the cache, reducing costs.": "从缓存载入 token ,降低了成本", + "Tip: For a full token breakdown, run `/stats model`.": + "提示:要查看完整的令牌明细,请运行 `/stats model`", + "Model Stats For Nerds": "模型统计(技术细节)", + "Tool Stats For Nerds": "工具统计(技术细节)", + Metric: "指标", + API: "API", + Requests: "请求数", + Errors: "错误数", + "Avg Latency": "平均延迟", + Tokens: "令牌", + Total: "总计", + Prompt: "提示", + Cached: "缓存", + Thoughts: "思考", + Tool: "工具", + Output: "输出", + "No API calls have been made in this session.": "本次会话中未进行任何 API 调用", + "Tool Name": "工具名称", + Calls: "调用次数", + "Success Rate": "成功率", + "Avg Duration": "平均耗时", + "User Decision Summary": "用户决策摘要", + "Total Reviewed Suggestions:": "已审核建议总数:", + " » Accepted:": " » 已接受:", + " » Rejected:": " » 已拒绝:", + " » Modified:": " » 已修改:", + " Overall Agreement Rate:": " 总体同意率:", + "No tool calls have been made in this session.": "本次会话中未进行任何工具调用", + "Session start time is unavailable, cannot calculate stats.": + "会话开始时间不可用,无法计算统计信息", // ============================================================================ // Command Format Migration // ============================================================================ - 'Command Format Migration': '命令格式迁移', - 'Found {{count}} TOML command file:': '发现 {{count}} 个 TOML 命令文件:', - 'Found {{count}} TOML command files:': '发现 {{count}} 个 TOML 命令文件:', - '... and {{count}} more': '... 以及其他 {{count}} 个', - 'The TOML format is deprecated. Would you like to migrate them to Markdown format?': - 'TOML 格式已弃用。是否将它们迁移到 Markdown 格式?', - '(Backups will be created and original files will be preserved)': - '(将创建备份,原始文件将保留)', + "Command Format Migration": "命令格式迁移", + "Found {{count}} TOML command file:": "发现 {{count}} 个 TOML 命令文件:", + "Found {{count}} TOML command files:": "发现 {{count}} 个 TOML 命令文件:", + "... and {{count}} more": "... 以及其他 {{count}} 个", + "The TOML format is deprecated. Would you like to migrate them to Markdown format?": + "TOML 格式已弃用。是否将它们迁移到 Markdown 格式?", + "(Backups will be created and original files will be preserved)": + "(将创建备份,原始文件将保留)", // ============================================================================ // Loading Phrases // ============================================================================ - 'Waiting for user confirmation...': '等待用户确认...', - '(esc to cancel, {{time}})': '(按 esc 取消,{{time}})', + "Waiting for user confirmation...": "等待用户确认...", + "(esc to cancel, {{time}})": "(按 esc 取消,{{time}})", WITTY_LOADING_PHRASES: [ // --- 职场搬砖系列 --- - '正在努力搬砖,请稍候...', - '老板在身后,快加载啊!', - '头发掉光前,一定能加载完...', - '服务器正在深呼吸,准备放大招...', - '正在向服务器投喂咖啡...', + "正在努力搬砖,请稍候...", + "老板在身后,快加载啊!", + "头发掉光前,一定能加载完...", + "服务器正在深呼吸,准备放大招...", + "正在向服务器投喂咖啡...", // --- 大厂黑话系列 --- - '正在赋能全链路,寻找关键抓手...', - '正在降本增效,优化加载路径...', - '正在打破部门壁垒,沉淀方法论...', - '正在拥抱变化,迭代核心价值...', - '正在对齐颗粒度,打磨底层逻辑...', - '大力出奇迹,正在强行加载...', + "正在赋能全链路,寻找关键抓手...", + "正在降本增效,优化加载路径...", + "正在打破部门壁垒,沉淀方法论...", + "正在拥抱变化,迭代核心价值...", + "正在对齐颗粒度,打磨底层逻辑...", + "大力出奇迹,正在强行加载...", // --- 程序员自嘲系列 --- - '只要我不写代码,代码就没有 Bug...', - '正在把 Bug 转化为 Feature...', - '只要我不尴尬,Bug 就追不上我...', - '正在试图理解去年的自己写了什么...', - '正在猿力觉醒中,请耐心等待...', + "只要我不写代码,代码就没有 Bug...", + "正在把 Bug 转化为 Feature...", + "只要我不尴尬,Bug 就追不上我...", + "正在试图理解去年的自己写了什么...", + "正在猿力觉醒中,请耐心等待...", // --- 合作愉快系列 --- - '正在询问产品经理:这需求是真的吗?', - '正在给产品经理画饼,请稍等...', + "正在询问产品经理:这需求是真的吗?", + "正在给产品经理画饼,请稍等...", // --- 温暖治愈系列 --- - '每一行代码,都在努力让世界变得更好一点点...', - '每一个伟大的想法,都值得这份耐心的等待...', - '别急,美好的事物总是需要一点时间去酝酿...', - '愿你的代码永无 Bug,愿你的梦想终将成真...', - '哪怕只有 0.1% 的进度,也是在向目标靠近...', - '加载的是字节,承载的是对技术的热爱...', + "每一行代码,都在努力让世界变得更好一点点...", + "每一个伟大的想法,都值得这份耐心的等待...", + "别急,美好的事物总是需要一点时间去酝酿...", + "愿你的代码永无 Bug,愿你的梦想终将成真...", + "哪怕只有 0.1% 的进度,也是在向目标靠近...", + "加载的是字节,承载的是对技术的热爱...", ], // ============================================================================ // Extension Settings Input // ============================================================================ - 'Enter value...': '请输入值...', - 'Enter sensitive value...': '请输入敏感值...', - 'Press Enter to submit, Escape to cancel': '按 Enter 提交,Escape 取消', + "Enter value...": "请输入值...", + "Enter sensitive value...": "请输入敏感值...", + "Press Enter to submit, Escape to cancel": "按 Enter 提交,Escape 取消", // ============================================================================ // Command Migration Tool // ============================================================================ - 'Markdown file already exists: {{filename}}': - 'Markdown 文件已存在:{{filename}}', - 'TOML Command Format Deprecation Notice': 'TOML 命令格式弃用通知', - 'Found {{count}} command file(s) in TOML format:': - '发现 {{count}} 个 TOML 格式的命令文件:', - 'The TOML format for commands is being deprecated in favor of Markdown format.': - '命令的 TOML 格式正在被弃用,推荐使用 Markdown 格式。', - 'Markdown format is more readable and easier to edit.': - 'Markdown 格式更易读、更易编辑。', - 'You can migrate these files automatically using:': - '您可以使用以下命令自动迁移这些文件:', - 'Or manually convert each file:': '或手动转换每个文件:', - 'TOML: prompt = "..." / description = "..."': - 'TOML:prompt = "..." / description = "..."', - 'Markdown: YAML frontmatter + content': 'Markdown:YAML frontmatter + 内容', - 'The migration tool will:': '迁移工具将:', - 'Convert TOML files to Markdown': '将 TOML 文件转换为 Markdown', - 'Create backups of original files': '创建原始文件的备份', - 'Preserve all command functionality': '保留所有命令功能', - 'TOML format will continue to work for now, but migration is recommended.': - 'TOML 格式目前仍可使用,但建议迁移。', + "Markdown file already exists: {{filename}}": "Markdown 文件已存在:{{filename}}", + "TOML Command Format Deprecation Notice": "TOML 命令格式弃用通知", + "Found {{count}} command file(s) in TOML format:": "发现 {{count}} 个 TOML 格式的命令文件:", + "The TOML format for commands is being deprecated in favor of Markdown format.": + "命令的 TOML 格式正在被弃用,推荐使用 Markdown 格式。", + "Markdown format is more readable and easier to edit.": "Markdown 格式更易读、更易编辑。", + "You can migrate these files automatically using:": "您可以使用以下命令自动迁移这些文件:", + "Or manually convert each file:": "或手动转换每个文件:", + 'TOML: prompt = "..." / description = "..."': 'TOML:prompt = "..." / description = "..."', + "Markdown: YAML frontmatter + content": "Markdown:YAML frontmatter + 内容", + "The migration tool will:": "迁移工具将:", + "Convert TOML files to Markdown": "将 TOML 文件转换为 Markdown", + "Create backups of original files": "创建原始文件的备份", + "Preserve all command functionality": "保留所有命令功能", + "TOML format will continue to work for now, but migration is recommended.": + "TOML 格式目前仍可使用,但建议迁移。", // ============================================================================ // Extensions - Explore Command // ============================================================================ - 'Open extensions page in your browser': '在浏览器中打开扩展市场页面', - 'Unknown extensions source: {{source}}.': '未知的扩展来源:{{source}}。', - 'Would open extensions page in your browser: {{url}} (skipped in test environment)': - '将在浏览器中打开扩展页面:{{url}}(测试环境中已跳过)', - 'View available extensions at {{url}}': '在 {{url}} 查看可用扩展', - 'Opening extensions page in your browser: {{url}}': - '正在浏览器中打开扩展页面:{{url}}', - 'Failed to open browser. Check out the extensions gallery at {{url}}': - '打开浏览器失败。请访问扩展市场:{{url}}', + "Open extensions page in your browser": "在浏览器中打开扩展市场页面", + "Unknown extensions source: {{source}}.": "未知的扩展来源:{{source}}。", + "Would open extensions page in your browser: {{url}} (skipped in test environment)": + "将在浏览器中打开扩展页面:{{url}}(测试环境中已跳过)", + "View available extensions at {{url}}": "在 {{url}} 查看可用扩展", + "Opening extensions page in your browser: {{url}}": "正在浏览器中打开扩展页面:{{url}}", + "Failed to open browser. Check out the extensions gallery at {{url}}": + "打开浏览器失败。请访问扩展市场:{{url}}", // ============================================================================ // Retry / Rate Limit // ============================================================================ - 'Rate limit error: {{reason}}': '触发限流:{{reason}}', - 'Retrying in {{seconds}} seconds… (attempt {{attempt}}/{{maxRetries}})': - '将于 {{seconds}} 秒后重试…(第 {{attempt}}/{{maxRetries}} 次)', - 'Press Ctrl+Y to retry': '按 Ctrl+Y 重试。', - 'No failed request to retry.': '没有可重试的失败请求。', - 'to retry last request': '重试上一次请求', + "Rate limit error: {{reason}}": "触发限流:{{reason}}", + "Retrying in {{seconds}} seconds… (attempt {{attempt}}/{{maxRetries}})": + "将于 {{seconds}} 秒后重试…(第 {{attempt}}/{{maxRetries}} 次)", + "Press Ctrl+Y to retry": "按 Ctrl+Y 重试。", + "No failed request to retry.": "没有可重试的失败请求。", + "to retry last request": "重试上一次请求", // ============================================================================ // Coding Plan Authentication // ============================================================================ - 'API key cannot be empty.': 'API Key 不能为空。', + "API key cannot be empty.": "API Key 不能为空。", 'Invalid API key. Coding Plan API keys start with "sk-sp-". Please check.': '无效的 API Key,Coding Plan API Key 均以 "sk-sp-" 开头,请检查', - 'You can get your Coding Plan API key here': - '您可以在这里获取 Coding Plan API Key', - 'API key is stored in settings.env. You can migrate it to a .env file for better security.': - 'API Key 已存储在 settings.env 中。您可以将其迁移到 .env 文件以获得更好的安全性。', - 'New model configurations are available for Alibaba Cloud Coding Plan. Update now?': - '阿里云百炼 Coding Plan 有新模型配置可用。是否立即更新?', - 'Coding Plan configuration updated successfully. New models are now available.': - 'Coding Plan 配置更新成功。新模型现已可用。', - 'Coding Plan API key not found. Please re-authenticate with Coding Plan.': - '未找到 Coding Plan API Key。请重新通过 Coding Plan 认证。', - 'Failed to update Coding Plan configuration: {{message}}': - '更新 Coding Plan 配置失败:{{message}}', + "You can get your Coding Plan API key here": "您可以在这里获取 Coding Plan API Key", + "API key is stored in settings.env. You can migrate it to a .env file for better security.": + "API Key 已存储在 settings.env 中。您可以将其迁移到 .env 文件以获得更好的安全性。", + "New model configurations are available for Alibaba Cloud Coding Plan. Update now?": + "阿里云百炼 Coding Plan 有新模型配置可用。是否立即更新?", + "Coding Plan configuration updated successfully. New models are now available.": + "Coding Plan 配置更新成功。新模型现已可用。", + "Coding Plan API key not found. Please re-authenticate with Coding Plan.": + "未找到 Coding Plan API Key。请重新通过 Coding Plan 认证。", + "Failed to update Coding Plan configuration: {{message}}": + "更新 Coding Plan 配置失败:{{message}}", // ============================================================================ // Custom API Key Configuration // ============================================================================ - 'You can configure your API key and models in settings.json': - '您可以在 settings.json 中配置 API Key 和模型', - 'Refer to the documentation for setup instructions': '请参考文档了解配置说明', + "You can configure your API key and models in settings.json": + "您可以在 settings.json 中配置 API Key 和模型", + "Refer to the documentation for setup instructions": "请参考文档了解配置说明", // ============================================================================ // Auth Dialog - View Titles and Labels // ============================================================================ - 'API-KEY': 'API-KEY', - 'Coding Plan': 'Coding Plan', + "API-KEY": "API-KEY", + "Coding Plan": "Coding Plan", "Paste your api key of ModelStudio Coding Plan and you're all set!": - '粘贴您的百炼 Coding Plan API Key,即可完成设置!', - Custom: '自定义', - 'More instructions about configuring `modelProviders` manually.': - '关于手动配置 `modelProviders` 的更多说明。', - 'Select API-KEY configuration mode:': '选择 API-KEY 配置模式:', - '(Press Escape to go back)': '(按 Escape 键返回)', - '(Press Enter to submit, Escape to cancel)': '(按 Enter 提交,Escape 取消)', - 'Select Region for Coding Plan': '选择 Coding Plan 区域', - 'Choose based on where your account is registered': - '请根据您的账号注册地区选择', - 'Enter Coding Plan API Key': '输入 Coding Plan API Key', + "粘贴您的百炼 Coding Plan API Key,即可完成设置!", + Custom: "自定义", + "More instructions about configuring `modelProviders` manually.": + "关于手动配置 `modelProviders` 的更多说明。", + "Select API-KEY configuration mode:": "选择 API-KEY 配置模式:", + "(Press Escape to go back)": "(按 Escape 键返回)", + "(Press Enter to submit, Escape to cancel)": "(按 Enter 提交,Escape 取消)", + "Select Region for Coding Plan": "选择 Coding Plan 区域", + "Choose based on where your account is registered": "请根据您的账号注册地区选择", + "Enter Coding Plan API Key": "输入 Coding Plan API Key", // ============================================================================ // Coding Plan International Updates // ============================================================================ - 'New model configurations are available for {{region}}. Update now?': - '{{region}} 有新的模型配置可用。是否立即更新?', + "New model configurations are available for {{region}}. Update now?": + "{{region}} 有新的模型配置可用。是否立即更新?", '{{region}} configuration updated successfully. Model switched to "{{model}}".': '{{region}} 配置更新成功。模型已切换至 "{{model}}"。', - 'Authenticated successfully with {{region}}. API key and model configs saved to settings.json (backed up).': - '成功通过 {{region}} 认证。API Key 和模型配置已保存至 settings.json(已备份)。', + "Authenticated successfully with {{region}}. API key and model configs saved to settings.json (backed up).": + "成功通过 {{region}} 认证。API Key 和模型配置已保存至 settings.json(已备份)。", // ============================================================================ // Context Usage // ============================================================================ - 'Context Usage': '上下文使用情况', - 'Context window': '上下文窗口', - Used: '已用', - Free: '空闲', - 'Autocompact buffer': '自动压缩缓冲区', - 'Usage by category': '分类用量', - 'System prompt': '系统提示', - 'Built-in tools': '内置工具', - 'MCP tools': 'MCP 工具', - 'Memory files': '记忆文件', - Skills: '技能', - Messages: '消息', - tokens: 'tokens', - 'Estimated pre-conversation overhead': '预估对话前开销', - 'No API response yet. Send a message to see actual usage.': - '暂无 API 响应。发送消息以查看实际使用情况。', - 'Show context window usage breakdown.': '显示上下文窗口使用情况分解。', - 'Run /context detail for per-item breakdown.': - '运行 /context detail 查看详细分解。', - 'body loaded': '内容已加载', - memory: '记忆', - '{{region}} configuration updated successfully.': '{{region}} 配置更新成功。', - 'Authenticated successfully with {{region}}. API key and model configs saved to settings.json.': - '成功通过 {{region}} 认证。API Key 和模型配置已保存至 settings.json。', - 'Tip: Use /model to switch between available Coding Plan models.': - '提示:使用 /model 切换可用的 Coding Plan 模型。', + "Context Usage": "上下文使用情况", + "Context window": "上下文窗口", + Used: "已用", + Free: "空闲", + "Autocompact buffer": "自动压缩缓冲区", + "Usage by category": "分类用量", + "System prompt": "系统提示", + "Built-in tools": "内置工具", + "MCP tools": "MCP 工具", + "Memory files": "记忆文件", + Skills: "技能", + Messages: "消息", + tokens: "tokens", + "Estimated pre-conversation overhead": "预估对话前开销", + "No API response yet. Send a message to see actual usage.": + "暂无 API 响应。发送消息以查看实际使用情况。", + "Show context window usage breakdown.": "显示上下文窗口使用情况分解。", + "Run /context detail for per-item breakdown.": "运行 /context detail 查看详细分解。", + "body loaded": "内容已加载", + memory: "记忆", + "{{region}} configuration updated successfully.": "{{region}} 配置更新成功。", + "Authenticated successfully with {{region}}. API key and model configs saved to settings.json.": + "成功通过 {{region}} 认证。API Key 和模型配置已保存至 settings.json。", + "Tip: Use /model to switch between available Coding Plan models.": + "提示:使用 /model 切换可用的 Coding Plan 模型。", // ============================================================================ // Ask User Question Tool // ============================================================================ - 'Please answer the following question(s):': '请回答以下问题:', - 'Cannot ask user questions in non-interactive mode. Please run in interactive mode to use this tool.': - '无法在非交互模式下询问用户问题。请在交互模式下运行以使用此工具。', - 'User declined to answer the questions.': '用户拒绝回答问题。', - 'User has provided the following answers:': '用户提供了以下答案:', - 'Failed to process user answers:': '处理用户答案失败:', - 'Type something...': '输入内容...', - Submit: '提交', - 'Submit answers': '提交答案', - Cancel: '取消', - 'Your answers:': '您的答案:', - '(not answered)': '(未回答)', - 'Ready to submit your answers?': '准备好提交您的答案了吗?', - '↑/↓: Navigate | ←/→: Switch tabs | Enter: Select': - '↑/↓: 导航 | ←/→: 切换标签页 | Enter: 选择', - '↑/↓: Navigate | ←/→: Switch tabs | Space/Enter: Toggle | Esc: Cancel': - '↑/↓: 导航 | ←/→: 切换标签页 | Space/Enter: 切换 | Esc: 取消', - '↑/↓: Navigate | Space/Enter: Toggle | Esc: Cancel': - '↑/↓: 导航 | Space/Enter: 切换 | Esc: 取消', - '↑/↓: Navigate | Enter: Select | Esc: Cancel': - '↑/↓: 导航 | Enter: 选择 | Esc: 取消', + "Please answer the following question(s):": "请回答以下问题:", + "Cannot ask user questions in non-interactive mode. Please run in interactive mode to use this tool.": + "无法在非交互模式下询问用户问题。请在交互模式下运行以使用此工具。", + "User declined to answer the questions.": "用户拒绝回答问题。", + "User has provided the following answers:": "用户提供了以下答案:", + "Failed to process user answers:": "处理用户答案失败:", + "Type something...": "输入内容...", + Submit: "提交", + "Submit answers": "提交答案", + Cancel: "取消", + "Your answers:": "您的答案:", + "(not answered)": "(未回答)", + "Ready to submit your answers?": "准备好提交您的答案了吗?", + "↑/↓: Navigate | ←/→: Switch tabs | Enter: Select": "↑/↓: 导航 | ←/→: 切换标签页 | Enter: 选择", + "↑/↓: Navigate | ←/→: Switch tabs | Space/Enter: Toggle | Esc: Cancel": + "↑/↓: 导航 | ←/→: 切换标签页 | Space/Enter: 切换 | Esc: 取消", + "↑/↓: Navigate | Space/Enter: Toggle | Esc: Cancel": "↑/↓: 导航 | Space/Enter: 切换 | Esc: 取消", + "↑/↓: Navigate | Enter: Select | Esc: Cancel": "↑/↓: 导航 | Enter: 选择 | Esc: 取消", // ============================================================================ // Commands - Auth // ============================================================================ - 'Authenticate using Alibaba Cloud Coding Plan': - '使用阿里云百炼 Coding Plan 进行认证', - 'Region for Coding Plan (china/global)': 'Coding Plan 区域 (china/global)', - 'API key for Coding Plan': 'Coding Plan 的 API 密钥', - 'Show current authentication status': '显示当前认证状态', - 'Authentication completed successfully.': '认证完成。', - 'Processing Alibaba Cloud Coding Plan authentication...': - '正在处理阿里云百炼 Coding Plan 认证...', - 'Successfully authenticated with Alibaba Cloud Coding Plan.': - '已成功通过阿里云百炼 Coding Plan 认证。', - 'Failed to authenticate with Coding Plan: {{error}}': - 'Coding Plan 认证失败:{{error}}', - '中国 (China)': '中国 (China)', - '阿里云百炼 (aliyun.com)': '阿里云百炼 (aliyun.com)', - Global: '全球', - 'Alibaba Cloud (alibabacloud.com)': 'Alibaba Cloud (alibabacloud.com)', - 'Select region for Coding Plan:': '选择 Coding Plan 区域:', - 'Enter your Coding Plan API key: ': '请输入您的 Coding Plan API 密钥:', - 'Select authentication method:': '选择认证方式:', - '\n=== Authentication Status ===\n': '\n=== 认证状态 ===\n', - '⚠️ No authentication method configured.\n': '⚠️ 未配置认证方式。\n', - 'Run one of the following commands to get started:\n': - '运行以下命令之一开始配置:\n', - ' qwen auth coding-plan - Authenticate with Alibaba Cloud Coding Plan\n': - ' qwen auth coding-plan - 使用阿里云百炼 Coding Plan 认证\n', - 'Or simply run:': '或者直接运行:', - ' qwen auth - Interactive authentication setup\n': - ' qwen auth - 交互式认证配置\n', - '✓ Authentication Method: Alibaba Cloud Coding Plan': - '✓ 认证方式:阿里云百炼 Coding Plan', - '中国 (China) - 阿里云百炼': '中国 (China) - 阿里云百炼', - 'Global - Alibaba Cloud': '全球 - Alibaba Cloud', - ' Region: {{region}}': ' 区域:{{region}}', - ' Current Model: {{model}}': ' 当前模型:{{model}}', - ' Config Version: {{version}}': ' 配置版本:{{version}}', - ' Status: API key configured\n': ' 状态:API 密钥已配置\n', - '⚠️ Authentication Method: Alibaba Cloud Coding Plan (Incomplete)': - '⚠️ 认证方式:阿里云百炼 Coding Plan(不完整)', - ' Issue: API key not found in environment or settings\n': - ' 问题:在环境变量或设置中未找到 API 密钥\n', - ' Run `qwen auth coding-plan` to re-configure.\n': - ' 运行 `qwen auth coding-plan` 重新配置。\n', - '✓ Authentication Method: {{type}}': '✓ 认证方式:{{type}}', - ' Status: Configured\n': ' 状态:已配置\n', - 'Failed to check authentication status: {{error}}': - '检查认证状态失败:{{error}}', - 'Select an option:': '请选择:', - 'Raw mode not available. Please run in an interactive terminal.': - '原始模式不可用。请在交互式终端中运行。', - '(Use ↑ ↓ arrows to navigate, Enter to select, Ctrl+C to exit)\n': - '(使用 ↑ ↓ 箭头导航,Enter 选择,Ctrl+C 退出)\n', - verbose: '详细', - 'Show full tool output and thinking in verbose mode (toggle with Ctrl+O).': - '详细模式下显示完整工具输出和思考过程(Ctrl+O 切换)。', - 'Press Ctrl+O to show full tool output': '按 Ctrl+O 查看详细工具调用结果', + "Authenticate using Alibaba Cloud Coding Plan": "使用阿里云百炼 Coding Plan 进行认证", + "Region for Coding Plan (china/global)": "Coding Plan 区域 (china/global)", + "API key for Coding Plan": "Coding Plan 的 API 密钥", + "Show current authentication status": "显示当前认证状态", + "Authentication completed successfully.": "认证完成。", + "Processing Alibaba Cloud Coding Plan authentication...": + "正在处理阿里云百炼 Coding Plan 认证...", + "Successfully authenticated with Alibaba Cloud Coding Plan.": + "已成功通过阿里云百炼 Coding Plan 认证。", + "Failed to authenticate with Coding Plan: {{error}}": "Coding Plan 认证失败:{{error}}", + "中国 (China)": "中国 (China)", + "阿里云百炼 (aliyun.com)": "阿里云百炼 (aliyun.com)", + Global: "全球", + "Alibaba Cloud (alibabacloud.com)": "Alibaba Cloud (alibabacloud.com)", + "Select region for Coding Plan:": "选择 Coding Plan 区域:", + "Enter your Coding Plan API key: ": "请输入您的 Coding Plan API 密钥:", + "Select authentication method:": "选择认证方式:", + "\n=== Authentication Status ===\n": "\n=== 认证状态 ===\n", + "⚠️ No authentication method configured.\n": "⚠️ 未配置认证方式。\n", + "Run one of the following commands to get started:\n": "运行以下命令之一开始配置:\n", + " qwen auth coding-plan - Authenticate with Alibaba Cloud Coding Plan\n": + " qwen auth coding-plan - 使用阿里云百炼 Coding Plan 认证\n", + "Or simply run:": "或者直接运行:", + " qwen auth - Interactive authentication setup\n": + " qwen auth - 交互式认证配置\n", + "✓ Authentication Method: Alibaba Cloud Coding Plan": "✓ 认证方式:阿里云百炼 Coding Plan", + "中国 (China) - 阿里云百炼": "中国 (China) - 阿里云百炼", + "Global - Alibaba Cloud": "全球 - Alibaba Cloud", + " Region: {{region}}": " 区域:{{region}}", + " Current Model: {{model}}": " 当前模型:{{model}}", + " Config Version: {{version}}": " 配置版本:{{version}}", + " Status: API key configured\n": " 状态:API 密钥已配置\n", + "⚠️ Authentication Method: Alibaba Cloud Coding Plan (Incomplete)": + "⚠️ 认证方式:阿里云百炼 Coding Plan(不完整)", + " Issue: API key not found in environment or settings\n": + " 问题:在环境变量或设置中未找到 API 密钥\n", + " Run `qwen auth coding-plan` to re-configure.\n": " 运行 `qwen auth coding-plan` 重新配置。\n", + "✓ Authentication Method: {{type}}": "✓ 认证方式:{{type}}", + " Status: Configured\n": " 状态:已配置\n", + "Failed to check authentication status: {{error}}": "检查认证状态失败:{{error}}", + "Select an option:": "请选择:", + "Raw mode not available. Please run in an interactive terminal.": + "原始模式不可用。请在交互式终端中运行。", + "(Use ↑ ↓ arrows to navigate, Enter to select, Ctrl+C to exit)\n": + "(使用 ↑ ↓ 箭头导航,Enter 选择,Ctrl+C 退出)\n", + verbose: "详细", + "Show full tool output and thinking in verbose mode (toggle with Ctrl+O).": + "详细模式下显示完整工具输出和思考过程(Ctrl+O 切换)。", + "Press Ctrl+O to show full tool output": "按 Ctrl+O 查看详细工具调用结果", - 'Switch to plan mode or exit plan mode': '切换到计划模式或退出计划模式', - 'Exited plan mode. Previous approval mode restored.': - '已退出计划模式,已恢复之前的审批模式。', - 'Enabled plan mode. The agent will analyze and plan without executing tools.': - '启用计划模式。智能体将只分析和规划,而不执行工具。', + "Switch to plan mode or exit plan mode": "切换到计划模式或退出计划模式", + "Exited plan mode. Previous approval mode restored.": "已退出计划模式,已恢复之前的审批模式。", + "Enabled plan mode. The agent will analyze and plan without executing tools.": + "启用计划模式。智能体将只分析和规划,而不执行工具。", 'Already in plan mode. Use "/plan exit" to exit plan mode.': '已处于计划模式。使用 "/plan exit" 退出计划模式。', 'Not in plan mode. Use "/plan" to enter plan mode first.': '未处于计划模式。请先使用 "/plan" 进入计划模式。', - "Set up Qwen Code's status line UI": '配置 Qwen Code 的状态栏', + "Set up Qwen Code's status line UI": "配置 Qwen Code 的状态栏", }; diff --git a/apps/airiscode-cli/src/index.ts b/apps/airiscode-cli/src/index.ts index be8e4f0e6..4992ab429 100644 --- a/apps/airiscode-cli/src/index.ts +++ b/apps/airiscode-cli/src/index.ts @@ -6,10 +6,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -import './gemini.js'; -import { main } from './gemini.js'; -import { FatalError } from '@airiscode/core'; -import { writeStderrLine } from './utils/stdioHelpers.js'; +import "./gemini.js"; +import { FatalError } from "@airiscode/core"; +import { main } from "./gemini.js"; +import { writeStderrLine } from "./utils/stdioHelpers.js"; // --- Global Entry Point --- @@ -22,11 +22,11 @@ import { writeStderrLine } from './utils/stdioHelpers.js'; // - https://github.com/microsoft/node-pty/issues/178 (EIO on macOS/Linux) // - https://github.com/microsoft/node-pty/issues/827 (resize on Windows) const getErrnoCode = (error: unknown): string | undefined => { - if (!error || typeof error !== 'object') { + if (!error || typeof error !== "object") { return undefined; } const code = (error as { code?: unknown }).code; - return typeof code === 'string' ? code : undefined; + return typeof code === "string" ? code : undefined; }; const isExpectedPtyRaceError = (error: unknown): boolean => { @@ -37,16 +37,13 @@ const isExpectedPtyRaceError = (error: unknown): boolean => { const message = error.message; const code = getErrnoCode(error); - if ( - (code === 'EIO' && message.includes('read')) || - message.includes('read EIO') - ) { + if ((code === "EIO" && message.includes("read")) || message.includes("read EIO")) { return true; } if ( - message.includes('ioctl(2) failed, EBADF') || - message.includes('Cannot resize a pty that has already exited') + message.includes("ioctl(2) failed, EBADF") || + message.includes("Cannot resize a pty that has already exited") ) { return true; } @@ -54,7 +51,7 @@ const isExpectedPtyRaceError = (error: unknown): boolean => { return false; }; -process.on('uncaughtException', (error) => { +process.on("uncaughtException", (error) => { if (isExpectedPtyRaceError(error)) { return; } @@ -70,13 +67,13 @@ process.on('uncaughtException', (error) => { main().catch((error) => { if (error instanceof FatalError) { let errorMessage = error.message; - if (!process.env['NO_COLOR']) { + if (!process.env["NO_COLOR"]) { errorMessage = `\x1b[31m${errorMessage}\x1b[0m`; } console.error(errorMessage); process.exit(error.exitCode); } - console.error('An unexpected critical error occurred:'); + console.error("An unexpected critical error occurred:"); if (error instanceof Error) { console.error(error.stack); } else { diff --git a/apps/airiscode-cli/src/nonInteractive/control/ControlContext.ts b/apps/airiscode-cli/src/nonInteractive/control/ControlContext.ts index bc284f70e..c35b20a6f 100644 --- a/apps/airiscode-cli/src/nonInteractive/control/ControlContext.ts +++ b/apps/airiscode-cli/src/nonInteractive/control/ControlContext.ts @@ -13,10 +13,10 @@ * runtime state (e.g. permission mode, active MCP clients). */ -import type { Config, MCPServerConfig } from '@airiscode/core'; -import type { Client } from '@modelcontextprotocol/sdk/client/index.js'; -import type { StreamJsonOutputAdapter } from '../io/StreamJsonOutputAdapter.js'; -import type { PermissionMode } from '../types.js'; +import type { Config, MCPServerConfig } from "@airiscode/core"; +import type { Client } from "@modelcontextprotocol/sdk/client/index.js"; +import type { StreamJsonOutputAdapter } from "../io/StreamJsonOutputAdapter.js"; +import type { PermissionMode } from "../types.js"; /** * Control Context interface @@ -70,7 +70,7 @@ export class ControlContext implements IControlContext { this.sessionId = options.sessionId; this.abortSignal = options.abortSignal; this.debugMode = options.config.getDebugMode(); - this.permissionMode = options.permissionMode || 'default'; + this.permissionMode = options.permissionMode || "default"; this.sdkMcpServers = new Set(); this.mcpClients = new Map(); this.inputClosed = false; diff --git a/apps/airiscode-cli/src/nonInteractive/control/ControlDispatcher.ts b/apps/airiscode-cli/src/nonInteractive/control/ControlDispatcher.ts index 7b579e3e1..63580e731 100644 --- a/apps/airiscode-cli/src/nonInteractive/control/ControlDispatcher.ts +++ b/apps/airiscode-cli/src/nonInteractive/control/ControlDispatcher.ts @@ -26,21 +26,21 @@ * enum in packages/sdk/typescript/src/types/controlRequests.ts */ -import type { IControlContext } from './ControlContext.js'; -import type { IPendingRequestRegistry } from './controllers/baseController.js'; -import { SystemController } from './controllers/systemController.js'; -import { PermissionController } from './controllers/permissionController.js'; -import { SdkMcpController } from './controllers/sdkMcpController.js'; +import { createDebugLogger } from "@airiscode/core"; // import { HookController } from './controllers/hookController.js'; import type { CLIControlRequest, CLIControlResponse, - ControlResponse, ControlRequestPayload, -} from '../types.js'; -import { createDebugLogger } from '@airiscode/core'; + ControlResponse, +} from "../types.js"; +import type { IControlContext } from "./ControlContext.js"; +import type { IPendingRequestRegistry } from "./controllers/baseController.js"; +import { PermissionController } from "./controllers/permissionController.js"; +import { SdkMcpController } from "./controllers/sdkMcpController.js"; +import { SystemController } from "./controllers/systemController.js"; -const debugLogger = createDebugLogger('CONTROL_DISPATCHER'); +const debugLogger = createDebugLogger("CONTROL_DISPATCHER"); /** * Tracks an incoming request from SDK awaiting CLI response @@ -75,10 +75,8 @@ export class ControlDispatcher implements IPendingRequestRegistry { // readonly hookController: HookController; // Central pending request registries - private pendingIncomingRequests: Map = - new Map(); - private pendingOutgoingRequests: Map = - new Map(); + private pendingIncomingRequests: Map = new Map(); + private pendingOutgoingRequests: Map = new Map(); private abortHandler: (() => void) | null = null; @@ -86,28 +84,16 @@ export class ControlDispatcher implements IPendingRequestRegistry { this.context = context; // Create domain controllers with context and registry - this.systemController = new SystemController( - context, - this, - 'SystemController', - ); - this.permissionController = new PermissionController( - context, - this, - 'PermissionController', - ); - this.sdkMcpController = new SdkMcpController( - context, - this, - 'SdkMcpController', - ); + this.systemController = new SystemController(context, this, "SystemController"); + this.permissionController = new PermissionController(context, this, "PermissionController"); + this.sdkMcpController = new SdkMcpController(context, this, "SdkMcpController"); // this.hookController = new HookController(context, this, 'HookController'); // Listen for main abort signal this.abortHandler = () => { this.shutdown(); }; - this.context.abortSignal.addEventListener('abort', this.abortHandler); + this.context.abortSignal.addEventListener("abort", this.abortHandler); } /** @@ -125,8 +111,7 @@ export class ControlDispatcher implements IPendingRequestRegistry { this.sendSuccessResponse(request_id, response); } catch (error) { // Send error response - const errorMessage = - error instanceof Error ? error.message : String(error); + const errorMessage = error instanceof Error ? error.message : String(error); this.sendErrorResponse(request_id, errorMessage); } } @@ -141,9 +126,7 @@ export class ControlDispatcher implements IPendingRequestRegistry { const pending = this.pendingOutgoingRequests.get(requestId); if (!pending) { // No pending request found - may have timed out or been cancelled - debugLogger.debug( - `[ControlDispatcher] No pending outgoing request for: ${requestId}`, - ); + debugLogger.debug(`[ControlDispatcher] No pending outgoing request for: ${requestId}`); return; } @@ -151,13 +134,13 @@ export class ControlDispatcher implements IPendingRequestRegistry { this.deregisterOutgoingRequest(requestId); // Resolve or reject based on response type - if (responsePayload.subtype === 'success') { + if (responsePayload.subtype === "success") { pending.resolve(responsePayload); } else { const errorMessage = - typeof responsePayload.error === 'string' + typeof responsePayload.error === "string" ? responsePayload.error - : (responsePayload.error?.message ?? 'Unknown error'); + : (responsePayload.error?.message ?? "Unknown error"); pending.reject(new Error(errorMessage)); } } @@ -183,11 +166,9 @@ export class ControlDispatcher implements IPendingRequestRegistry { if (pending) { pending.abortController.abort(); this.deregisterIncomingRequest(requestId); - this.sendErrorResponse(requestId, 'Request cancelled'); + this.sendErrorResponse(requestId, "Request cancelled"); - debugLogger.debug( - `[ControlDispatcher] Cancelled incoming request: ${requestId}`, - ); + debugLogger.debug(`[ControlDispatcher] Cancelled incoming request: ${requestId}`); } } else { // Cancel ALL pending incoming requests @@ -197,7 +178,7 @@ export class ControlDispatcher implements IPendingRequestRegistry { if (pending) { pending.abortController.abort(); this.deregisterIncomingRequest(id); - this.sendErrorResponse(id, 'All requests cancelled'); + this.sendErrorResponse(id, "All requests cancelled"); } } @@ -232,7 +213,7 @@ export class ControlDispatcher implements IPendingRequestRegistry { const pending = this.pendingOutgoingRequests.get(id); if (pending) { this.deregisterOutgoingRequest(id); - pending.reject(new Error('Input closed')); + pending.reject(new Error("Input closed")); } } } @@ -241,31 +222,25 @@ export class ControlDispatcher implements IPendingRequestRegistry { * Stops all pending requests and cleans up all controllers */ shutdown(): void { - debugLogger.debug('[ControlDispatcher] Shutting down'); + debugLogger.debug("[ControlDispatcher] Shutting down"); // Remove abort listener to prevent memory leak if (this.abortHandler) { - this.context.abortSignal.removeEventListener('abort', this.abortHandler); + this.context.abortSignal.removeEventListener("abort", this.abortHandler); this.abortHandler = null; } // Cancel all incoming requests - for (const [ - _requestId, - pending, - ] of this.pendingIncomingRequests.entries()) { + for (const [_requestId, pending] of this.pendingIncomingRequests.entries()) { pending.abortController.abort(); clearTimeout(pending.timeoutId); } this.pendingIncomingRequests.clear(); // Cancel all outgoing requests - for (const [ - _requestId, - pending, - ] of this.pendingOutgoingRequests.entries()) { + for (const [_requestId, pending] of this.pendingOutgoingRequests.entries()) { clearTimeout(pending.timeoutId); - pending.reject(new Error('Dispatcher shutdown')); + pending.reject(new Error("Dispatcher shutdown")); } this.pendingOutgoingRequests.clear(); @@ -367,7 +342,7 @@ export class ControlDispatcher implements IPendingRequestRegistry { } if (this.pendingIncomingRequests.size === 0) { - debugLogger.debug('[ControlDispatcher] All incoming requests completed'); + debugLogger.debug("[ControlDispatcher] All incoming requests completed"); } } @@ -376,17 +351,17 @@ export class ControlDispatcher implements IPendingRequestRegistry { */ private getControllerForRequest(subtype: string) { switch (subtype) { - case 'initialize': - case 'interrupt': - case 'set_model': - case 'supported_commands': + case "initialize": + case "interrupt": + case "set_model": + case "supported_commands": return this.systemController; - case 'can_use_tool': - case 'set_permission_mode': + case "can_use_tool": + case "set_permission_mode": return this.permissionController; - case 'mcp_server_status': + case "mcp_server_status": return this.sdkMcpController; // case 'hook_callback': @@ -400,14 +375,11 @@ export class ControlDispatcher implements IPendingRequestRegistry { /** * Sends a success response back to SDK */ - private sendSuccessResponse( - requestId: string, - response: Record, - ): void { + private sendSuccessResponse(requestId: string, response: Record): void { const controlResponse: CLIControlResponse = { - type: 'control_response', + type: "control_response", response: { - subtype: 'success', + subtype: "success", request_id: requestId, response, }, @@ -420,9 +392,9 @@ export class ControlDispatcher implements IPendingRequestRegistry { */ private sendErrorResponse(requestId: string, error: string): void { const controlResponse: CLIControlResponse = { - type: 'control_response', + type: "control_response", response: { - subtype: 'error', + subtype: "error", request_id: requestId, error, }, diff --git a/apps/airiscode-cli/src/nonInteractive/control/ControlService.ts b/apps/airiscode-cli/src/nonInteractive/control/ControlService.ts index 671a18530..8f584cf31 100644 --- a/apps/airiscode-cli/src/nonInteractive/control/ControlService.ts +++ b/apps/airiscode-cli/src/nonInteractive/control/ControlService.ts @@ -26,14 +26,14 @@ * plane exclusively through ControlService. */ -import type { IControlContext } from './ControlContext.js'; -import type { ControlDispatcher } from './ControlDispatcher.js'; +import type { IControlContext } from "./ControlContext.js"; +import type { ControlDispatcher } from "./ControlDispatcher.js"; import type { PermissionServiceAPI, SystemServiceAPI, // McpServiceAPI, // HookServiceAPI, -} from './types/serviceAPIs.js'; +} from "./types/serviceAPIs.js"; /** * Control Service @@ -72,8 +72,7 @@ export class ControlService { * @param confirmationDetails - Tool confirmation details * @returns Array of permission suggestions or null */ - buildPermissionSuggestions: - controller.buildPermissionSuggestions.bind(controller), + buildPermissionSuggestions: controller.buildPermissionSuggestions.bind(controller), /** * Get callback for monitoring tool call status updates @@ -82,8 +81,7 @@ export class ControlService { * * @returns Callback function for tool call updates */ - getToolCallUpdateCallback: - controller.getToolCallUpdateCallback.bind(controller), + getToolCallUpdateCallback: controller.getToolCallUpdateCallback.bind(controller), }; } diff --git a/apps/airiscode-cli/src/nonInteractive/control/controllers/baseController.ts b/apps/airiscode-cli/src/nonInteractive/control/controllers/baseController.ts index 4f9ee462c..9a47bdae4 100644 --- a/apps/airiscode-cli/src/nonInteractive/control/controllers/baseController.ts +++ b/apps/airiscode-cli/src/nonInteractive/control/controllers/baseController.ts @@ -15,15 +15,11 @@ * - Integration with central pending request registry */ -import { randomUUID } from 'node:crypto'; -import type { DebugLogger } from '@airiscode/core'; -import { createDebugLogger } from '@airiscode/core'; -import type { IControlContext } from '../ControlContext.js'; -import type { - ControlRequestPayload, - ControlResponse, - CLIControlRequest, -} from '../../types.js'; +import { randomUUID } from "node:crypto"; +import type { DebugLogger } from "@airiscode/core"; +import { createDebugLogger } from "@airiscode/core"; +import type { CLIControlRequest, ControlRequestPayload, ControlResponse } from "../../types.js"; +import type { IControlContext } from "../ControlContext.js"; const DEFAULT_REQUEST_TIMEOUT_MS = 30000; // 30 seconds @@ -61,11 +57,7 @@ export abstract class BaseController { protected controllerName: string; protected debugLogger: DebugLogger; - constructor( - context: IControlContext, - registry: IPendingRequestRegistry, - controllerName: string, - ) { + constructor(context: IControlContext, registry: IPendingRequestRegistry, controllerName: string) { this.context = context; this.registry = registry; this.controllerName = controllerName; @@ -87,9 +79,7 @@ export abstract class BaseController { const timeoutId = setTimeout(() => { requestAbortController.abort(); this.registry.deregisterIncomingRequest(requestId); - this.debugLogger.warn( - `[${this.controllerName}] Request timeout: ${requestId}`, - ); + this.debugLogger.warn(`[${this.controllerName}] Request timeout: ${requestId}`); }, DEFAULT_REQUEST_TIMEOUT_MS); // Register with central registry @@ -101,10 +91,7 @@ export abstract class BaseController { ); try { - const response = await this.handleRequestPayload( - payload, - requestAbortController.signal, - ); + const response = await this.handleRequestPayload(payload, requestAbortController.signal); // Success - deregister this.registry.deregisterIncomingRequest(requestId); @@ -130,12 +117,12 @@ export abstract class BaseController { ): Promise { // Check if stream is closed if (this.context.inputClosed) { - throw new Error('Input closed'); + throw new Error("Input closed"); } // Check if already aborted if (signal?.aborted) { - throw new Error('Request aborted'); + throw new Error("Request aborted"); } const requestId = randomUUID(); @@ -144,39 +131,35 @@ export abstract class BaseController { // Setup abort handler const abortHandler = () => { this.registry.deregisterOutgoingRequest(requestId); - reject(new Error('Request aborted')); - this.debugLogger.warn( - `[${this.controllerName}] Outgoing request aborted: ${requestId}`, - ); + reject(new Error("Request aborted")); + this.debugLogger.warn(`[${this.controllerName}] Outgoing request aborted: ${requestId}`); }; if (signal) { - signal.addEventListener('abort', abortHandler, { once: true }); + signal.addEventListener("abort", abortHandler, { once: true }); } // Setup timeout const timeoutId = setTimeout(() => { if (signal) { - signal.removeEventListener('abort', abortHandler); + signal.removeEventListener("abort", abortHandler); } this.registry.deregisterOutgoingRequest(requestId); - reject(new Error('Control request timeout')); - this.debugLogger.warn( - `[${this.controllerName}] Outgoing request timeout: ${requestId}`, - ); + reject(new Error("Control request timeout")); + this.debugLogger.warn(`[${this.controllerName}] Outgoing request timeout: ${requestId}`); }, timeoutMs); // Wrap resolve/reject to clean up abort listener const wrappedResolve = (response: ControlResponse) => { if (signal) { - signal.removeEventListener('abort', abortHandler); + signal.removeEventListener("abort", abortHandler); } resolve(response); }; const wrappedReject = (error: Error) => { if (signal) { - signal.removeEventListener('abort', abortHandler); + signal.removeEventListener("abort", abortHandler); } reject(error); }; @@ -192,7 +175,7 @@ export abstract class BaseController { // Send control request const request: CLIControlRequest = { - type: 'control_request', + type: "control_request", request_id: requestId, request: payload, }; @@ -201,7 +184,7 @@ export abstract class BaseController { this.context.streamJson.send(request); } catch (error) { if (signal) { - signal.removeEventListener('abort', abortHandler); + signal.removeEventListener("abort", abortHandler); } this.registry.deregisterOutgoingRequest(requestId); reject(error); diff --git a/apps/airiscode-cli/src/nonInteractive/control/controllers/hookController.ts b/apps/airiscode-cli/src/nonInteractive/control/controllers/hookController.ts index df6eb4c0e..c48979e6c 100644 --- a/apps/airiscode-cli/src/nonInteractive/control/controllers/hookController.ts +++ b/apps/airiscode-cli/src/nonInteractive/control/controllers/hookController.ts @@ -11,11 +11,8 @@ * - hook_callback: Process hook callbacks (placeholder for future) */ -import { BaseController } from './baseController.js'; -import type { - ControlRequestPayload, - CLIHookCallbackRequest, -} from '../../types.js'; +import type { CLIHookCallbackRequest, ControlRequestPayload } from "../../types.js"; +import { BaseController } from "./baseController.js"; export class HookController extends BaseController { /** @@ -26,7 +23,7 @@ export class HookController extends BaseController { _signal: AbortSignal, ): Promise> { switch (payload.subtype) { - case 'hook_callback': + case "hook_callback": return this.handleHookCallback(payload as CLIHookCallbackRequest); default: @@ -42,13 +39,11 @@ export class HookController extends BaseController { private async handleHookCallback( payload: CLIHookCallbackRequest, ): Promise> { - this.debugLogger.debug( - `[HookController] Hook callback: ${payload.callback_id}`, - ); + this.debugLogger.debug(`[HookController] Hook callback: ${payload.callback_id}`); // Hook callback processing not yet implemented return { - result: 'Hook callback processing not yet implemented', + result: "Hook callback processing not yet implemented", callback_id: payload.callback_id, tool_use_id: payload.tool_use_id, }; diff --git a/apps/airiscode-cli/src/nonInteractive/control/controllers/permissionController.ts b/apps/airiscode-cli/src/nonInteractive/control/controllers/permissionController.ts index 4296d4ae3..7c0232736 100644 --- a/apps/airiscode-cli/src/nonInteractive/control/controllers/permissionController.ts +++ b/apps/airiscode-cli/src/nonInteractive/control/controllers/permissionController.ts @@ -15,26 +15,23 @@ */ import type { - WaitingToolCall, + ApprovalMode, ToolExecuteConfirmationDetails, ToolMcpConfirmationDetails, - ApprovalMode, -} from '@airiscode/core'; -import { - InputFormat, - ToolConfirmationOutcome, -} from '@airiscode/core'; + WaitingToolCall, +} from "@airiscode/core"; +import { InputFormat, ToolConfirmationOutcome } from "@airiscode/core"; import type { CLIControlPermissionRequest, CLIControlSetPermissionModeRequest, ControlRequestPayload, PermissionMode, PermissionSuggestion, -} from '../../types.js'; -import { BaseController } from './baseController.js'; +} from "../../types.js"; +import { BaseController } from "./baseController.js"; // Import ToolCallConfirmationDetails types for type alignment -type ToolConfirmationType = 'edit' | 'exec' | 'mcp' | 'info' | 'plan'; +type ToolConfirmationType = "edit" | "exec" | "mcp" | "info" | "plan"; export class PermissionController extends BaseController { private pendingOutgoingRequests = new Set(); @@ -47,21 +44,15 @@ export class PermissionController extends BaseController { signal: AbortSignal, ): Promise> { if (signal.aborted) { - throw new Error('Request aborted'); + throw new Error("Request aborted"); } switch (payload.subtype) { - case 'can_use_tool': - return this.handleCanUseTool( - payload as CLIControlPermissionRequest, - signal, - ); + case "can_use_tool": + return this.handleCanUseTool(payload as CLIControlPermissionRequest, signal); - case 'set_permission_mode': - return this.handleSetPermissionMode( - payload as CLIControlSetPermissionModeRequest, - signal, - ); + case "set_permission_mode": + return this.handleSetPermissionMode(payload as CLIControlSetPermissionModeRequest, signal); default: throw new Error(`Unsupported request subtype in PermissionController`); @@ -81,56 +72,52 @@ export class PermissionController extends BaseController { signal: AbortSignal, ): Promise> { if (signal.aborted) { - throw new Error('Request aborted'); + throw new Error("Request aborted"); } const toolName = payload.tool_name; - if ( - !toolName || - typeof toolName !== 'string' || - toolName.trim().length === 0 - ) { + if (!toolName || typeof toolName !== "string" || toolName.trim().length === 0) { return { - subtype: 'can_use_tool', - behavior: 'deny', - message: 'Missing or invalid tool_name in can_use_tool request', + subtype: "can_use_tool", + behavior: "deny", + message: "Missing or invalid tool_name in can_use_tool request", }; } - let behavior: 'allow' | 'deny' = 'allow'; + let behavior: "allow" | "deny" = "allow"; let message: string | undefined; try { // Check permission mode first const permissionResult = this.checkPermissionMode(); if (!permissionResult.allowed) { - behavior = 'deny'; + behavior = "deny"; message = permissionResult.message; } // Check tool registry if permission mode allows - if (behavior === 'allow') { + if (behavior === "allow") { const registryResult = this.checkToolRegistry(toolName); if (!registryResult.allowed) { - behavior = 'deny'; + behavior = "deny"; message = registryResult.message; } } } catch (error) { - behavior = 'deny'; + behavior = "deny"; message = error instanceof Error ? `Failed to evaluate tool permission: ${error.message}` - : 'Failed to evaluate tool permission'; + : "Failed to evaluate tool permission"; } const response: Record = { - subtype: 'can_use_tool', + subtype: "can_use_tool", behavior, }; if (message) { - response['message'] = message; + response["message"] = message; } return response; @@ -144,17 +131,17 @@ export class PermissionController extends BaseController { // Map permission modes to approval logic (aligned with VALID_APPROVAL_MODE_VALUES) switch (mode) { - case 'yolo': // Allow all tools - case 'auto-edit': // Auto-approve edit operations - case 'plan': // Auto-approve planning operations + case "yolo": // Allow all tools + case "auto-edit": // Auto-approve edit operations + case "plan": // Auto-approve planning operations return { allowed: true }; - case 'default': // TODO: allow all tools for test + case "default": // TODO: allow all tools for test default: return { allowed: false, message: - 'Tool execution requires manual approval. Update permission mode or approve via host.', + "Tool execution requires manual approval. Update permission mode or approve via host.", }; } } @@ -175,13 +162,9 @@ export class PermissionController extends BaseController { }; }; - if (typeof registryProvider.getToolRegistry === 'function') { + if (typeof registryProvider.getToolRegistry === "function") { const registry = registryProvider.getToolRegistry(); - if ( - registry && - typeof registry.getTool === 'function' && - !registry.getTool(toolName) - ) { + if (registry && typeof registry.getTool === "function" && !registry.getTool(toolName)) { return { allowed: false, message: `Tool "${toolName}" is not registered.`, @@ -193,7 +176,7 @@ export class PermissionController extends BaseController { } catch (error) { return { allowed: false, - message: `Failed to check tool registry: ${error instanceof Error ? error.message : 'Unknown error'}`, + message: `Failed to check tool registry: ${error instanceof Error ? error.message : "Unknown error"}`, }; } } @@ -208,31 +191,24 @@ export class PermissionController extends BaseController { signal: AbortSignal, ): Promise> { if (signal.aborted) { - throw new Error('Request aborted'); + throw new Error("Request aborted"); } const mode = payload.mode; - const validModes: PermissionMode[] = [ - 'default', - 'plan', - 'auto-edit', - 'yolo', - ]; + const validModes: PermissionMode[] = ["default", "plan", "auto-edit", "yolo"]; if (!validModes.includes(mode)) { throw new Error( - `Invalid permission mode: ${mode}. Valid values are: ${validModes.join(', ')}`, + `Invalid permission mode: ${mode}. Valid values are: ${validModes.join(", ")}`, ); } this.context.permissionMode = mode; this.context.config.setApprovalMode(mode as ApprovalMode); - this.debugLogger.info( - `[PermissionController] Permission mode updated to: ${mode}`, - ); + this.debugLogger.info(`[PermissionController] Permission mode updated to: ${mode}`); - return { status: 'updated', mode }; + return { status: "updated", mode }; } /** @@ -241,98 +217,95 @@ export class PermissionController extends BaseController { * This method creates UI suggestions based on tool confirmation details, * helping the host application present appropriate permission options. */ - buildPermissionSuggestions( - confirmationDetails: unknown, - ): PermissionSuggestion[] | null { + buildPermissionSuggestions(confirmationDetails: unknown): PermissionSuggestion[] | null { if ( !confirmationDetails || - typeof confirmationDetails !== 'object' || - !('type' in confirmationDetails) + typeof confirmationDetails !== "object" || + !("type" in confirmationDetails) ) { return null; } const details = confirmationDetails as Record; - const type = String(details['type'] ?? ''); - const title = - typeof details['title'] === 'string' ? details['title'] : undefined; + const type = String(details["type"] ?? ""); + const title = typeof details["title"] === "string" ? details["title"] : undefined; // Ensure type matches ToolCallConfirmationDetails union const confirmationType = type as ToolConfirmationType; switch (confirmationType) { - case 'exec': // ToolExecuteConfirmationDetails + case "exec": // ToolExecuteConfirmationDetails return [ { - type: 'allow', - label: 'Allow Command', - description: `Execute: ${details['command']}`, + type: "allow", + label: "Allow Command", + description: `Execute: ${details["command"]}`, }, { - type: 'deny', - label: 'Deny', - description: 'Block this command execution', + type: "deny", + label: "Deny", + description: "Block this command execution", }, ]; - case 'edit': // ToolEditConfirmationDetails + case "edit": // ToolEditConfirmationDetails return [ { - type: 'allow', - label: 'Allow Edit', - description: `Edit file: ${details['fileName']}`, + type: "allow", + label: "Allow Edit", + description: `Edit file: ${details["fileName"]}`, }, { - type: 'deny', - label: 'Deny', - description: 'Block this file edit', + type: "deny", + label: "Deny", + description: "Block this file edit", }, { - type: 'modify', - label: 'Review Changes', - description: 'Review the proposed changes before applying', + type: "modify", + label: "Review Changes", + description: "Review the proposed changes before applying", }, ]; - case 'plan': // ToolPlanConfirmationDetails + case "plan": // ToolPlanConfirmationDetails return [ { - type: 'allow', - label: 'Approve Plan', - description: title || 'Execute the proposed plan', + type: "allow", + label: "Approve Plan", + description: title || "Execute the proposed plan", }, { - type: 'deny', - label: 'Reject Plan', - description: 'Do not execute this plan', + type: "deny", + label: "Reject Plan", + description: "Do not execute this plan", }, ]; - case 'mcp': // ToolMcpConfirmationDetails + case "mcp": // ToolMcpConfirmationDetails return [ { - type: 'allow', - label: 'Allow MCP Call', - description: `${details['serverName']}: ${details['toolName']}`, + type: "allow", + label: "Allow MCP Call", + description: `${details["serverName"]}: ${details["toolName"]}`, }, { - type: 'deny', - label: 'Deny', - description: 'Block this MCP server call', + type: "deny", + label: "Deny", + description: "Block this MCP server call", }, ]; - case 'info': // ToolInfoConfirmationDetails + case "info": // ToolInfoConfirmationDetails return [ { - type: 'allow', - label: 'Allow Info Request', - description: title || 'Allow information request', + type: "allow", + label: "Allow Info Request", + description: title || "Allow information request", }, { - type: 'deny', - label: 'Deny', - description: 'Block this information request', + type: "deny", + label: "Deny", + description: "Block this information request", }, ]; @@ -340,13 +313,13 @@ export class PermissionController extends BaseController { // Fallback for unknown types return [ { - type: 'allow', - label: 'Allow', + type: "allow", + label: "Allow", description: title || `Allow ${type} operation`, }, { - type: 'deny', - label: 'Deny', + type: "deny", + label: "Deny", description: `Block ${type} operation`, }, ]; @@ -362,12 +335,12 @@ export class PermissionController extends BaseController { for (const call of toolCalls) { if ( call && - typeof call === 'object' && - (call as { status?: string }).status === 'awaiting_approval' + typeof call === "object" && + (call as { status?: string }).status === "awaiting_approval" ) { const awaiting = call as WaitingToolCall; if ( - typeof awaiting.confirmationDetails?.onConfirm === 'function' && + typeof awaiting.confirmationDetails?.onConfirm === "function" && !this.pendingOutgoingRequests.has(awaiting.request.callId) ) { this.pendingOutgoingRequests.add(awaiting.request.callId); @@ -385,15 +358,11 @@ export class PermissionController extends BaseController { * - stream-json mode: Send can_use_tool to SDK and await response * - Other modes: Check local approval mode and decide immediately */ - private async handleOutgoingPermissionRequest( - toolCall: WaitingToolCall, - ): Promise { + private async handleOutgoingPermissionRequest(toolCall: WaitingToolCall): Promise { try { // Check if already aborted if (this.context.abortSignal?.aborted) { - await toolCall.confirmationDetails.onConfirm( - ToolConfirmationOutcome.Cancel, - ); + await toolCall.confirmationDetails.onConfirm(ToolConfirmationOutcome.Cancel); return; } @@ -412,13 +381,11 @@ export class PermissionController extends BaseController { } // Stream-json mode: ask SDK for permission - const permissionSuggestions = this.buildPermissionSuggestions( - toolCall.confirmationDetails, - ); + const permissionSuggestions = this.buildPermissionSuggestions(toolCall.confirmationDetails); const response = await this.sendControlRequest( { - subtype: 'can_use_tool', + subtype: "can_use_tool", tool_name: toolCall.request.name, tool_use_id: toolCall.request.callId, input: toolCall.request.args, @@ -429,31 +396,25 @@ export class PermissionController extends BaseController { this.context.abortSignal, ); - if (response.subtype !== 'success') { - await toolCall.confirmationDetails.onConfirm( - ToolConfirmationOutcome.Cancel, - ); + if (response.subtype !== "success") { + await toolCall.confirmationDetails.onConfirm(ToolConfirmationOutcome.Cancel); return; } const payload = (response.response || {}) as Record; - const behavior = String(payload['behavior'] || '').toLowerCase(); + const behavior = String(payload["behavior"] || "").toLowerCase(); - if (behavior === 'allow') { + if (behavior === "allow") { // Handle updated input if provided - const updatedInput = payload['updatedInput']; - if (updatedInput && typeof updatedInput === 'object') { + const updatedInput = payload["updatedInput"]; + if (updatedInput && typeof updatedInput === "object") { toolCall.request.args = updatedInput as Record; } - await toolCall.confirmationDetails.onConfirm( - ToolConfirmationOutcome.ProceedOnce, - ); + await toolCall.confirmationDetails.onConfirm(ToolConfirmationOutcome.ProceedOnce); } else { // Extract cancel message from response if available const cancelMessage = - typeof payload['message'] === 'string' - ? payload['message'] - : undefined; + typeof payload["message"] === "string" ? payload["message"] : undefined; await toolCall.confirmationDetails.onConfirm( ToolConfirmationOutcome.Cancel, @@ -461,19 +422,15 @@ export class PermissionController extends BaseController { ); } } catch (error) { - this.debugLogger.error( - '[PermissionController] Outgoing permission failed:', - error, - ); + this.debugLogger.error("[PermissionController] Outgoing permission failed:", error); // Extract error message - const errorMessage = - error instanceof Error ? error.message : String(error); + const errorMessage = error instanceof Error ? error.message : String(error); // On error, pass error message as cancel message // Only pass payload for exec and mcp types that support it const confirmationType = toolCall.confirmationDetails.type; - if (['edit', 'exec', 'mcp'].includes(confirmationType)) { + if (["edit", "exec", "mcp"].includes(confirmationType)) { const execOrMcpDetails = toolCall.confirmationDetails as | ToolExecuteConfirmationDetails | ToolMcpConfirmationDetails; @@ -481,12 +438,9 @@ export class PermissionController extends BaseController { cancelMessage: `Error: ${errorMessage}`, }); } else { - await toolCall.confirmationDetails.onConfirm( - ToolConfirmationOutcome.Cancel, - { - cancelMessage: `Error: ${errorMessage}`, - }, - ); + await toolCall.confirmationDetails.onConfirm(ToolConfirmationOutcome.Cancel, { + cancelMessage: `Error: ${errorMessage}`, + }); } } finally { this.pendingOutgoingRequests.delete(toolCall.request.callId); diff --git a/apps/airiscode-cli/src/nonInteractive/control/controllers/sdkMcpController.ts b/apps/airiscode-cli/src/nonInteractive/control/controllers/sdkMcpController.ts index 058226c09..ff920aedf 100644 --- a/apps/airiscode-cli/src/nonInteractive/control/controllers/sdkMcpController.ts +++ b/apps/airiscode-cli/src/nonInteractive/control/controllers/sdkMcpController.ts @@ -17,12 +17,9 @@ * SDK MCP Server processes → control_response → CLI MCP Client */ -import type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js'; -import { BaseController } from './baseController.js'; -import type { - ControlRequestPayload, - CLIControlMcpMessageRequest, -} from '../../types.js'; +import type { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js"; +import type { CLIControlMcpMessageRequest, ControlRequestPayload } from "../../types.js"; +import { BaseController } from "./baseController.js"; const MCP_REQUEST_TIMEOUT = 30_000; // 30 seconds @@ -39,11 +36,11 @@ export class SdkMcpController extends BaseController { signal: AbortSignal, ): Promise> { if (signal.aborted) { - throw new Error('Request aborted'); + throw new Error("Request aborted"); } switch (payload.subtype) { - case 'mcp_server_status': + case "mcp_server_status": return this.handleMcpStatus(); default: @@ -62,11 +59,11 @@ export class SdkMcpController extends BaseController { for (const serverName of this.context.sdkMcpServers) { // SDK MCP servers are "connected" once registered since they run in SDK process - status[serverName] = 'connected'; + status[serverName] = "connected"; } return { - subtype: 'mcp_server_status', + subtype: "mcp_server_status", status, }; } @@ -90,9 +87,9 @@ export class SdkMcpController extends BaseController { // Send control request to SDK with the MCP message const response = await this.sendControlRequest( { - subtype: 'mcp_message', + subtype: "mcp_message", server_name: serverName, - message: message as CLIControlMcpMessageRequest['message'], + message: message as CLIControlMcpMessageRequest["message"], }, MCP_REQUEST_TIMEOUT, this.context.abortSignal, @@ -100,12 +97,10 @@ export class SdkMcpController extends BaseController { // Extract MCP response from control response const responsePayload = response.response as Record; - const mcpResponse = responsePayload?.['mcp_response'] as JSONRPCMessage; + const mcpResponse = responsePayload?.["mcp_response"] as JSONRPCMessage; if (!mcpResponse) { - throw new Error( - `Invalid MCP response from SDK for server '${serverName}'`, - ); + throw new Error(`Invalid MCP response from SDK for server '${serverName}'`); } this.debugLogger.debug( diff --git a/apps/airiscode-cli/src/nonInteractive/control/controllers/systemController.ts b/apps/airiscode-cli/src/nonInteractive/control/controllers/systemController.ts index d9cde131e..ebae5a833 100644 --- a/apps/airiscode-cli/src/nonInteractive/control/controllers/systemController.ts +++ b/apps/airiscode-cli/src/nonInteractive/control/controllers/systemController.ts @@ -13,22 +13,22 @@ * - set_model: Switch model (placeholder) */ -import { BaseController } from './baseController.js'; +import { + AuthProviderType, + createDebugLogger, + type MCPOAuthConfig, + MCPServerConfig, +} from "@airiscode/core"; +import { getAvailableCommands } from "../../../nonInteractiveCliCommands.js"; import type { - ControlRequestPayload, CLIControlInitializeRequest, CLIControlSetModelRequest, CLIMcpServerConfig, -} from '../../types.js'; -import { getAvailableCommands } from '../../../nonInteractiveCliCommands.js'; -import { - createDebugLogger, - MCPServerConfig, - AuthProviderType, - type MCPOAuthConfig, -} from '@airiscode/core'; + ControlRequestPayload, +} from "../../types.js"; +import { BaseController } from "./baseController.js"; -const debugLogger = createDebugLogger('SYSTEM_CONTROLLER'); +const debugLogger = createDebugLogger("SYSTEM_CONTROLLER"); export class SystemController extends BaseController { /** @@ -39,26 +39,20 @@ export class SystemController extends BaseController { signal: AbortSignal, ): Promise> { if (signal.aborted) { - throw new Error('Request aborted'); + throw new Error("Request aborted"); } switch (payload.subtype) { - case 'initialize': - return this.handleInitialize( - payload as CLIControlInitializeRequest, - signal, - ); + case "initialize": + return this.handleInitialize(payload as CLIControlInitializeRequest, signal); - case 'interrupt': + case "interrupt": return this.handleInterrupt(); - case 'set_model': - return this.handleSetModel( - payload as CLIControlSetModelRequest, - signal, - ); + case "set_model": + return this.handleSetModel(payload as CLIControlSetModelRequest, signal); - case 'supported_commands': + case "supported_commands": return this.handleSupportedCommands(signal); default: @@ -79,7 +73,7 @@ export class SystemController extends BaseController { signal: AbortSignal, ): Promise> { if (signal.aborted) { - throw new Error('Request aborted'); + throw new Error("Request aborted"); } this.context.config.setSdkMode(true); @@ -87,13 +81,13 @@ export class SystemController extends BaseController { // Process SDK MCP servers if ( payload.sdkMcpServers && - typeof payload.sdkMcpServers === 'object' && + typeof payload.sdkMcpServers === "object" && payload.sdkMcpServers !== null ) { const sdkServers: Record = {}; for (const [key, wireConfig] of Object.entries(payload.sdkMcpServers)) { const name = - typeof wireConfig?.name === 'string' && wireConfig.name.trim().length + typeof wireConfig?.name === "string" && wireConfig.name.trim().length ? wireConfig.name : key; @@ -117,7 +111,7 @@ export class SystemController extends BaseController { undefined, // authProviderType undefined, // targetAudience undefined, // targetServiceAccount - 'sdk', // type + "sdk", // type ); } @@ -125,21 +119,16 @@ export class SystemController extends BaseController { if (sdkServerCount > 0) { try { this.context.config.addMcpServers(sdkServers); - debugLogger.debug( - `[SystemController] Added ${sdkServerCount} SDK MCP servers to config`, - ); + debugLogger.debug(`[SystemController] Added ${sdkServerCount} SDK MCP servers to config`); } catch (error) { - debugLogger.error( - '[SystemController] Failed to add SDK MCP servers:', - error, - ); + debugLogger.error("[SystemController] Failed to add SDK MCP servers:", error); } } } if ( payload.mcpServers && - typeof payload.mcpServers === 'object' && + typeof payload.mcpServers === "object" && payload.mcpServers !== null ) { const externalServers: Record = {}; @@ -161,10 +150,7 @@ export class SystemController extends BaseController { `[SystemController] Added ${externalCount} external MCP servers to config`, ); } catch (error) { - debugLogger.error( - '[SystemController] Failed to add external MCP servers:', - error, - ); + debugLogger.error("[SystemController] Failed to add external MCP servers:", error); } } } @@ -177,10 +163,7 @@ export class SystemController extends BaseController { `[SystemController] Added ${payload.agents.length} session subagents to config`, ); } catch (error) { - debugLogger.error( - '[SystemController] Failed to add session subagents:', - error, - ); + debugLogger.error("[SystemController] Failed to add session subagents:", error); } } @@ -192,7 +175,7 @@ export class SystemController extends BaseController { ); return { - subtype: 'initialize', + subtype: "initialize", session_id: this.context.config.getSessionId(), capabilities, }; @@ -209,9 +192,8 @@ export class SystemController extends BaseController { const capabilities: Record = { can_handle_can_use_tool: true, can_handle_hook_callback: false, - can_set_permission_mode: - typeof this.context.config.setApprovalMode === 'function', - can_set_model: typeof this.context.config.setModel === 'function', + can_set_permission_mode: typeof this.context.config.setApprovalMode === "function", + can_set_model: typeof this.context.config.setModel === "function", // SDK MCP servers are supported - messages routed through control plane can_handle_mcp_message: true, }; @@ -223,16 +205,12 @@ export class SystemController extends BaseController { serverName: string, config?: CLIMcpServerConfig, ): MCPServerConfig | null { - if (!config || typeof config !== 'object') { - debugLogger.warn( - `[SystemController] Ignoring invalid MCP server config for '${serverName}'`, - ); + if (!config || typeof config !== "object") { + debugLogger.warn(`[SystemController] Ignoring invalid MCP server config for '${serverName}'`); return null; } - const authProvider = this.normalizeAuthProviderType( - config.authProviderType, - ); + const authProvider = this.normalizeAuthProviderType(config.authProviderType); const oauthConfig = this.normalizeOAuthConfig(config.oauth); return new MCPServerConfig( @@ -257,9 +235,7 @@ export class SystemController extends BaseController { ); } - private normalizeAuthProviderType( - value?: string, - ): AuthProviderType | undefined { + private normalizeAuthProviderType(value?: string): AuthProviderType | undefined { if (!value) { return undefined; } @@ -268,16 +244,12 @@ export class SystemController extends BaseController { case AuthProviderType.DYNAMIC_DISCOVERY: return value as AuthProviderType; default: - debugLogger.warn( - `[SystemController] Unsupported authProviderType '${value}', skipping`, - ); + debugLogger.warn(`[SystemController] Unsupported authProviderType '${value}', skipping`); return undefined; } } - private normalizeOAuthConfig( - oauth?: CLIMcpServerConfig['oauth'], - ): MCPOAuthConfig | undefined { + private normalizeOAuthConfig(oauth?: CLIMcpServerConfig["oauth"]): MCPOAuthConfig | undefined { if (!oauth) { return undefined; } @@ -310,12 +282,12 @@ export class SystemController extends BaseController { // Abort the main signal to cancel ongoing operations if (this.context.abortSignal && !this.context.abortSignal.aborted) { // Note: We can't directly abort the signal, but the onInterrupt callback should handle this - debugLogger.debug('[SystemController] Interrupt signal triggered'); + debugLogger.debug("[SystemController] Interrupt signal triggered"); } - debugLogger.debug('[SystemController] Interrupt handled'); + debugLogger.debug("[SystemController] Interrupt handled"); - return { subtype: 'interrupt' }; + return { subtype: "interrupt" }; } /** @@ -328,14 +300,14 @@ export class SystemController extends BaseController { signal: AbortSignal, ): Promise> { if (signal.aborted) { - throw new Error('Request aborted'); + throw new Error("Request aborted"); } const model = payload.model; // Validate model parameter - if (typeof model !== 'string' || model.trim() === '') { - throw new Error('Invalid model specified for set_model request'); + if (typeof model !== "string" || model.trim() === "") { + throw new Error("Invalid model specified for set_model request"); } try { @@ -345,17 +317,13 @@ export class SystemController extends BaseController { debugLogger.info(`[SystemController] Model switched to: ${model}`); return { - subtype: 'set_model', + subtype: "set_model", model, }; } catch (error) { - const errorMessage = - error instanceof Error ? error.message : 'Failed to set model'; + const errorMessage = error instanceof Error ? error.message : "Failed to set model"; - debugLogger.error( - `[SystemController] Failed to set model ${model}:`, - error, - ); + debugLogger.error(`[SystemController] Failed to set model ${model}:`, error); throw new Error(errorMessage); } @@ -366,17 +334,15 @@ export class SystemController extends BaseController { * * Returns list of supported slash commands loaded dynamically */ - private async handleSupportedCommands( - signal: AbortSignal, - ): Promise> { + private async handleSupportedCommands(signal: AbortSignal): Promise> { if (signal.aborted) { - throw new Error('Request aborted'); + throw new Error("Request aborted"); } const slashCommands = await this.loadSlashCommandNames(signal); return { - subtype: 'supported_commands', + subtype: "supported_commands", commands: slashCommands, }; } @@ -407,10 +373,7 @@ export class SystemController extends BaseController { return []; } - debugLogger.error( - '[SystemController] Failed to load slash commands:', - error, - ); + debugLogger.error("[SystemController] Failed to load slash commands:", error); return []; } } diff --git a/apps/airiscode-cli/src/nonInteractive/control/types/serviceAPIs.ts b/apps/airiscode-cli/src/nonInteractive/control/types/serviceAPIs.ts index 3e0b83084..45717f977 100644 --- a/apps/airiscode-cli/src/nonInteractive/control/types/serviceAPIs.ts +++ b/apps/airiscode-cli/src/nonInteractive/control/types/serviceAPIs.ts @@ -12,9 +12,9 @@ * for internal CLI code (nonInteractiveCli, session managers, etc.). */ -import type { Client } from '@modelcontextprotocol/sdk/client/index.js'; -import type { MCPServerConfig } from '@airiscode/core'; -import type { PermissionSuggestion } from '../../types.js'; +import type { MCPServerConfig } from "@airiscode/core"; +import type { Client } from "@modelcontextprotocol/sdk/client/index.js"; +import type { PermissionSuggestion } from "../../types.js"; /** * Permission Service API @@ -32,9 +32,7 @@ export interface PermissionServiceAPI { * @param confirmationDetails - Tool confirmation details (type, title, metadata) * @returns Array of permission suggestions or null if details are invalid */ - buildPermissionSuggestions( - confirmationDetails: unknown, - ): PermissionSuggestion[] | null; + buildPermissionSuggestions(confirmationDetails: unknown): PermissionSuggestion[] | null; /** * Get callback for monitoring tool call status updates diff --git a/apps/airiscode-cli/src/nonInteractive/io/BaseJsonOutputAdapter.ts b/apps/airiscode-cli/src/nonInteractive/io/BaseJsonOutputAdapter.ts index 57fb5212b..63119ef07 100644 --- a/apps/airiscode-cli/src/nonInteractive/io/BaseJsonOutputAdapter.ts +++ b/apps/airiscode-cli/src/nonInteractive/io/BaseJsonOutputAdapter.ts @@ -4,22 +4,20 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { randomUUID } from 'node:crypto'; +import { randomUUID } from "node:crypto"; import type { + AgentResultDisplay, Config, + GenerateContentResponseUsageMetadata, + McpToolProgressData, + Part, + ServerGeminiStreamEvent, + SessionMetrics, ToolCallRequestInfo, ToolCallResponseInfo, - SessionMetrics, - ServerGeminiStreamEvent, - AgentResultDisplay, - McpToolProgressData, -} from '@airiscode/core'; -import { - GeminiEventType, - ToolErrorType, - parseAndFormatApiError, -} from '@airiscode/core'; -import type { Part, GenerateContentResponseUsageMetadata } from '@airiscode/core'; +} from "@airiscode/core"; +import { GeminiEventType, parseAndFormatApiError, ToolErrorType } from "@airiscode/core"; +import { functionResponsePartsToString } from "../../utils/nonInteractiveHelpers.js"; import type { CLIAssistantMessage, CLIMessage, @@ -35,8 +33,7 @@ import type { ToolResultBlock, ToolUseBlock, Usage, -} from '../types.js'; -import { functionResponsePartsToString } from '../../utils/nonInteractiveHelpers.js'; +} from "../types.js"; /** * Internal state for managing a single message context (main agent or subagent). @@ -48,7 +45,7 @@ export interface MessageState { usage: Usage; messageStarted: boolean; finalized: boolean; - currentBlockType: ContentBlock['type'] | null; + currentBlockType: ContentBlock["type"] | null; } /** @@ -91,10 +88,7 @@ export interface MessageEmitter { * @param request - Tool call request info * @param progress - Structured MCP progress data */ - emitToolProgress( - request: ToolCallRequestInfo, - progress: McpToolProgressData, - ): void; + emitToolProgress(request: ToolCallRequestInfo, progress: McpToolProgressData): void; } /** @@ -110,17 +104,11 @@ export interface JsonOutputAdapterInterface extends MessageEmitter { startSubagentAssistantMessage?(parentToolUseId: string): void; processSubagentToolCall?( - toolCall: NonNullable[number], - parentToolUseId: string, - ): void; - finalizeSubagentAssistantMessage?( - parentToolUseId: string, - ): CLIAssistantMessage; - emitSubagentErrorResult?( - errorMessage: string, - numTurns: number, + toolCall: NonNullable[number], parentToolUseId: string, ): void; + finalizeSubagentAssistantMessage?(parentToolUseId: string): CLIAssistantMessage; + emitSubagentErrorResult?(errorMessage: string, numTurns: number, parentToolUseId: string): void; getSessionId(): string; getModel(): string; @@ -190,9 +178,7 @@ export abstract class BaseJsonOutputAdapter { * @param metadata - Optional usage metadata from Gemini API * @returns Usage object */ - protected createUsage( - metadata?: GenerateContentResponseUsageMetadata | null, - ): Usage { + protected createUsage(metadata?: GenerateContentResponseUsageMetadata | null): Usage { const usage: Usage = { input_tokens: 0, output_tokens: 0, @@ -202,16 +188,16 @@ export abstract class BaseJsonOutputAdapter { return usage; } - if (typeof metadata.promptTokenCount === 'number') { + if (typeof metadata.promptTokenCount === "number") { usage.input_tokens = metadata.promptTokenCount; } - if (typeof metadata.candidatesTokenCount === 'number') { + if (typeof metadata.candidatesTokenCount === "number") { usage.output_tokens = metadata.candidatesTokenCount; } - if (typeof metadata.cachedContentTokenCount === 'number') { + if (typeof metadata.cachedContentTokenCount === "number") { usage.cache_read_input_tokens = metadata.cachedContentTokenCount; } - if (typeof metadata.totalTokenCount === 'number') { + if (typeof metadata.totalTokenCount === "number") { usage.total_tokens = metadata.totalTokenCount; } @@ -228,7 +214,7 @@ export abstract class BaseJsonOutputAdapter { const state = this.getMessageState(parentToolUseId); if (!state.messageId) { - throw new Error('Message not started'); + throw new Error("Message not started"); } // Enforce constraint: assistant message must contain only a single type of ContentBlock @@ -236,7 +222,7 @@ export abstract class BaseJsonOutputAdapter { const blockTypes = new Set(state.blocks.map((block) => block.type)); if (blockTypes.size > 1) { throw new Error( - `Assistant message must contain only one type of ContentBlock, found: ${Array.from(blockTypes).join(', ')}`, + `Assistant message must contain only one type of ContentBlock, found: ${Array.from(blockTypes).join(", ")}`, ); } } @@ -244,20 +230,19 @@ export abstract class BaseJsonOutputAdapter { // Determine stop_reason based on content block types // If the message contains only tool_use blocks, set stop_reason to 'tool_use' const stopReason = - state.blocks.length > 0 && - state.blocks.every((block) => block.type === 'tool_use') - ? 'tool_use' + state.blocks.length > 0 && state.blocks.every((block) => block.type === "tool_use") + ? "tool_use" : null; return { - type: 'assistant', + type: "assistant", uuid: state.messageId, session_id: this.config.getSessionId(), parent_tool_use_id: parentToolUseId, message: { id: state.messageId, - type: 'message', - role: 'assistant', + type: "message", + role: "assistant", model: this.config.getModel(), content: state.blocks, stop_reason: stopReason, @@ -272,10 +257,7 @@ export abstract class BaseJsonOutputAdapter { * @param state - Message state to finalize blocks for * @param parentToolUseId - null for main agent, string for subagent (optional, defaults to null) */ - protected finalizePendingBlocks( - state: MessageState, - parentToolUseId?: string | null, - ): void { + protected finalizePendingBlocks(state: MessageState, parentToolUseId?: string | null): void { const actualParentToolUseId = parentToolUseId ?? null; const lastBlock = state.blocks[state.blocks.length - 1]; if (!lastBlock) { @@ -287,7 +269,7 @@ export abstract class BaseJsonOutputAdapter { return; } - if (lastBlock.type === 'text' || lastBlock.type === 'thinking') { + if (lastBlock.type === "text" || lastBlock.type === "thinking") { this.onBlockClosed(state, index, actualParentToolUseId); this.closeBlock(state, index); } @@ -300,11 +282,7 @@ export abstract class BaseJsonOutputAdapter { * @param index - Block index * @param _block - Content block */ - protected openBlock( - state: MessageState, - index: number, - _block: ContentBlock, - ): void { + protected openBlock(state: MessageState, index: number, _block: ContentBlock): void { state.openBlocks.add(index); } @@ -333,7 +311,7 @@ export abstract class BaseJsonOutputAdapter { */ protected ensureBlockTypeConsistency( state: MessageState, - targetType: ContentBlock['type'], + targetType: ContentBlock["type"], parentToolUseId: string | null, ): void { if (state.currentBlockType === targetType) { @@ -383,9 +361,7 @@ export abstract class BaseJsonOutputAdapter { state.finalized = true; this.finalizePendingBlocks(state, parentToolUseId); - const orderedOpenBlocks = Array.from(state.openBlocks).sort( - (a, b) => a - b, - ); + const orderedOpenBlocks = Array.from(state.openBlocks).sort((a, b) => a - b); for (const index of orderedOpenBlocks) { this.onBlockClosed(state, index, parentToolUseId); this.closeBlock(state, index); @@ -546,10 +522,7 @@ export abstract class BaseJsonOutputAdapter { * @param state - Message state * @param parentToolUseId - null for main agent, string for subagent */ - protected onEnsureMessageStarted( - _state: MessageState, - _parentToolUseId: string | null, - ): void { + protected onEnsureMessageStarted(_state: MessageState, _parentToolUseId: string | null): void { // Default implementation does nothing } @@ -598,17 +571,12 @@ export abstract class BaseJsonOutputAdapter { this.appendText(state, event.value, null); break; case GeminiEventType.Citation: - if (typeof event.value === 'string') { + if (typeof event.value === "string") { this.appendText(state, `\n${event.value}`, null); } break; case GeminiEventType.Thought: - this.appendThinking( - state, - event.value.subject, - event.value.description, - null, - ); + this.appendThinking(state, event.value.subject, event.value.description, null); break; case GeminiEventType.ToolCallRequest: this.appendToolUse(state, event.value, null); @@ -654,9 +622,7 @@ export abstract class BaseJsonOutputAdapter { * @param parentToolUseId - Parent tool use ID * @returns CLIAssistantMessage */ - finalizeSubagentAssistantMessage( - parentToolUseId: string, - ): CLIAssistantMessage { + finalizeSubagentAssistantMessage(parentToolUseId: string): CLIAssistantMessage { const state = this.getMessageState(parentToolUseId); return this.finalizeAssistantMessageInternal(state, parentToolUseId); } @@ -669,11 +635,7 @@ export abstract class BaseJsonOutputAdapter { * @param numTurns - Number of turns * @param parentToolUseId - Parent tool use ID */ - emitSubagentErrorResult( - errorMessage: string, - numTurns: number, - parentToolUseId: string, - ): void { + emitSubagentErrorResult(errorMessage: string, numTurns: number, parentToolUseId: string): void { const state = this.getMessageState(parentToolUseId); // Finalize any pending assistant message if (state.messageStarted && !state.finalized) { @@ -693,15 +655,15 @@ export abstract class BaseJsonOutputAdapter { * @param parentToolUseId - Parent tool use ID */ processSubagentToolCall( - toolCall: NonNullable[number], + toolCall: NonNullable[number], parentToolUseId: string, ): void { const state = this.getMessageState(parentToolUseId); // Finalize any pending text message before starting tool_use const hasText = - state.blocks.some((b) => b.type === 'text') || - (state.currentBlockType === 'text' && state.blocks.length > 0); + state.blocks.some((b) => b.type === "text") || + (state.currentBlockType === "text" && state.blocks.length > 0); if (hasText) { this.finalizeSubagentAssistantMessage(parentToolUseId); this.startSubagentAssistantMessage(parentToolUseId); @@ -712,15 +674,11 @@ export abstract class BaseJsonOutputAdapter { this.startAssistantMessageInternal(state); } - this.ensureBlockTypeConsistency(state, 'tool_use', parentToolUseId); + this.ensureBlockTypeConsistency(state, "tool_use", parentToolUseId); this.ensureMessageStarted(state, parentToolUseId); this.finalizePendingBlocks(state, parentToolUseId); - const { index } = this.createSubagentToolUseBlock( - state, - toolCall, - parentToolUseId, - ); + const { index } = this.createSubagentToolUseBlock(state, toolCall, parentToolUseId); // Process tool use block creation and closure // Subclasses can override hook methods to emit stream events @@ -744,12 +702,12 @@ export abstract class BaseJsonOutputAdapter { protected processSubagentToolUseBlock( state: MessageState, index: number, - toolCall: NonNullable[number], + toolCall: NonNullable[number], parentToolUseId: string, ): void { // Emit tool_use block creation event (with empty input) const startBlock: ToolUseBlock = { - type: 'tool_use', + type: "tool_use", id: toolCall.callId, name: toolCall.name, input: {}, @@ -789,15 +747,13 @@ export abstract class BaseJsonOutputAdapter { return; } - this.ensureBlockTypeConsistency(state, 'text', parentToolUseId); + this.ensureBlockTypeConsistency(state, "text", parentToolUseId); this.ensureMessageStarted(state, parentToolUseId); - let current = state.blocks[state.blocks.length - 1] as - | TextBlock - | undefined; - const isNewBlock = !current || current.type !== 'text'; + let current = state.blocks[state.blocks.length - 1] as TextBlock | undefined; + const isNewBlock = !current || current.type !== "text"; if (isNewBlock) { - current = { type: 'text', text: '' } satisfies TextBlock; + current = { type: "text", text: "" } satisfies TextBlock; const index = state.blocks.length; state.blocks.push(current); this.openBlock(state, index, current); @@ -837,22 +793,20 @@ export abstract class BaseJsonOutputAdapter { parts.push(description); } - const fragment = parts.join(': '); + const fragment = parts.join(": "); if (!fragment) { return; } - this.ensureBlockTypeConsistency(state, 'thinking', actualParentToolUseId); + this.ensureBlockTypeConsistency(state, "thinking", actualParentToolUseId); this.ensureMessageStarted(state, actualParentToolUseId); - let current = state.blocks[state.blocks.length - 1] as - | ThinkingBlock - | undefined; - const isNewBlock = !current || current.type !== 'thinking'; + let current = state.blocks[state.blocks.length - 1] as ThinkingBlock | undefined; + const isNewBlock = !current || current.type !== "thinking"; if (isNewBlock) { current = { - type: 'thinking', - thinking: '', + type: "thinking", + thinking: "", signature: subject, } satisfies ThinkingBlock; const index = state.blocks.length; @@ -862,7 +816,7 @@ export abstract class BaseJsonOutputAdapter { } // current is guaranteed to be defined here (either existing or newly created) - current!.thinking = `${current!.thinking ?? ''}${fragment}`; + current!.thinking = `${current!.thinking ?? ""}${fragment}`; const index = state.blocks.length - 1; this.onThinkingAppended(state, index, fragment, actualParentToolUseId); } @@ -880,13 +834,13 @@ export abstract class BaseJsonOutputAdapter { request: ToolCallRequestInfo, parentToolUseId: string | null, ): void { - this.ensureBlockTypeConsistency(state, 'tool_use', parentToolUseId); + this.ensureBlockTypeConsistency(state, "tool_use", parentToolUseId); this.ensureMessageStarted(state, parentToolUseId); this.finalizePendingBlocks(state, parentToolUseId); const index = state.blocks.length; const block: ToolUseBlock = { - type: 'tool_use', + type: "tool_use", id: request.callId, name: request.name, input: request.args, @@ -896,7 +850,7 @@ export abstract class BaseJsonOutputAdapter { // Emit tool_use block creation event (with empty input) const startBlock: ToolUseBlock = { - type: 'tool_use', + type: "tool_use", id: request.callId, name: request.name, input: {}, @@ -915,10 +869,7 @@ export abstract class BaseJsonOutputAdapter { * @param state - Message state * @param parentToolUseId - null for main agent, string for subagent */ - protected ensureMessageStarted( - state: MessageState, - parentToolUseId: string | null, - ): void { + protected ensureMessageStarted(state: MessageState, parentToolUseId: string | null): void { if (state.messageStarted) { return; } @@ -937,12 +888,12 @@ export abstract class BaseJsonOutputAdapter { */ protected createSubagentToolUseBlock( state: MessageState, - toolCall: NonNullable[number], + toolCall: NonNullable[number], _parentToolUseId: string, ): { block: ToolUseBlock; index: number } { const index = state.blocks.length; const block: ToolUseBlock = { - type: 'tool_use', + type: "tool_use", id: toolCall.callId, name: toolCall.name, input: toolCall.args || {}, @@ -960,12 +911,12 @@ export abstract class BaseJsonOutputAdapter { emitUserMessage(parts: Part[], parentToolUseId?: string | null): void { const content = partsToContentBlock(parts); const message: CLIUserMessage = { - type: 'user', + type: "user", uuid: randomUUID(), session_id: this.getSessionId(), parent_tool_use_id: parentToolUseId ?? null, message: { - role: 'user', + role: "user", content, }, }; @@ -979,9 +930,7 @@ export abstract class BaseJsonOutputAdapter { * @param responseParts - Array of Part objects * @returns Error message if found, undefined otherwise */ - private checkResponsePartsForError( - responseParts: Part[] | undefined, - ): string | undefined { + private checkResponsePartsForError(responseParts: Part[] | undefined): string | undefined { // Use the shared helper function defined at file level return checkResponsePartsForError(responseParts); } @@ -1001,18 +950,13 @@ export abstract class BaseJsonOutputAdapter { parentToolUseId: string | null = null, ): void { // Check for errors in responseParts (e.g., cancelled responses) - const responsePartsError = this.checkResponsePartsForError( - response.responseParts, - ); + const responsePartsError = this.checkResponsePartsForError(response.responseParts); // Determine if this is an error response const hasError = Boolean(response.error) || Boolean(responsePartsError); // Track permission denials (execution denied errors) - if ( - response.error && - response.errorType === ToolErrorType.EXECUTION_DENIED - ) { + if (response.error && response.errorType === ToolErrorType.EXECUTION_DENIED) { const denial: CLIPermissionDenial = { tool_name: request.name, tool_use_id: request.callId, @@ -1022,7 +966,7 @@ export abstract class BaseJsonOutputAdapter { } const block: ToolResultBlock = { - type: 'tool_result', + type: "tool_result", tool_use_id: request.callId, is_error: hasError, }; @@ -1032,12 +976,12 @@ export abstract class BaseJsonOutputAdapter { } const message: CLIUserMessage = { - type: 'user', + type: "user", uuid: randomUUID(), session_id: this.getSessionId(), parent_tool_use_id: parentToolUseId, message: { - role: 'user', + role: "user", content: [block], }, }; @@ -1051,7 +995,7 @@ export abstract class BaseJsonOutputAdapter { */ emitSystemMessage(subtype: string, data?: unknown): void { const systemMessage = { - type: 'system', + type: "system", subtype, uuid: randomUUID(), session_id: this.getSessionId(), @@ -1069,10 +1013,7 @@ export abstract class BaseJsonOutputAdapter { * @param _request - Tool call request info * @param _progress - Structured MCP progress data */ - emitToolProgress( - _request: ToolCallRequestInfo, - _progress: McpToolProgressData, - ): void { + emitToolProgress(_request: ToolCallRequestInfo, _progress: McpToolProgressData): void { // No-op in base class. Only StreamJsonOutputAdapter emits tool progress // as stream events when includePartialMessages is enabled. } @@ -1092,20 +1033,16 @@ export abstract class BaseJsonOutputAdapter { const usage = options.usage ?? createExtendedUsage(); const resultText = options.summary ?? - (lastAssistantMessage - ? extractTextFromBlocks(lastAssistantMessage.message.content) - : ''); + (lastAssistantMessage ? extractTextFromBlocks(lastAssistantMessage.message.content) : ""); const baseUuid = randomUUID(); const baseSessionId = this.getSessionId(); if (options.isError) { - const errorMessage = options.errorMessage ?? 'Unknown error'; + const errorMessage = options.errorMessage ?? "Unknown error"; return { - type: 'result', - subtype: - (options.subtype as CLIResultMessageError['subtype']) ?? - 'error_during_execution', + type: "result", + subtype: (options.subtype as CLIResultMessageError["subtype"]) ?? "error_during_execution", uuid: baseUuid, session_id: baseSessionId, is_error: true, @@ -1118,9 +1055,8 @@ export abstract class BaseJsonOutputAdapter { }; } else { const success: CLIResultMessageSuccess & { stats?: SessionMetrics } = { - type: 'result', - subtype: - (options.subtype as CLIResultMessageSuccess['subtype']) ?? 'success', + type: "result", + subtype: (options.subtype as CLIResultMessageSuccess["subtype"]) ?? "success", uuid: baseUuid, session_id: baseSessionId, is_error: false, @@ -1159,8 +1095,8 @@ export abstract class BaseJsonOutputAdapter { }; return { - type: 'result', - subtype: 'error_during_execution', + type: "result", + subtype: "error_during_execution", uuid: randomUUID(), session_id: this.getSessionId(), is_error: true, @@ -1191,17 +1127,16 @@ export function partsToContentBlock(parts: Part[]): ContentBlock[] { let textContent: string | null = null; // Handle text parts - if ('text' in part && typeof part.text === 'string') { + if ("text" in part && typeof part.text === "string") { textContent = part.text; } // Handle functionResponse parts - extract output content - else if ('functionResponse' in part && part.functionResponse) { + else if ("functionResponse" in part && part.functionResponse) { const output = - part.functionResponse.response?.['output'] ?? - part.functionResponse.response?.['content'] ?? - ''; - textContent = - typeof output === 'string' ? output : JSON.stringify(output); + part.functionResponse.response?.["output"] ?? + part.functionResponse.response?.["content"] ?? + ""; + textContent = typeof output === "string" ? output : JSON.stringify(output); } // Handle other part types - convert to JSON string else { @@ -1212,7 +1147,7 @@ export function partsToContentBlock(parts: Part[]): ContentBlock[] { if (textContent !== null && textContent.length > 0) { if (currentTextBlock === null) { currentTextBlock = { - type: 'text', + type: "text", text: textContent, }; blocks.push(currentTextBlock); @@ -1238,12 +1173,12 @@ export function partsToContentBlock(parts: Part[]): ContentBlock[] { export function partsToString(parts: Part[]): string { return parts .map((part) => { - if ('text' in part && typeof part.text === 'string') { + if ("text" in part && typeof part.text === "string") { return part.text; } return JSON.stringify(part); }) - .join(''); + .join(""); } /** @@ -1252,23 +1187,21 @@ export function partsToString(parts: Part[]): string { * @param responseParts - Array of Part objects * @returns Error message if found, undefined otherwise */ -function checkResponsePartsForError( - responseParts: Part[] | undefined, -): string | undefined { +function checkResponsePartsForError(responseParts: Part[] | undefined): string | undefined { if (!responseParts || responseParts.length === 0) { return undefined; } for (const part of responseParts) { if ( - 'functionResponse' in part && + "functionResponse" in part && part.functionResponse?.response && - typeof part.functionResponse.response === 'object' && - 'error' in part.functionResponse.response && - part.functionResponse.response['error'] + typeof part.functionResponse.response === "object" && + "error" in part.functionResponse.response && + part.functionResponse.response["error"] ) { - const error = part.functionResponse.response['error']; - return typeof error === 'string' ? error : String(error); + const error = part.functionResponse.response["error"]; + return typeof error === "string" ? error : String(error); } } @@ -1285,9 +1218,7 @@ function checkResponsePartsForError( * @param response - Tool call response * @returns String content or undefined */ -export function toolResultContent( - response: ToolCallResponseInfo, -): string | undefined { +export function toolResultContent(response: ToolCallResponseInfo): string | undefined { if (response.error) { return response.error.message; } @@ -1296,10 +1227,7 @@ export function toolResultContent( if (responsePartsError) { return responsePartsError; } - if ( - typeof response.resultDisplay === 'string' && - response.resultDisplay.trim().length > 0 - ) { + if (typeof response.resultDisplay === "string" && response.resultDisplay.trim().length > 0) { return response.resultDisplay; } if (response.responseParts && response.responseParts.length > 0) { @@ -1318,9 +1246,9 @@ export function toolResultContent( */ export function extractTextFromBlocks(blocks: ContentBlock[]): string { return blocks - .filter((block) => block.type === 'text') - .map((block) => (block.type === 'text' ? block.text : '')) - .join(''); + .filter((block) => block.type === "text") + .map((block) => (block.type === "text" ? block.text : "")) + .join(""); } /** diff --git a/apps/airiscode-cli/src/nonInteractive/io/JsonOutputAdapter.ts b/apps/airiscode-cli/src/nonInteractive/io/JsonOutputAdapter.ts index 8ba836d74..1985475e4 100644 --- a/apps/airiscode-cli/src/nonInteractive/io/JsonOutputAdapter.ts +++ b/apps/airiscode-cli/src/nonInteractive/io/JsonOutputAdapter.ts @@ -4,23 +4,20 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { Config } from '@airiscode/core'; -import type { CLIAssistantMessage, CLIMessage } from '../types.js'; +import type { Config } from "@airiscode/core"; +import type { CLIAssistantMessage, CLIMessage } from "../types.js"; import { BaseJsonOutputAdapter, type JsonOutputAdapterInterface, type ResultOptions, -} from './BaseJsonOutputAdapter.js'; +} from "./BaseJsonOutputAdapter.js"; /** * JSON output adapter that collects all messages and emits them * as a single JSON array at the end of the turn. * Supports both main agent and subagent messages through distinct APIs. */ -export class JsonOutputAdapter - extends BaseJsonOutputAdapter - implements JsonOutputAdapterInterface -{ +export class JsonOutputAdapter extends BaseJsonOutputAdapter implements JsonOutputAdapterInterface { private readonly messages: CLIMessage[] = []; constructor(config: Config) { @@ -35,10 +32,10 @@ export class JsonOutputAdapter this.messages.push(message); // Track assistant messages for result generation if ( - typeof message === 'object' && + typeof message === "object" && message !== null && - 'type' in message && - message.type === 'assistant' + "type" in message && + message.type === "assistant" ) { this.updateLastAssistantMessage(message as CLIAssistantMessage); } @@ -52,22 +49,16 @@ export class JsonOutputAdapter } finalizeAssistantMessage(): CLIAssistantMessage { - return this.finalizeAssistantMessageInternal( - this.mainAgentMessageState, - null, - ); + return this.finalizeAssistantMessageInternal(this.mainAgentMessageState, null); } emitResult(options: ResultOptions): void { - const resultMessage = this.buildResultMessage( - options, - this.lastAssistantMessage, - ); + const resultMessage = this.buildResultMessage(options, this.lastAssistantMessage); this.messages.push(resultMessage); - if (this.config.getOutputFormat() === 'text') { + if (this.config.getOutputFormat() === "text") { if (resultMessage.is_error) { - process.stderr.write(`${resultMessage.error?.message || ''}`); + process.stderr.write(`${resultMessage.error?.message || ""}`); } else { process.stdout.write(`${resultMessage.result}`); } diff --git a/apps/airiscode-cli/src/nonInteractive/io/StreamJsonInputReader.ts b/apps/airiscode-cli/src/nonInteractive/io/StreamJsonInputReader.ts index f297d7415..dc1df6bdc 100644 --- a/apps/airiscode-cli/src/nonInteractive/io/StreamJsonInputReader.ts +++ b/apps/airiscode-cli/src/nonInteractive/io/StreamJsonInputReader.ts @@ -4,15 +4,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { createInterface } from 'node:readline/promises'; -import type { Readable } from 'node:stream'; -import process from 'node:process'; +import process from "node:process"; +import { createInterface } from "node:readline/promises"; +import type { Readable } from "node:stream"; import type { CLIControlRequest, CLIControlResponse, CLIMessage, ControlCancelRequest, -} from '../types.js'; +} from "../types.js"; export type StreamJsonInputMessage = | CLIMessage @@ -53,10 +53,10 @@ export class StreamJsonInputReader { private parse(line: string): StreamJsonInputMessage { try { const parsed = JSON.parse(line) as StreamJsonInputMessage; - if (!parsed || typeof parsed !== 'object') { - throw new StreamJsonParseError('Parsed value is not an object'); + if (!parsed || typeof parsed !== "object") { + throw new StreamJsonParseError("Parsed value is not an object"); } - if (!('type' in parsed) || typeof parsed.type !== 'string') { + if (!("type" in parsed) || typeof parsed.type !== "string") { throw new StreamJsonParseError('Missing required "type" field'); } return parsed; @@ -65,9 +65,7 @@ export class StreamJsonInputReader { throw error; } const reason = error instanceof Error ? error.message : String(error); - throw new StreamJsonParseError( - `Failed to parse stream-json line: ${reason}`, - ); + throw new StreamJsonParseError(`Failed to parse stream-json line: ${reason}`); } } } diff --git a/apps/airiscode-cli/src/nonInteractive/io/StreamJsonOutputAdapter.ts b/apps/airiscode-cli/src/nonInteractive/io/StreamJsonOutputAdapter.ts index b06e8e0df..537c254af 100644 --- a/apps/airiscode-cli/src/nonInteractive/io/StreamJsonOutputAdapter.ts +++ b/apps/airiscode-cli/src/nonInteractive/io/StreamJsonOutputAdapter.ts @@ -4,12 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { randomUUID } from 'node:crypto'; -import type { - Config, - ToolCallRequestInfo, - McpToolProgressData, -} from '@airiscode/core'; +import { randomUUID } from "node:crypto"; +import type { Config, McpToolProgressData, ToolCallRequestInfo } from "@airiscode/core"; import type { CLIAssistantMessage, CLIMessage, @@ -19,13 +15,13 @@ import type { TextBlock, ThinkingBlock, ToolUseBlock, -} from '../types.js'; +} from "../types.js"; import { BaseJsonOutputAdapter, + type JsonOutputAdapterInterface, type MessageState, type ResultOptions, - type JsonOutputAdapterInterface, -} from './BaseJsonOutputAdapter.js'; +} from "./BaseJsonOutputAdapter.js"; /** * Stream JSON output adapter that emits messages immediately @@ -51,10 +47,10 @@ export class StreamJsonOutputAdapter protected emitMessageImpl(message: CLIMessage | ControlMessage): void { // Track assistant messages for result generation if ( - typeof message === 'object' && + typeof message === "object" && message !== null && - 'type' in message && - message.type === 'assistant' + "type" in message && + message.type === "assistant" ) { this.updateLastAssistantMessage(message as CLIAssistantMessage); } @@ -76,17 +72,14 @@ export class StreamJsonOutputAdapter } finalizeAssistantMessage(): CLIAssistantMessage { - const message = this.finalizeAssistantMessageInternal( - this.mainAgentMessageState, - null, - ); + const message = this.finalizeAssistantMessageInternal(this.mainAgentMessageState, null); if (this.mainTurnMessageStartEmitted && this.includePartialMessages) { const partial: CLIPartialAssistantMessage = { - type: 'stream_event', + type: "stream_event", uuid: randomUUID(), session_id: this.getSessionId(), parent_tool_use_id: null, - event: { type: 'message_stop' }, + event: { type: "message_stop" }, }; this.emitMessageImpl(partial); } @@ -95,10 +88,7 @@ export class StreamJsonOutputAdapter } emitResult(options: ResultOptions): void { - const resultMessage = this.buildResultMessage( - options, - this.lastAssistantMessage, - ); + const resultMessage = this.buildResultMessage(options, this.lastAssistantMessage); this.emitMessageImpl(resultMessage); } @@ -122,7 +112,7 @@ export class StreamJsonOutputAdapter ): void { this.emitStreamEventIfEnabled( { - type: 'content_block_start', + type: "content_block_start", index, content_block: block, }, @@ -141,9 +131,9 @@ export class StreamJsonOutputAdapter ): void { this.emitStreamEventIfEnabled( { - type: 'content_block_delta', + type: "content_block_delta", index, - delta: { type: 'text_delta', text: fragment }, + delta: { type: "text_delta", text: fragment }, }, parentToolUseId, ); @@ -160,7 +150,7 @@ export class StreamJsonOutputAdapter ): void { this.emitStreamEventIfEnabled( { - type: 'content_block_start', + type: "content_block_start", index, content_block: block, }, @@ -179,9 +169,9 @@ export class StreamJsonOutputAdapter ): void { this.emitStreamEventIfEnabled( { - type: 'content_block_delta', + type: "content_block_delta", index, - delta: { type: 'thinking_delta', thinking: fragment }, + delta: { type: "thinking_delta", thinking: fragment }, }, parentToolUseId, ); @@ -198,7 +188,7 @@ export class StreamJsonOutputAdapter ): void { this.emitStreamEventIfEnabled( { - type: 'content_block_start', + type: "content_block_start", index, content_block: block, }, @@ -217,10 +207,10 @@ export class StreamJsonOutputAdapter ): void { this.emitStreamEventIfEnabled( { - type: 'content_block_delta', + type: "content_block_delta", index, delta: { - type: 'input_json_delta', + type: "input_json_delta", partial_json: JSON.stringify(input), }, }, @@ -239,7 +229,7 @@ export class StreamJsonOutputAdapter if (this.includePartialMessages) { this.emitStreamEventIfEnabled( { - type: 'content_block_stop', + type: "content_block_stop", index, }, parentToolUseId, @@ -260,10 +250,10 @@ export class StreamJsonOutputAdapter this.mainTurnMessageStartEmitted = true; this.emitStreamEventIfEnabled( { - type: 'message_start', + type: "message_start", message: { id: state.messageId!, - role: 'assistant', + role: "assistant", model: this.config.getModel(), content: [], }, @@ -277,21 +267,18 @@ export class StreamJsonOutputAdapter * Emits a tool progress stream event when partial messages are enabled. * This overrides the no-op in BaseJsonOutputAdapter. */ - override emitToolProgress( - request: ToolCallRequestInfo, - progress: McpToolProgressData, - ): void { + override emitToolProgress(request: ToolCallRequestInfo, progress: McpToolProgressData): void { if (!this.includePartialMessages) { return; } const partial: CLIPartialAssistantMessage = { - type: 'stream_event', + type: "stream_event", uuid: randomUUID(), session_id: this.getSessionId(), parent_tool_use_id: null, event: { - type: 'tool_progress', + type: "tool_progress", tool_use_id: request.callId, content: progress, }, @@ -305,16 +292,13 @@ export class StreamJsonOutputAdapter * @param event - Stream event to emit * @param parentToolUseId - null for main agent, string for subagent */ - private emitStreamEventIfEnabled( - event: StreamEvent, - parentToolUseId: string | null, - ): void { + private emitStreamEventIfEnabled(event: StreamEvent, parentToolUseId: string | null): void { if (!this.includePartialMessages) { return; } const partial: CLIPartialAssistantMessage = { - type: 'stream_event', + type: "stream_event", uuid: randomUUID(), session_id: this.getSessionId(), parent_tool_use_id: parentToolUseId, diff --git a/apps/airiscode-cli/src/nonInteractive/session.ts b/apps/airiscode-cli/src/nonInteractive/session.ts index 837ef2f1a..44086e01c 100644 --- a/apps/airiscode-cli/src/nonInteractive/session.ts +++ b/apps/airiscode-cli/src/nonInteractive/session.ts @@ -4,37 +4,34 @@ * SPDX-License-Identifier: Apache-2.0 */ +import type { Config, ConfigInitializeOptions } from "@airiscode/core"; +import { createDebugLogger } from "@airiscode/core"; +import { createMinimalSettings } from "../config/settings.js"; +import { runNonInteractive } from "../nonInteractiveCli.js"; +import { ControlContext } from "./control/ControlContext.js"; +import { ControlDispatcher } from "./control/ControlDispatcher.js"; +import { ControlService } from "./control/ControlService.js"; +import { StreamJsonInputReader } from "./io/StreamJsonInputReader.js"; +import { StreamJsonOutputAdapter } from "./io/StreamJsonOutputAdapter.js"; import type { - Config, - ConfigInitializeOptions, -} from '@airiscode/core'; -import { createDebugLogger } from '@airiscode/core'; -import { StreamJsonInputReader } from './io/StreamJsonInputReader.js'; -import { StreamJsonOutputAdapter } from './io/StreamJsonOutputAdapter.js'; -import { ControlContext } from './control/ControlContext.js'; -import { ControlDispatcher } from './control/ControlDispatcher.js'; -import { ControlService } from './control/ControlService.js'; -import type { - CLIMessage, - CLIUserMessage, CLIControlRequest, CLIControlResponse, + CLIMessage, + CLIUserMessage, ControlCancelRequest, -} from './types.js'; +} from "./types.js"; import { - isCLIUserMessage, isCLIAssistantMessage, - isCLISystemMessage, - isCLIResultMessage, isCLIPartialAssistantMessage, + isCLIResultMessage, + isCLISystemMessage, + isCLIUserMessage, + isControlCancel, isControlRequest, isControlResponse, - isControlCancel, -} from './types.js'; -import { createMinimalSettings } from '../config/settings.js'; -import { runNonInteractive } from '../nonInteractiveCli.js'; +} from "./types.js"; -const debugLogger = createDebugLogger('NON_INTERACTIVE_SESSION'); +const debugLogger = createDebugLogger("NON_INTERACTIVE_SESSION"); class Session { private userMessageQueue: CLIUserMessage[] = []; @@ -67,10 +64,7 @@ class Session { this.initialPrompt = initialPrompt ?? null; this.inputReader = new StreamJsonInputReader(); - this.outputAdapter = new StreamJsonOutputAdapter( - config, - config.getIncludePartialMessages(), - ); + this.outputAdapter = new StreamJsonOutputAdapter(config, config.getIncludePartialMessages()); this.setupSignalHandlers(); } @@ -98,20 +92,18 @@ class Session { return `${this.sessionId}########${this.promptIdCounter}`; } - private async ensureConfigInitialized( - options?: ConfigInitializeOptions, - ): Promise { + private async ensureConfigInitialized(options?: ConfigInitializeOptions): Promise { if (this.configInitialized) { return; } - debugLogger.debug('[Session] Initializing config'); + debugLogger.debug("[Session] Initializing config"); try { await this.config.initialize(options); this.configInitialized = true; } catch (error) { - debugLogger.error('[Session] Failed to initialize config:', error); + debugLogger.error("[Session] Failed to initialize config:", error); throw error; } } @@ -121,7 +113,7 @@ class Session { */ private completeInitialization(): void { if (this.initializationResolve) { - debugLogger.debug('[Session] Initialization complete'); + debugLogger.debug("[Session] Initialization complete"); this.initializationResolve(); this.initializationResolve = null; this.initializationReject = null; @@ -133,7 +125,7 @@ class Session { */ private failInitialization(error: Error): void { if (this.initializationReject) { - debugLogger.error('[Session] Initialization failed:', error); + debugLogger.error("[Session] Initialization failed:", error); this.initializationReject(error); this.initializationResolve = null; this.initializationReject = null; @@ -163,10 +155,7 @@ class Session { onInterrupt: () => this.handleInterrupt(), }); this.dispatcher = new ControlDispatcher(this.controlContext); - this.controlService = new ControlService( - this.controlContext, - this.dispatcher, - ); + this.controlService = new ControlService(this.controlContext, this.dispatcher); } private getDispatcher(): ControlDispatcher | null { @@ -188,26 +177,20 @@ class Session { * when ready for user messages. */ private handleFirstMessage( - message: - | CLIMessage - | CLIControlRequest - | CLIControlResponse - | ControlCancelRequest, + message: CLIMessage | CLIControlRequest | CLIControlResponse | ControlCancelRequest, ): void { if (isControlRequest(message)) { const request = message as CLIControlRequest; this.controlSystemEnabled = true; this.ensureControlSystem(); - if (request.request.subtype === 'initialize') { + if (request.request.subtype === "initialize") { // Start SDK mode initialization (fire-and-forget from loop perspective) void this.initializeSdkMode(request); return; } - debugLogger.debug( - '[Session] Ignoring non-initialize control request during initialization', - ); + debugLogger.debug("[Session] Ignoring non-initialize control request during initialization"); return; } @@ -235,8 +218,7 @@ class Session { // Get sendSdkMcpMessage callback from SdkMcpController // This callback is used by McpClientManager to send MCP messages // from CLI MCP clients to SDK MCP servers via the control plane - const sendSdkMcpMessage = - this.dispatcher?.sdkMcpController.createSendSdkMcpMessage(); + const sendSdkMcpMessage = this.dispatcher?.sdkMcpController.createSendSdkMcpMessage(); // Initialize config with SDK MCP message support await this.ensureConfigInitialized({ sendSdkMcpMessage }); @@ -244,10 +226,8 @@ class Session { // Initialization complete! this.completeInitialization(); } catch (error) { - debugLogger.error('[Session] SDK mode initialization failed:', error); - this.failInitialization( - error instanceof Error ? error : new Error(String(error)), - ); + debugLogger.error("[Session] SDK mode initialization failed:", error); + this.failInitialization(error instanceof Error ? error : new Error(String(error))); } } @@ -255,9 +235,7 @@ class Session { * Direct mode initialization flow * Initializes config and enqueues the first user message */ - private async initializeDirectMode( - userMessage: CLIUserMessage, - ): Promise { + private async initializeDirectMode(userMessage: CLIUserMessage): Promise { this.ensureInitializationPromise(); try { // Initialize config @@ -269,10 +247,8 @@ class Session { // Enqueue the first user message for processing this.enqueueUserMessage(userMessage); } catch (error) { - debugLogger.error('[Session] Direct mode initialization failed:', error); - this.failInitialization( - error instanceof Error ? error : new Error(String(error)), - ); + debugLogger.error("[Session] Direct mode initialization failed:", error); + this.failInitialization(error instanceof Error ? error : new Error(String(error))); } } @@ -283,14 +259,14 @@ class Session { private handleControlRequestAsync(request: CLIControlRequest): void { const dispatcher = this.getDispatcher(); if (!dispatcher) { - debugLogger.warn('[Session] Control system not enabled'); + debugLogger.warn("[Session] Control system not enabled"); return; } // Fire-and-forget: dispatch runs concurrently // The dispatcher's pendingIncomingRequests tracks completion void dispatcher.dispatch(request).catch((error) => { - debugLogger.error('[Session] Control request dispatch error:', error); + debugLogger.error("[Session] Control request dispatch error:", error); // Error response is already sent by dispatcher.dispatch() }); } @@ -320,7 +296,7 @@ class Session { private async processUserMessage(userMessage: CLIUserMessage): Promise { const input = extractUserMessageText(userMessage); if (!input) { - debugLogger.debug('[Session] No text content in user message'); + debugLogger.debug("[Session] No text content in user message"); return; } @@ -330,19 +306,13 @@ class Session { const promptId = this.getNextPromptId(); try { - await runNonInteractive( - this.config, - createMinimalSettings(), - input, - promptId, - { - abortController: this.abortController, - adapter: this.outputAdapter, - controlService: this.controlService ?? undefined, - }, - ); + await runNonInteractive(this.config, createMinimalSettings(), input, promptId, { + abortController: this.abortController, + adapter: this.outputAdapter, + controlService: this.controlService ?? undefined, + }); } catch (error) { - debugLogger.error('[Session] Query execution error:', error); + debugLogger.error("[Session] Query execution error:", error); } } @@ -360,7 +330,7 @@ class Session { try { await this.processUserMessage(userMessage); } catch (error) { - debugLogger.error('[Session] Error processing user message:', error); + debugLogger.error("[Session] Error processing user message:", error); this.emitErrorResult(error); } } @@ -406,7 +376,7 @@ class Session { } private handleInterrupt(): void { - debugLogger.info('[Session] Interrupt requested'); + debugLogger.info("[Session] Interrupt requested"); this.abortController.abort(); // Do not create a new AbortController to prevent listener leaks. // Subsequent queries will check signal.aborted and fail immediately. @@ -414,13 +384,13 @@ class Session { private setupSignalHandlers(): void { this.shutdownHandler = () => { - debugLogger.info('[Session] Shutdown signal received'); + debugLogger.info("[Session] Shutdown signal received"); this.isShuttingDown = true; this.abortController.abort(); }; - process.on('SIGINT', this.shutdownHandler); - process.on('SIGTERM', this.shutdownHandler); + process.on("SIGINT", this.shutdownHandler); + process.on("SIGTERM", this.shutdownHandler); } /** @@ -431,36 +401,31 @@ class Session { try { await this.waitForInitialization(); } catch (error) { - debugLogger.error( - '[Session] Initialization error during shutdown:', - error, - ); + debugLogger.error("[Session] Initialization error during shutdown:", error); } // 2. Wait for all control request handlers using dispatcher's tracking if (this.dispatcher) { const pendingCount = this.dispatcher.getPendingIncomingRequestCount(); if (pendingCount > 0) { - debugLogger.debug( - `[Session] Waiting for ${pendingCount} pending control request handlers`, - ); + debugLogger.debug(`[Session] Waiting for ${pendingCount} pending control request handlers`); } await this.dispatcher.waitForPendingIncomingRequests(); } // 3. Wait for user message processing queue while (this.processingPromise) { - debugLogger.debug('[Session] Waiting for user message processing'); + debugLogger.debug("[Session] Waiting for user message processing"); try { await this.processingPromise; } catch (error) { - debugLogger.error('[Session] Error in user message processing:', error); + debugLogger.error("[Session] Error in user message processing:", error); } } } private async shutdown(): Promise { - debugLogger.debug('[Session] Shutting down'); + debugLogger.debug("[Session] Shutting down"); this.isShuttingDown = true; @@ -473,8 +438,8 @@ class Session { private cleanupSignalHandlers(): void { if (this.shutdownHandler) { - process.removeListener('SIGINT', this.shutdownHandler); - process.removeListener('SIGTERM', this.shutdownHandler); + process.removeListener("SIGINT", this.shutdownHandler); + process.removeListener("SIGTERM", this.shutdownHandler); this.shutdownHandler = null; } } @@ -496,7 +461,7 @@ class Session { */ async run(): Promise { try { - debugLogger.info('[Session] Starting session', this.sessionId); + debugLogger.info("[Session] Starting session", this.sessionId); // Handle initial prompt if provided (fire-and-forget) if (this.initialPrompt !== null) { @@ -543,10 +508,7 @@ class Session { !isCLIResultMessage(message) && !isCLIPartialAssistantMessage(message) ) { - debugLogger.warn( - '[Session] Unknown message type:', - JSON.stringify(message, null, 2), - ); + debugLogger.warn("[Session] Unknown message type:", JSON.stringify(message, null, 2)); } if (this.isShuttingDown) { @@ -554,7 +516,7 @@ class Session { } } } catch (streamError) { - debugLogger.error('[Session] Stream reading error:', streamError); + debugLogger.error("[Session] Stream reading error:", streamError); throw streamError; } @@ -569,7 +531,7 @@ class Session { await this.waitForAllPendingWork(); await this.shutdown(); } catch (error) { - debugLogger.error('[Session] Error:', error); + debugLogger.error("[Session] Error:", error); await this.shutdown(); throw error; } finally { @@ -580,41 +542,38 @@ class Session { function extractUserMessageText(message: CLIUserMessage): string | null { const content = message.message.content; - if (typeof content === 'string') { + if (typeof content === "string") { return content; } if (Array.isArray(content)) { const parts = content .map((block) => { - if (!block || typeof block !== 'object') { - return ''; + if (!block || typeof block !== "object") { + return ""; } - if ('type' in block && block.type === 'text' && 'text' in block) { - return typeof block.text === 'string' ? block.text : ''; + if ("type" in block && block.type === "text" && "text" in block) { + return typeof block.text === "string" ? block.text : ""; } return JSON.stringify(block); }) .filter((part) => part.length > 0); - return parts.length > 0 ? parts.join('\n') : null; + return parts.length > 0 ? parts.join("\n") : null; } return null; } -export async function runNonInteractiveStreamJson( - config: Config, - input: string, -): Promise { +export async function runNonInteractiveStreamJson(config: Config, input: string): Promise { let initialPrompt: CLIUserMessage | undefined = undefined; if (input && input.trim().length > 0) { const sessionId = config.getSessionId(); initialPrompt = { - type: 'user', + type: "user", session_id: sessionId, message: { - role: 'user', + role: "user", content: input.trim(), }, parent_tool_use_id: null, diff --git a/apps/airiscode-cli/src/nonInteractive/types.ts b/apps/airiscode-cli/src/nonInteractive/types.ts index abaaae898..866d5178a 100644 --- a/apps/airiscode-cli/src/nonInteractive/types.ts +++ b/apps/airiscode-cli/src/nonInteractive/types.ts @@ -1,8 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import type { - SubagentConfig, - McpToolProgressData, -} from '@airiscode/core'; +import type { McpToolProgressData, SubagentConfig } from "@airiscode/core"; /** * Annotation for attaching metadata to content blocks @@ -56,20 +53,20 @@ export interface CLIPermissionDenial { * Content block types from Anthropic SDK */ export interface TextBlock { - type: 'text'; + type: "text"; text: string; annotations?: Annotation[]; } export interface ThinkingBlock { - type: 'thinking'; + type: "thinking"; thinking: string; signature?: string; annotations?: Annotation[]; } export interface ToolUseBlock { - type: 'tool_use'; + type: "tool_use"; id: string; name: string; input: unknown; @@ -77,31 +74,27 @@ export interface ToolUseBlock { } export interface ToolResultBlock { - type: 'tool_result'; + type: "tool_result"; tool_use_id: string; content?: string | ContentBlock[]; is_error?: boolean; annotations?: Annotation[]; } -export type ContentBlock = - | TextBlock - | ThinkingBlock - | ToolUseBlock - | ToolResultBlock; +export type ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock | ToolResultBlock; /** * Anthropic SDK Message types */ export interface APIUserMessage { - role: 'user'; + role: "user"; content: string | ContentBlock[]; } export interface APIAssistantMessage { id: string; - type: 'message'; - role: 'assistant'; + type: "message"; + role: "assistant"; model: string; content: ContentBlock[]; stop_reason?: string | null; @@ -112,7 +105,7 @@ export interface APIAssistantMessage { * CLI Message wrapper types */ export interface CLIUserMessage { - type: 'user'; + type: "user"; uuid?: string; session_id: string; message: APIUserMessage; @@ -121,7 +114,7 @@ export interface CLIUserMessage { } export interface CLIAssistantMessage { - type: 'assistant'; + type: "assistant"; uuid: string; session_id: string; message: APIAssistantMessage; @@ -129,7 +122,7 @@ export interface CLIAssistantMessage { } export interface CLISystemMessage { - type: 'system'; + type: "system"; subtype: string; uuid: string; session_id: string; @@ -149,14 +142,14 @@ export interface CLISystemMessage { skills?: string[]; capabilities?: Record; compact_metadata?: { - trigger: 'manual' | 'auto'; + trigger: "manual" | "auto"; pre_tokens: number; }; } export interface CLIResultMessageSuccess { - type: 'result'; - subtype: 'success'; + type: "result"; + subtype: "success"; uuid: string; session_id: string; is_error: false; @@ -171,8 +164,8 @@ export interface CLIResultMessageSuccess { } export interface CLIResultMessageError { - type: 'result'; - subtype: 'error_max_turns' | 'error_during_execution'; + type: "result"; + subtype: "error_max_turns" | "error_during_execution"; uuid: string; session_id: string; is_error: true; @@ -196,52 +189,52 @@ export type CLIResultMessage = CLIResultMessageSuccess | CLIResultMessageError; * Stream event types for real-time message updates */ export interface MessageStartStreamEvent { - type: 'message_start'; + type: "message_start"; message: { id: string; - role: 'assistant'; + role: "assistant"; model: string; content: []; }; } export interface ContentBlockStartEvent { - type: 'content_block_start'; + type: "content_block_start"; index: number; content_block: ContentBlock; } export type ContentBlockDelta = | { - type: 'text_delta'; + type: "text_delta"; text: string; } | { - type: 'thinking_delta'; + type: "thinking_delta"; thinking: string; } | { - type: 'input_json_delta'; + type: "input_json_delta"; partial_json: string; }; export interface ContentBlockDeltaEvent { - type: 'content_block_delta'; + type: "content_block_delta"; index: number; delta: ContentBlockDelta; } export interface ContentBlockStopEvent { - type: 'content_block_stop'; + type: "content_block_stop"; index: number; } export interface MessageStopStreamEvent { - type: 'message_stop'; + type: "message_stop"; } export interface ToolProgressStreamEvent { - type: 'tool_progress'; + type: "tool_progress"; tool_use_id: string; content: McpToolProgressData; } @@ -255,21 +248,21 @@ export type StreamEvent = | ToolProgressStreamEvent; export interface CLIPartialAssistantMessage { - type: 'stream_event'; + type: "stream_event"; uuid: string; session_id: string; event: StreamEvent; parent_tool_use_id: string | null; } -export type PermissionMode = 'default' | 'plan' | 'auto-edit' | 'yolo'; +export type PermissionMode = "default" | "plan" | "auto-edit" | "yolo"; /** * Permission suggestion for tool use requests * TODO: Align with `ToolCallConfirmationDetails` */ export interface PermissionSuggestion { - type: 'allow' | 'deny' | 'modify'; + type: "allow" | "deny" | "modify"; label: string; description?: string; modifiedInput?: unknown; @@ -294,11 +287,11 @@ export interface HookCallbackResult { } export interface CLIControlInterruptRequest { - subtype: 'interrupt'; + subtype: "interrupt"; } export interface CLIControlPermissionRequest { - subtype: 'can_use_tool'; + subtype: "can_use_tool"; tool_name: string; tool_use_id: string; input: unknown; @@ -311,7 +304,7 @@ export interface CLIControlPermissionRequest { * The actual Server instance stays in the SDK process. */ export interface SDKMcpServerConfig { - type: 'sdk'; + type: "sdk"; name: string; } @@ -346,23 +339,20 @@ export interface CLIMcpServerConfig { tokenParamName?: string; registrationUrl?: string; }; - authProviderType?: - | 'dynamic_discovery' - | 'google_credentials' - | 'service_account_impersonation'; + authProviderType?: "dynamic_discovery" | "google_credentials" | "service_account_impersonation"; targetAudience?: string; targetServiceAccount?: string; } export interface CLIControlInitializeRequest { - subtype: 'initialize'; + subtype: "initialize"; hooks?: HookRegistration[] | null; /** * SDK MCP servers config * These are MCP servers running in the SDK process, connected via control plane. * External MCP servers are configured separately in settings, not via initialization. */ - sdkMcpServers?: Record>; + sdkMcpServers?: Record>; /** * External MCP servers that the SDK wants the CLI to manage. * These run outside the SDK process and require CLI-side transport setup. @@ -372,19 +362,19 @@ export interface CLIControlInitializeRequest { } export interface CLIControlSetPermissionModeRequest { - subtype: 'set_permission_mode'; + subtype: "set_permission_mode"; mode: PermissionMode; } export interface CLIHookCallbackRequest { - subtype: 'hook_callback'; + subtype: "hook_callback"; callback_id: string; input: unknown; tool_use_id: string | null; } export interface CLIControlMcpMessageRequest { - subtype: 'mcp_message'; + subtype: "mcp_message"; server_name: string; message: { jsonrpc?: string; @@ -395,16 +385,16 @@ export interface CLIControlMcpMessageRequest { } export interface CLIControlSetModelRequest { - subtype: 'set_model'; + subtype: "set_model"; model: string; } export interface CLIControlMcpStatusRequest { - subtype: 'mcp_server_status'; + subtype: "mcp_server_status"; } export interface CLIControlSupportedCommandsRequest { - subtype: 'supported_commands'; + subtype: "supported_commands"; } export type ControlRequestPayload = @@ -419,7 +409,7 @@ export type ControlRequestPayload = | CLIControlSupportedCommandsRequest; export interface CLIControlRequest { - type: 'control_request'; + type: "control_request"; request_id: string; request: ControlRequestPayload; } @@ -434,31 +424,28 @@ export interface PermissionApproval { } export interface ControlResponse { - subtype: 'success'; + subtype: "success"; request_id: string; response: unknown; } export interface ControlErrorResponse { - subtype: 'error'; + subtype: "error"; request_id: string; error: string | { message: string; [key: string]: unknown }; } export interface CLIControlResponse { - type: 'control_response'; + type: "control_response"; response: ControlResponse | ControlErrorResponse; } export interface ControlCancelRequest { - type: 'control_cancel_request'; + type: "control_cancel_request"; request_id?: string; } -export type ControlMessage = - | CLIControlRequest - | CLIControlResponse - | ControlCancelRequest; +export type ControlMessage = CLIControlRequest | CLIControlResponse | ControlCancelRequest; /** * Union of all CLI message types @@ -475,86 +462,74 @@ export type CLIMessage = */ export function isCLIUserMessage(msg: any): msg is CLIUserMessage { - return ( - msg && typeof msg === 'object' && msg.type === 'user' && 'message' in msg - ); + return msg && typeof msg === "object" && msg.type === "user" && "message" in msg; } export function isCLIAssistantMessage(msg: any): msg is CLIAssistantMessage { return ( msg && - typeof msg === 'object' && - msg.type === 'assistant' && - 'uuid' in msg && - 'message' in msg && - 'session_id' in msg && - 'parent_tool_use_id' in msg + typeof msg === "object" && + msg.type === "assistant" && + "uuid" in msg && + "message" in msg && + "session_id" in msg && + "parent_tool_use_id" in msg ); } export function isCLISystemMessage(msg: any): msg is CLISystemMessage { return ( msg && - typeof msg === 'object' && - msg.type === 'system' && - 'subtype' in msg && - 'uuid' in msg && - 'session_id' in msg + typeof msg === "object" && + msg.type === "system" && + "subtype" in msg && + "uuid" in msg && + "session_id" in msg ); } export function isCLIResultMessage(msg: any): msg is CLIResultMessage { return ( msg && - typeof msg === 'object' && - msg.type === 'result' && - 'subtype' in msg && - 'duration_ms' in msg && - 'is_error' in msg && - 'uuid' in msg && - 'session_id' in msg + typeof msg === "object" && + msg.type === "result" && + "subtype" in msg && + "duration_ms" in msg && + "is_error" in msg && + "uuid" in msg && + "session_id" in msg ); } -export function isCLIPartialAssistantMessage( - msg: any, -): msg is CLIPartialAssistantMessage { +export function isCLIPartialAssistantMessage(msg: any): msg is CLIPartialAssistantMessage { return ( msg && - typeof msg === 'object' && - msg.type === 'stream_event' && - 'uuid' in msg && - 'session_id' in msg && - 'event' in msg && - 'parent_tool_use_id' in msg + typeof msg === "object" && + msg.type === "stream_event" && + "uuid" in msg && + "session_id" in msg && + "event" in msg && + "parent_tool_use_id" in msg ); } export function isControlRequest(msg: any): msg is CLIControlRequest { return ( msg && - typeof msg === 'object' && - msg.type === 'control_request' && - 'request_id' in msg && - 'request' in msg + typeof msg === "object" && + msg.type === "control_request" && + "request_id" in msg && + "request" in msg ); } export function isControlResponse(msg: any): msg is CLIControlResponse { - return ( - msg && - typeof msg === 'object' && - msg.type === 'control_response' && - 'response' in msg - ); + return msg && typeof msg === "object" && msg.type === "control_response" && "response" in msg; } export function isControlCancel(msg: any): msg is ControlCancelRequest { return ( - msg && - typeof msg === 'object' && - msg.type === 'control_cancel_request' && - 'request_id' in msg + msg && typeof msg === "object" && msg.type === "control_cancel_request" && "request_id" in msg ); } @@ -563,17 +538,17 @@ export function isControlCancel(msg: any): msg is ControlCancelRequest { */ export function isTextBlock(block: any): block is TextBlock { - return block && typeof block === 'object' && block.type === 'text'; + return block && typeof block === "object" && block.type === "text"; } export function isThinkingBlock(block: any): block is ThinkingBlock { - return block && typeof block === 'object' && block.type === 'thinking'; + return block && typeof block === "object" && block.type === "thinking"; } export function isToolUseBlock(block: any): block is ToolUseBlock { - return block && typeof block === 'object' && block.type === 'tool_use'; + return block && typeof block === "object" && block.type === "tool_use"; } export function isToolResultBlock(block: any): block is ToolResultBlock { - return block && typeof block === 'object' && block.type === 'tool_result'; + return block && typeof block === "object" && block.type === "tool_result"; } diff --git a/apps/airiscode-cli/src/nonInteractiveCli.ts b/apps/airiscode-cli/src/nonInteractiveCli.ts index f8ebe03ea..705593df2 100644 --- a/apps/airiscode-cli/src/nonInteractiveCli.ts +++ b/apps/airiscode-cli/src/nonInteractiveCli.ts @@ -4,48 +4,47 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { Config, ToolCallRequestInfo } from '@airiscode/core'; -import { isSlashCommand } from './ui/utils/commandUtils.js'; -import type { LoadedSettings } from './config/settings.js'; +import type { Config, Content, Part, PartListUnion, ToolCallRequestInfo } from "@airiscode/core"; import { + createDebugLogger, executeToolCall, - shutdownTelemetry, - isTelemetrySdkInitialized, - GeminiEventType, FatalInputError, - promptIdContext, - OutputFormat, + GeminiEventType, InputFormat, - uiTelemetryService, + isTelemetrySdkInitialized, + OutputFormat, parseAndFormatApiError, - createDebugLogger, + promptIdContext, SendMessageType, -} from '@airiscode/core'; -import type { Content, Part, PartListUnion } from '@airiscode/core'; -import type { CLIUserMessage, PermissionMode } from './nonInteractive/types.js'; -import type { JsonOutputAdapterInterface } from './nonInteractive/io/BaseJsonOutputAdapter.js'; -import { JsonOutputAdapter } from './nonInteractive/io/JsonOutputAdapter.js'; -import { StreamJsonOutputAdapter } from './nonInteractive/io/StreamJsonOutputAdapter.js'; -import type { ControlService } from './nonInteractive/control/ControlService.js'; - -import { handleSlashCommand } from './nonInteractiveCliCommands.js'; -import { handleAtCommand } from './ui/hooks/atCommandProcessor.js'; + shutdownTelemetry, + uiTelemetryService, +} from "@airiscode/core"; +import type { LoadedSettings } from "./config/settings.js"; +import type { ControlService } from "./nonInteractive/control/ControlService.js"; +import type { JsonOutputAdapterInterface } from "./nonInteractive/io/BaseJsonOutputAdapter.js"; +import { JsonOutputAdapter } from "./nonInteractive/io/JsonOutputAdapter.js"; +import { StreamJsonOutputAdapter } from "./nonInteractive/io/StreamJsonOutputAdapter.js"; +import type { CLIUserMessage, PermissionMode } from "./nonInteractive/types.js"; +import { handleSlashCommand } from "./nonInteractiveCliCommands.js"; +import { handleAtCommand } from "./ui/hooks/atCommandProcessor.js"; +import { isSlashCommand } from "./ui/utils/commandUtils.js"; import { - handleError, - handleToolError, handleCancellationError, + handleError, handleMaxTurnsExceededError, -} from './utils/errors.js'; + handleToolError, +} from "./utils/errors.js"; + +const debugLogger = createDebugLogger("NON_INTERACTIVE_CLI"); -const debugLogger = createDebugLogger('NON_INTERACTIVE_CLI'); import { - normalizePartList, - extractPartsFromUserMessage, buildSystemMessage, - createToolProgressHandler, - createAgentToolProgressHandler, computeUsageFromMetrics, -} from './utils/nonInteractiveHelpers.js'; + createAgentToolProgressHandler, + createToolProgressHandler, + extractPartsFromUserMessage, + normalizePartList, +} from "./utils/nonInteractiveHelpers.js"; /** * Emits a final message for slash command results. @@ -66,16 +65,13 @@ async function emitNonInteractiveFinalMessage(params: { adapter.processEvent({ type: GeminiEventType.Content, value: message, - } as unknown as Parameters[0]); + } as unknown as Parameters[0]); adapter.finalizeAssistantMessage(); const metrics = uiTelemetryService.getMetrics(); const usage = computeUsageFromMetrics(metrics); const outputFormat = config.getOutputFormat(); - const stats = - outputFormat === OutputFormat.JSON - ? uiTelemetryService.getMetrics() - : undefined; + const stats = outputFormat === OutputFormat.JSON ? uiTelemetryService.getMetrics() : undefined; adapter.emitResult({ isError, @@ -122,10 +118,7 @@ export async function runNonInteractive( if (options.adapter) { adapter = options.adapter; } else if (outputFormat === OutputFormat.STREAM_JSON) { - adapter = new StreamJsonOutputAdapter( - config, - config.getIncludePartialMessages(), - ); + adapter = new StreamJsonOutputAdapter(config, config.getIncludePartialMessages()); } else { adapter = new JsonOutputAdapter(config); } @@ -139,8 +132,8 @@ export async function runNonInteractive( const startTime = Date.now(); const stdoutErrorHandler = (err: NodeJS.ErrnoException) => { - if (err.code === 'EPIPE') { - process.stdout.removeListener('error', stdoutErrorHandler); + if (err.code === "EPIPE") { + process.stdout.removeListener("error", stdoutErrorHandler); process.exit(0); } }; @@ -150,27 +143,21 @@ export async function runNonInteractive( // Setup signal handlers for graceful shutdown const shutdownHandler = () => { - debugLogger.debug('[runNonInteractive] Shutdown signal received'); + debugLogger.debug("[runNonInteractive] Shutdown signal received"); abortController.abort(); }; try { - process.stdout.on('error', stdoutErrorHandler); + process.stdout.on("error", stdoutErrorHandler); - process.on('SIGINT', shutdownHandler); - process.on('SIGTERM', shutdownHandler); + process.on("SIGINT", shutdownHandler); + process.on("SIGTERM", shutdownHandler); // Emit systemMessage first (always the first message in JSON mode) - const systemMessage = await buildSystemMessage( - config, - sessionId, - permissionMode, - ); + const systemMessage = await buildSystemMessage(config, sessionId, permissionMode); adapter.emitMessage(systemMessage); - let initialPartList: PartListUnion | null = extractPartsFromUserMessage( - options.userMessage, - ); + let initialPartList: PartListUnion | null = extractPartsFromUserMessage(options.userMessage); if (!initialPartList) { let slashHandled = false; @@ -182,27 +169,27 @@ export async function runNonInteractive( settings, ); switch (slashCommandResult.type) { - case 'submit_prompt': + case "submit_prompt": // A slash command can replace the prompt entirely; fall back to @-command processing otherwise. initialPartList = slashCommandResult.content; slashHandled = true; break; - case 'message': { + case "message": { // systemMessage already emitted above await emitNonInteractiveFinalMessage({ message: slashCommandResult.content, - isError: slashCommandResult.messageType === 'error', + isError: slashCommandResult.messageType === "error", adapter, config, startTimeMs: startTime, }); return; } - case 'stream_messages': + case "stream_messages": throw new FatalInputError( - 'Stream messages mode is not supported in non-interactive CLI', + "Stream messages mode is not supported in non-interactive CLI", ); - case 'unsupported': { + case "unsupported": { await emitNonInteractiveFinalMessage({ message: slashCommandResult.reason, isError: true, @@ -212,7 +199,7 @@ export async function runNonInteractive( }); return; } - case 'no_command': + case "no_command": break; default: { const _exhaustive: never = slashCommandResult; @@ -235,9 +222,7 @@ export async function runNonInteractive( if (!shouldProceed || !processedQuery) { // An error occurred during @include processing (e.g., file not found). // The error message is already logged by handleAtCommand. - throw new FatalInputError( - 'Exiting due to an error processing the @ command.', - ); + throw new FatalInputError("Exiting due to an error processing the @ command."); } initialPartList = processedQuery as PartListUnion; } @@ -248,15 +233,12 @@ export async function runNonInteractive( } const initialParts = normalizePartList(initialPartList); - let currentMessages: Content[] = [{ role: 'user', parts: initialParts }]; + let currentMessages: Content[] = [{ role: "user", parts: initialParts }]; let isFirstTurn = true; while (true) { turnCount++; - if ( - config.getMaxSessionTurns() >= 0 && - turnCount > config.getMaxSessionTurns() - ) { + if (config.getMaxSessionTurns() >= 0 && turnCount > config.getMaxSessionTurns()) { handleMaxTurnsExceededError(config); } @@ -267,9 +249,7 @@ export async function runNonInteractive( abortController.signal, prompt_id, { - type: isFirstTurn - ? SendMessageType.UserQuery - : SendMessageType.ToolResult, + type: isFirstTurn ? SendMessageType.UserQuery : SendMessageType.ToolResult, }, ); isFirstTurn = false; @@ -286,10 +266,7 @@ export async function runNonInteractive( if (event.type === GeminiEventType.ToolCallRequest) { toolCallRequests.push(event.value); } - if ( - outputFormat === OutputFormat.TEXT && - event.type === GeminiEventType.Error - ) { + if (outputFormat === OutputFormat.TEXT && event.type === GeminiEventType.Error) { const errorText = parseAndFormatApiError( event.value.error, config.getContentGeneratorConfig()?.authType, @@ -311,7 +288,7 @@ export async function runNonInteractive( const finalRequestInfo = requestInfo; const inputFormat = - typeof config.getInputFormat === 'function' + typeof config.getInputFormat === "function" ? config.getInputFormat() : InputFormat.TEXT; const toolCallUpdateCallback = @@ -323,13 +300,9 @@ export async function runNonInteractive( // Agent tool has its own complex handler (subagent messages). // All other tools with canUpdateOutput=true (e.g., MCP tools) // get a generic handler that emits progress via the adapter. - const isAgentTool = finalRequestInfo.name === 'agent'; + const isAgentTool = finalRequestInfo.name === "agent"; const { handler: outputUpdateHandler } = isAgentTool - ? createAgentToolProgressHandler( - config, - finalRequestInfo.callId, - adapter, - ) + ? createAgentToolProgressHandler(config, finalRequestInfo.callId, adapter) : createToolProgressHandler(finalRequestInfo, adapter); const toolResponse = await executeToolCall( @@ -356,8 +329,8 @@ export async function runNonInteractive( finalRequestInfo.name, toolResponse.error, config, - toolResponse.errorType || 'TOOL_EXECUTION_ERROR', - typeof toolResponse.resultDisplay === 'string' + toolResponse.errorType || "TOOL_EXECUTION_ERROR", + typeof toolResponse.resultDisplay === "string" ? toolResponse.resultDisplay : undefined, ); @@ -369,12 +342,10 @@ export async function runNonInteractive( toolResponseParts.push(...toolResponse.responseParts); } } - currentMessages = [{ role: 'user', parts: toolResponseParts }]; + currentMessages = [{ role: "user", parts: toolResponseParts }]; } else { // No more tool calls — check if cron jobs are keeping us alive - const scheduler = !config.isCronEnabled() - ? null - : config.getCronScheduler(); + const scheduler = !config.isCronEnabled() ? null : config.getCronScheduler(); if (scheduler && scheduler.size > 0) { // Start the scheduler and wait for all jobs to complete or be deleted. // Each fired prompt is processed as a new turn through the same loop. @@ -396,9 +367,7 @@ export async function runNonInteractive( while (cronQueue.length > 0) { const cronPrompt = cronQueue.shift()!; turnCount++; - let cronMessages: Content[] = [ - { role: 'user', parts: [{ text: cronPrompt }] }, - ]; + let cronMessages: Content[] = [{ role: "user", parts: [{ text: cronPrompt }] }]; let cronIsFirstTurn = true; while (true) { @@ -409,9 +378,7 @@ export async function runNonInteractive( abortController.signal, prompt_id, { - type: cronIsFirstTurn - ? SendMessageType.Cron - : SendMessageType.ToolResult, + type: cronIsFirstTurn ? SendMessageType.Cron : SendMessageType.ToolResult, }, ); cronIsFirstTurn = false; @@ -423,7 +390,7 @@ export async function runNonInteractive( const summary = scheduler.getExitSummary(); scheduler.stop(); if (summary) { - process.stderr.write(summary + '\n'); + process.stderr.write(summary + "\n"); } resolve(); return; @@ -441,13 +408,9 @@ export async function runNonInteractive( const cronToolResponseParts: Part[] = []; for (const requestInfo of cronToolCallRequests) { - const isAgentTool = requestInfo.name === 'agent'; + const isAgentTool = requestInfo.name === "agent"; const { handler: outputUpdateHandler } = isAgentTool - ? createAgentToolProgressHandler( - config, - requestInfo.callId, - adapter, - ) + ? createAgentToolProgressHandler(config, requestInfo.callId, adapter) : createToolProgressHandler(requestInfo, adapter); const toolResponse = await executeToolCall( @@ -462,8 +425,8 @@ export async function runNonInteractive( requestInfo.name, toolResponse.error, config, - toolResponse.errorType || 'TOOL_EXECUTION_ERROR', - typeof toolResponse.resultDisplay === 'string' + toolResponse.errorType || "TOOL_EXECUTION_ERROR", + typeof toolResponse.resultDisplay === "string" ? toolResponse.resultDisplay : undefined, ); @@ -472,21 +435,17 @@ export async function runNonInteractive( adapter.emitToolResult(requestInfo, toolResponse); if (toolResponse.responseParts) { - cronToolResponseParts.push( - ...toolResponse.responseParts, - ); + cronToolResponseParts.push(...toolResponse.responseParts); } } - cronMessages = [ - { role: 'user', parts: cronToolResponseParts }, - ]; + cronMessages = [{ role: "user", parts: cronToolResponseParts }]; } else { break; } } } } catch (error) { - debugLogger.error('Error processing cron prompt:', error); + debugLogger.error("Error processing cron prompt:", error); } finally { processing = false; checkDone(); @@ -507,9 +466,7 @@ export async function runNonInteractive( const usage = computeUsageFromMetrics(metrics); // Get stats for JSON format output const stats = - outputFormat === OutputFormat.JSON - ? uiTelemetryService.getMetrics() - : undefined; + outputFormat === OutputFormat.JSON ? uiTelemetryService.getMetrics() : undefined; adapter.emitResult({ isError: false, durationMs: Date.now() - startTime, @@ -538,9 +495,7 @@ export async function runNonInteractive( const usage = computeUsageFromMetrics(metrics); // Get stats for JSON format output const stats = - outputFormat === OutputFormat.JSON - ? uiTelemetryService.getMetrics() - : undefined; + outputFormat === OutputFormat.JSON ? uiTelemetryService.getMetrics() : undefined; adapter.emitResult({ isError: true, durationMs: Date.now() - startTime, @@ -552,10 +507,10 @@ export async function runNonInteractive( }); handleError(error, config); } finally { - process.stdout.removeListener('error', stdoutErrorHandler); + process.stdout.removeListener("error", stdoutErrorHandler); // Cleanup signal handlers - process.removeListener('SIGINT', shutdownHandler); - process.removeListener('SIGTERM', shutdownHandler); + process.removeListener("SIGINT", shutdownHandler); + process.removeListener("SIGTERM", shutdownHandler); if (isTelemetrySdkInitialized()) { await shutdownTelemetry(); } diff --git a/apps/airiscode-cli/src/nonInteractiveCliCommands.ts b/apps/airiscode-cli/src/nonInteractiveCliCommands.ts index 4215cec92..7faacbcef 100644 --- a/apps/airiscode-cli/src/nonInteractiveCliCommands.ts +++ b/apps/airiscode-cli/src/nonInteractiveCliCommands.ts @@ -4,30 +4,25 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { PartListUnion } from '@airiscode/core'; -import { parseSlashCommand } from './utils/commands.js'; +import type { PartListUnion } from "@airiscode/core"; +import { type Config, createDebugLogger, Logger, uiTelemetryService } from "@airiscode/core"; +import type { LoadedSettings } from "./config/settings.js"; +import { t } from "./i18n/index.js"; +import { BuiltinCommandLoader } from "./services/BuiltinCommandLoader.js"; +import { BundledSkillLoader } from "./services/BundledSkillLoader.js"; +import { CommandService } from "./services/CommandService.js"; +import { FileCommandLoader } from "./services/FileCommandLoader.js"; import { - Logger, - uiTelemetryService, - type Config, - createDebugLogger, -} from '@airiscode/core'; -import { CommandService } from './services/CommandService.js'; -import { BuiltinCommandLoader } from './services/BuiltinCommandLoader.js'; -import { BundledSkillLoader } from './services/BundledSkillLoader.js'; -import { FileCommandLoader } from './services/FileCommandLoader.js'; -import { - CommandKind, type CommandContext, + CommandKind, type SlashCommand, type SlashCommandActionReturn, -} from './ui/commands/types.js'; -import { createNonInteractiveUI } from './ui/noninteractive/nonInteractiveUi.js'; -import type { LoadedSettings } from './config/settings.js'; -import type { SessionStatsState } from './ui/contexts/SessionContext.js'; -import { t } from './i18n/index.js'; +} from "./ui/commands/types.js"; +import type { SessionStatsState } from "./ui/contexts/SessionContext.js"; +import { createNonInteractiveUI } from "./ui/noninteractive/nonInteractiveUi.js"; +import { parseSlashCommand } from "./utils/commands.js"; -const debugLogger = createDebugLogger('NON_INTERACTIVE_COMMANDS'); +const debugLogger = createDebugLogger("NON_INTERACTIVE_COMMANDS"); /** * Built-in commands that are allowed in non-interactive modes (CLI and ACP). @@ -39,11 +34,11 @@ const debugLogger = createDebugLogger('NON_INTERACTIVE_COMMANDS'); * - compress: Compress conversation history */ export const ALLOWED_BUILTIN_COMMANDS_NON_INTERACTIVE = [ - 'init', - 'summary', - 'compress', - 'btw', - 'bug', + "init", + "summary", + "compress", + "btw", + "bug", ] as const; /** @@ -58,29 +53,25 @@ export const ALLOWED_BUILTIN_COMMANDS_NON_INTERACTIVE = [ */ export type NonInteractiveSlashCommandResult = | { - type: 'submit_prompt'; + type: "submit_prompt"; content: PartListUnion; } | { - type: 'message'; - messageType: 'info' | 'error'; + type: "message"; + messageType: "info" | "error"; content: string; } | { - type: 'stream_messages'; - messages: AsyncGenerator< - { messageType: 'info' | 'error'; content: string }, - void, - unknown - >; + type: "stream_messages"; + messages: AsyncGenerator<{ messageType: "info" | "error"; content: string }, void, unknown>; } | { - type: 'unsupported'; + type: "unsupported"; reason: string; originalType: string; } | { - type: 'no_command'; + type: "no_command"; }; /** @@ -96,26 +87,24 @@ export type NonInteractiveSlashCommandResult = * @param result The result from executing a slash command action * @returns A NonInteractiveSlashCommandResult describing the outcome */ -function handleCommandResult( - result: SlashCommandActionReturn, -): NonInteractiveSlashCommandResult { +function handleCommandResult(result: SlashCommandActionReturn): NonInteractiveSlashCommandResult { switch (result.type) { - case 'submit_prompt': + case "submit_prompt": return { - type: 'submit_prompt', + type: "submit_prompt", content: result.content, }; - case 'message': + case "message": return { - type: 'message', + type: "message", messageType: result.messageType, content: result.content, }; - case 'stream_messages': + case "stream_messages": return { - type: 'stream_messages', + type: "stream_messages", messages: result.messages, }; @@ -124,60 +113,59 @@ function handleCommandResult( * whitelist of allowed slash commands in ACP and non-interactive mode. * We'll try to add more supported return types in the future. */ - case 'tool': + case "tool": return { - type: 'unsupported', - reason: - 'Tool execution from slash commands is not supported in non-interactive mode.', - originalType: 'tool', + type: "unsupported", + reason: "Tool execution from slash commands is not supported in non-interactive mode.", + originalType: "tool", }; - case 'quit': + case "quit": return { - type: 'unsupported', + type: "unsupported", reason: - 'Quit command is not supported in non-interactive mode. The process will exit naturally after completion.', - originalType: 'quit', + "Quit command is not supported in non-interactive mode. The process will exit naturally after completion.", + originalType: "quit", }; - case 'dialog': + case "dialog": return { - type: 'unsupported', + type: "unsupported", reason: `Dialog '${result.dialog}' cannot be opened in non-interactive mode.`, - originalType: 'dialog', + originalType: "dialog", }; - case 'load_history': + case "load_history": return { - type: 'unsupported', + type: "unsupported", reason: - 'Loading history is not supported in non-interactive mode. Each invocation starts with a fresh context.', - originalType: 'load_history', + "Loading history is not supported in non-interactive mode. Each invocation starts with a fresh context.", + originalType: "load_history", }; - case 'confirm_shell_commands': + case "confirm_shell_commands": return { - type: 'unsupported', + type: "unsupported", reason: - 'Shell command confirmation is not supported in non-interactive mode. Use YOLO mode or pre-approve commands.', - originalType: 'confirm_shell_commands', + "Shell command confirmation is not supported in non-interactive mode. Use YOLO mode or pre-approve commands.", + originalType: "confirm_shell_commands", }; - case 'confirm_action': + case "confirm_action": return { - type: 'unsupported', + type: "unsupported", reason: - 'Action confirmation is not supported in non-interactive mode. Commands requiring confirmation cannot be executed.', - originalType: 'confirm_action', + "Action confirmation is not supported in non-interactive mode. Commands requiring confirmation cannot be executed.", + originalType: "confirm_action", }; default: { // Exhaustiveness check const _exhaustive: never = result; return { - type: 'unsupported', + type: "unsupported", reason: `Unknown command result type: ${(_exhaustive as SlashCommandActionReturn).type}`, - originalType: 'unknown', + originalType: "unknown", }; } } @@ -231,23 +219,17 @@ export const handleSlashCommand = async ( abortController: AbortController, config: Config, settings: LoadedSettings, - allowedBuiltinCommandNames: string[] = [ - ...ALLOWED_BUILTIN_COMMANDS_NON_INTERACTIVE, - ], + allowedBuiltinCommandNames: string[] = [...ALLOWED_BUILTIN_COMMANDS_NON_INTERACTIVE], ): Promise => { const trimmed = rawQuery.trim(); - if (!trimmed.startsWith('/')) { - return { type: 'no_command' }; + if (!trimmed.startsWith("/")) { + return { type: "no_command" }; } const isAcpMode = config.getExperimentalZedIntegration(); const isInteractive = config.isInteractive(); - const executionMode = isAcpMode - ? 'acp' - : isInteractive - ? 'interactive' - : 'non_interactive'; + const executionMode = isAcpMode ? "acp" : isInteractive ? "interactive" : "non_interactive"; const allowedBuiltinSet = new Set(allowedBuiltinCommandNames ?? []); @@ -258,46 +240,33 @@ export const handleSlashCommand = async ( new FileCommandLoader(config), ]; - const commandService = await CommandService.create( - allLoaders, - abortController.signal, - ); + const commandService = await CommandService.create(allLoaders, abortController.signal); const allCommands = commandService.getCommands(); - const filteredCommands = filterCommandsForNonInteractive( - allCommands, - allowedBuiltinSet, - ); + const filteredCommands = filterCommandsForNonInteractive(allCommands, allowedBuiltinSet); // First, try to parse with filtered commands - const { commandToExecute, args } = parseSlashCommand( - rawQuery, - filteredCommands, - ); + const { commandToExecute, args } = parseSlashCommand(rawQuery, filteredCommands); if (!commandToExecute) { // Check if this is a known command that's just not allowed - const { commandToExecute: knownCommand } = parseSlashCommand( - rawQuery, - allCommands, - ); + const { commandToExecute: knownCommand } = parseSlashCommand(rawQuery, allCommands); if (knownCommand) { // Command exists but is not allowed in non-interactive mode return { - type: 'unsupported', - reason: t( - 'The command "/{{command}}" is not supported in non-interactive mode.', - { command: knownCommand.name }, - ), - originalType: 'filtered_command', + type: "unsupported", + reason: t('The command "/{{command}}" is not supported in non-interactive mode.', { + command: knownCommand.name, + }), + originalType: "filtered_command", }; } - return { type: 'no_command' }; + return { type: "no_command" }; } if (!commandToExecute.action) { - return { type: 'no_command' }; + return { type: "no_command" }; } // Not used by custom commands but may be in the future. @@ -309,7 +278,7 @@ export const handleSlashCommand = async ( promptCount: 1, }; - const logger = new Logger(config?.getSessionId() || '', config?.storage); + const logger = new Logger(config?.getSessionId() || "", config?.storage); const context: CommandContext = { executionMode, @@ -336,9 +305,9 @@ export const handleSlashCommand = async ( if (!result) { // Command executed but returned no result (e.g., void return) return { - type: 'message', - messageType: 'info', - content: 'Command executed successfully.', + type: "message", + messageType: "info", + content: "Command executed successfully.", }; } @@ -359,9 +328,7 @@ export const handleSlashCommand = async ( export const getAvailableCommands = async ( config: Config, abortSignal: AbortSignal, - allowedBuiltinCommandNames: string[] = [ - ...ALLOWED_BUILTIN_COMMANDS_NON_INTERACTIVE, - ], + allowedBuiltinCommandNames: string[] = [...ALLOWED_BUILTIN_COMMANDS_NON_INTERACTIVE], ): Promise => { try { const allowedBuiltinSet = new Set(allowedBuiltinCommandNames ?? []); @@ -378,16 +345,13 @@ export const getAvailableCommands = async ( const commandService = await CommandService.create(loaders, abortSignal); const commands = commandService.getCommands(); - const filteredCommands = filterCommandsForNonInteractive( - commands, - allowedBuiltinSet, - ); + const filteredCommands = filterCommandsForNonInteractive(commands, allowedBuiltinSet); // Filter out hidden commands return filteredCommands.filter((cmd) => !cmd.hidden); } catch (error) { // Handle errors gracefully - log and return empty array - debugLogger.error('Error loading available commands:', error); + debugLogger.error("Error loading available commands:", error); return []; } }; diff --git a/apps/airiscode-cli/src/services/BuiltinCommandLoader.ts b/apps/airiscode-cli/src/services/BuiltinCommandLoader.ts index 78c143ef9..676c9ba79 100644 --- a/apps/airiscode-cli/src/services/BuiltinCommandLoader.ts +++ b/apps/airiscode-cli/src/services/BuiltinCommandLoader.ts @@ -4,53 +4,53 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { ICommandLoader } from './types.js'; -import type { SlashCommand } from '../ui/commands/types.js'; -import type { Config } from '@airiscode/core'; -import { aboutCommand } from '../ui/commands/aboutCommand.js'; -import { agentsCommand } from '../ui/commands/agentsCommand.js'; -import { arenaCommand } from '../ui/commands/arenaCommand.js'; -import { approvalModeCommand } from '../ui/commands/approvalModeCommand.js'; -import { authCommand } from '../ui/commands/authCommand.js'; -import { btwCommand } from '../ui/commands/btwCommand.js'; -import { bugCommand } from '../ui/commands/bugCommand.js'; -import { clearCommand } from '../ui/commands/clearCommand.js'; -import { compressCommand } from '../ui/commands/compressCommand.js'; -import { contextCommand } from '../ui/commands/contextCommand.js'; -import { copyCommand } from '../ui/commands/copyCommand.js'; -import { docsCommand } from '../ui/commands/docsCommand.js'; -import { directoryCommand } from '../ui/commands/directoryCommand.js'; -import { editorCommand } from '../ui/commands/editorCommand.js'; -import { exportCommand } from '../ui/commands/exportCommand.js'; -import { extensionsCommand } from '../ui/commands/extensionsCommand.js'; -import { helpCommand } from '../ui/commands/helpCommand.js'; -import { hooksCommand } from '../ui/commands/hooksCommand.js'; -import { ideCommand } from '../ui/commands/ideCommand.js'; -import { createDebugLogger } from '@airiscode/core'; -import { initCommand } from '../ui/commands/initCommand.js'; -import { languageCommand } from '../ui/commands/languageCommand.js'; -import { mcpCommand } from '../ui/commands/mcpCommand.js'; -import { memoryCommand } from '../ui/commands/memoryCommand.js'; -import { modelCommand } from '../ui/commands/modelCommand.js'; -import { planCommand } from '../ui/commands/planCommand.js'; -import { permissionsCommand } from '../ui/commands/permissionsCommand.js'; -import { trustCommand } from '../ui/commands/trustCommand.js'; -import { quitCommand } from '../ui/commands/quitCommand.js'; -import { restoreCommand } from '../ui/commands/restoreCommand.js'; -import { resumeCommand } from '../ui/commands/resumeCommand.js'; -import { settingsCommand } from '../ui/commands/settingsCommand.js'; -import { skillsCommand } from '../ui/commands/skillsCommand.js'; -import { statsCommand } from '../ui/commands/statsCommand.js'; -import { summaryCommand } from '../ui/commands/summaryCommand.js'; -import { terminalSetupCommand } from '../ui/commands/terminalSetupCommand.js'; -import { themeCommand } from '../ui/commands/themeCommand.js'; -import { toolsCommand } from '../ui/commands/toolsCommand.js'; -import { vimCommand } from '../ui/commands/vimCommand.js'; -import { setupGithubCommand } from '../ui/commands/setupGithubCommand.js'; -import { insightCommand } from '../ui/commands/insightCommand.js'; -import { statuslineCommand } from '../ui/commands/statuslineCommand.js'; +import type { Config } from "@airiscode/core"; +import { createDebugLogger } from "@airiscode/core"; +import { aboutCommand } from "../ui/commands/aboutCommand.js"; +import { agentsCommand } from "../ui/commands/agentsCommand.js"; +import { approvalModeCommand } from "../ui/commands/approvalModeCommand.js"; +import { arenaCommand } from "../ui/commands/arenaCommand.js"; +import { authCommand } from "../ui/commands/authCommand.js"; +import { btwCommand } from "../ui/commands/btwCommand.js"; +import { bugCommand } from "../ui/commands/bugCommand.js"; +import { clearCommand } from "../ui/commands/clearCommand.js"; +import { compressCommand } from "../ui/commands/compressCommand.js"; +import { contextCommand } from "../ui/commands/contextCommand.js"; +import { copyCommand } from "../ui/commands/copyCommand.js"; +import { directoryCommand } from "../ui/commands/directoryCommand.js"; +import { docsCommand } from "../ui/commands/docsCommand.js"; +import { editorCommand } from "../ui/commands/editorCommand.js"; +import { exportCommand } from "../ui/commands/exportCommand.js"; +import { extensionsCommand } from "../ui/commands/extensionsCommand.js"; +import { helpCommand } from "../ui/commands/helpCommand.js"; +import { hooksCommand } from "../ui/commands/hooksCommand.js"; +import { ideCommand } from "../ui/commands/ideCommand.js"; +import { initCommand } from "../ui/commands/initCommand.js"; +import { insightCommand } from "../ui/commands/insightCommand.js"; +import { languageCommand } from "../ui/commands/languageCommand.js"; +import { mcpCommand } from "../ui/commands/mcpCommand.js"; +import { memoryCommand } from "../ui/commands/memoryCommand.js"; +import { modelCommand } from "../ui/commands/modelCommand.js"; +import { permissionsCommand } from "../ui/commands/permissionsCommand.js"; +import { planCommand } from "../ui/commands/planCommand.js"; +import { quitCommand } from "../ui/commands/quitCommand.js"; +import { restoreCommand } from "../ui/commands/restoreCommand.js"; +import { resumeCommand } from "../ui/commands/resumeCommand.js"; +import { settingsCommand } from "../ui/commands/settingsCommand.js"; +import { setupGithubCommand } from "../ui/commands/setupGithubCommand.js"; +import { skillsCommand } from "../ui/commands/skillsCommand.js"; +import { statsCommand } from "../ui/commands/statsCommand.js"; +import { statuslineCommand } from "../ui/commands/statuslineCommand.js"; +import { summaryCommand } from "../ui/commands/summaryCommand.js"; +import { terminalSetupCommand } from "../ui/commands/terminalSetupCommand.js"; +import { themeCommand } from "../ui/commands/themeCommand.js"; +import { toolsCommand } from "../ui/commands/toolsCommand.js"; +import { trustCommand } from "../ui/commands/trustCommand.js"; +import type { SlashCommand } from "../ui/commands/types.js"; +import { vimCommand } from "../ui/commands/vimCommand.js"; +import type { ICommandLoader } from "./types.js"; -const builtinDebugLogger = createDebugLogger('BUILTIN_COMMAND_LOADER'); +const builtinDebugLogger = createDebugLogger("BUILTIN_COMMAND_LOADER"); /** * Loads the core, hard-coded slash commands that are an integral part @@ -75,7 +75,7 @@ export class BuiltinCommandLoader implements ICommandLoader { resolvedIdeCommand = await ideCommand(); } catch (error) { builtinDebugLogger.warn( - 'Failed to load IDE command:', + "Failed to load IDE command:", error instanceof Error ? error.message : String(error), ); } diff --git a/apps/airiscode-cli/src/services/BundledSkillLoader.ts b/apps/airiscode-cli/src/services/BundledSkillLoader.ts index c0673f032..06848f8bd 100644 --- a/apps/airiscode-cli/src/services/BundledSkillLoader.ts +++ b/apps/airiscode-cli/src/services/BundledSkillLoader.ts @@ -4,19 +4,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { Config } from '@airiscode/core'; -import { - createDebugLogger, - appendToLastTextPart, -} from '@airiscode/core'; -import type { ICommandLoader } from './types.js'; -import type { - SlashCommand, - SlashCommandActionReturn, -} from '../ui/commands/types.js'; -import { CommandKind } from '../ui/commands/types.js'; +import type { Config } from "@airiscode/core"; +import { appendToLastTextPart, createDebugLogger } from "@airiscode/core"; +import type { SlashCommand, SlashCommandActionReturn } from "../ui/commands/types.js"; +import { CommandKind } from "../ui/commands/types.js"; +import type { ICommandLoader } from "./types.js"; -const debugLogger = createDebugLogger('BUNDLED_SKILL_LOADER'); +const debugLogger = createDebugLogger("BUNDLED_SKILL_LOADER"); /** * Loads bundled skills as slash commands, making them directly invocable @@ -28,31 +22,24 @@ export class BundledSkillLoader implements ICommandLoader { async loadCommands(_signal: AbortSignal): Promise { const skillManager = this.config?.getSkillManager(); if (!skillManager) { - debugLogger.debug('SkillManager not available, skipping bundled skills'); + debugLogger.debug("SkillManager not available, skipping bundled skills"); return []; } try { - const allSkills = await skillManager.listSkills({ level: 'bundled' }); + const allSkills = await skillManager.listSkills({ level: "bundled" }); // Hide skills whose allowedTools require cron when cron is disabled const cronEnabled = this.config?.isCronEnabled() ?? false; const skills = allSkills.filter((skill) => { - if ( - !cronEnabled && - skill.allowedTools?.some((t) => t.startsWith('cron_')) - ) { - debugLogger.debug( - `Hiding skill "${skill.name}" because cron is not enabled`, - ); + if (!cronEnabled && skill.allowedTools?.some((t) => t.startsWith("cron_"))) { + debugLogger.debug(`Hiding skill "${skill.name}" because cron is not enabled`); return false; } return true; }); - debugLogger.debug( - `Loaded ${skills.length} bundled skill(s) as slash commands`, - ); + debugLogger.debug(`Loaded ${skills.length} bundled skill(s) as slash commands`); return skills.map((skill) => ({ name: skill.name, @@ -61,10 +48,10 @@ export class BundledSkillLoader implements ICommandLoader { action: async (context, _args): Promise => { // Resolve template variables in skill body let body = skill.body; - const modelId = this.config?.getModel()?.trim() || ''; - if (body.includes('{{model}}') || body.includes('YOUR_MODEL_ID')) { - body = body.replaceAll('{{model}}', modelId); - body = body.replaceAll('YOUR_MODEL_ID', modelId); + const modelId = this.config?.getModel()?.trim() || ""; + if (body.includes("{{model}}") || body.includes("YOUR_MODEL_ID")) { + body = body.replaceAll("{{model}}", modelId); + body = body.replaceAll("YOUR_MODEL_ID", modelId); // Prepend model identity as a top-level declaration so the LLM // cannot miss it even if it doesn't copy the template exactly. if (modelId) { @@ -77,13 +64,13 @@ export class BundledSkillLoader implements ICommandLoader { : [{ text: body }]; return { - type: 'submit_prompt', + type: "submit_prompt", content, }; }, })); } catch (error) { - debugLogger.error('Failed to load bundled skills:', error); + debugLogger.error("Failed to load bundled skills:", error); return []; } } diff --git a/apps/airiscode-cli/src/services/CommandService.ts b/apps/airiscode-cli/src/services/CommandService.ts index 349e63b9f..136790ec8 100644 --- a/apps/airiscode-cli/src/services/CommandService.ts +++ b/apps/airiscode-cli/src/services/CommandService.ts @@ -4,11 +4,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { SlashCommand } from '../ui/commands/types.js'; -import type { ICommandLoader } from './types.js'; -import { createDebugLogger } from '@airiscode/core'; +import { createDebugLogger } from "@airiscode/core"; +import type { SlashCommand } from "../ui/commands/types.js"; +import type { ICommandLoader } from "./types.js"; -const debugLogger = createDebugLogger('CLI_COMMANDS'); +const debugLogger = createDebugLogger("CLI_COMMANDS"); /** * Orchestrates the discovery and loading of all slash commands for the CLI. @@ -47,20 +47,15 @@ export class CommandService { * @param signal An AbortSignal to cancel the loading process. * @returns A promise that resolves to a new, fully initialized `CommandService` instance. */ - static async create( - loaders: ICommandLoader[], - signal: AbortSignal, - ): Promise { - const results = await Promise.allSettled( - loaders.map((loader) => loader.loadCommands(signal)), - ); + static async create(loaders: ICommandLoader[], signal: AbortSignal): Promise { + const results = await Promise.allSettled(loaders.map((loader) => loader.loadCommands(signal))); const allCommands: SlashCommand[] = []; for (const result of results) { - if (result.status === 'fulfilled') { + if (result.status === "fulfilled") { allCommands.push(...result.value); } else { - debugLogger.debug('A command loader failed:', result.reason); + debugLogger.debug("A command loader failed:", result.reason); } } diff --git a/apps/airiscode-cli/src/services/FileCommandLoader.ts b/apps/airiscode-cli/src/services/FileCommandLoader.ts index 5bcd82717..9415f707a 100644 --- a/apps/airiscode-cli/src/services/FileCommandLoader.ts +++ b/apps/airiscode-cli/src/services/FileCommandLoader.ts @@ -4,35 +4,25 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { promises as fs } from 'node:fs'; -import * as fsSync from 'node:fs'; -import path from 'node:path'; -import toml from '@iarna/toml'; -import { glob } from 'glob'; -import { z } from 'zod'; -import type { Config } from '@airiscode/core'; -import { - createDebugLogger, - EXTENSIONS_CONFIG_FILENAME, - Storage, -} from '@airiscode/core'; -import type { ICommandLoader } from './types.js'; -import { - parseMarkdownCommand, - MarkdownCommandDefSchema, -} from './markdown-command-parser.js'; -import { - createSlashCommandFromDefinition, - type CommandDefinition, -} from './command-factory.js'; -import type { SlashCommand } from '../ui/commands/types.js'; +import * as fsSync from "node:fs"; +import { promises as fs } from "node:fs"; +import path from "node:path"; +import type { Config } from "@airiscode/core"; +import { createDebugLogger, EXTENSIONS_CONFIG_FILENAME, Storage } from "@airiscode/core"; +import toml from "@iarna/toml"; +import { glob } from "glob"; +import { z } from "zod"; +import type { SlashCommand } from "../ui/commands/types.js"; +import { type CommandDefinition, createSlashCommandFromDefinition } from "./command-factory.js"; +import { MarkdownCommandDefSchema, parseMarkdownCommand } from "./markdown-command-parser.js"; +import type { ICommandLoader } from "./types.js"; interface CommandDirectory { path: string; extensionName?: string; } -const debugLogger = createDebugLogger('FILE_COMMAND_LOADER'); +const debugLogger = createDebugLogger("FILE_COMMAND_LOADER"); /** * Defines the Zod schema for a command definition file. This serves as the @@ -92,11 +82,11 @@ export class FileCommandLoader implements ICommandLoader { for (const dirInfo of commandDirs) { try { // Scan both .toml and .md files - const tomlFiles = await glob('**/*.toml', { + const tomlFiles = await glob("**/*.toml", { ...globOptions, cwd: dirInfo.path, }); - const mdFiles = await glob('**/*.md', { + const mdFiles = await glob("**/*.md", { ...globOptions, cwd: dirInfo.path, }); @@ -123,17 +113,16 @@ export class FileCommandLoader implements ICommandLoader { ), ); - const commands = ( - await Promise.all([...tomlCommandPromises, ...mdCommandPromises]) - ).filter((cmd): cmd is SlashCommand => cmd !== null); + const commands = (await Promise.all([...tomlCommandPromises, ...mdCommandPromises])).filter( + (cmd): cmd is SlashCommand => cmd !== null, + ); // Add all commands without deduplication allCommands.push(...commands); } catch (error) { // Ignore ENOENT (directory doesn't exist) and AbortError (operation was cancelled) - const isEnoent = (error as NodeJS.ErrnoException).code === 'ENOENT'; - const isAbortError = - error instanceof Error && error.name === 'AbortError'; + const isEnoent = (error as NodeJS.ErrnoException).code === "ENOENT"; + const isAbortError = error instanceof Error && error.name === "AbortError"; if (!isEnoent && !isAbortError) { debugLogger.error( `[FileCommandLoader] Error loading commands from ${dirInfo.path}:`, @@ -190,15 +179,12 @@ export class FileCommandLoader implements ICommandLoader { * Get commands paths from an extension. * Returns paths from config.commands if specified, otherwise defaults to 'commands' directory. */ - private getExtensionCommandsPaths(ext: { - path: string; - name: string; - }): string[] { + private getExtensionCommandsPaths(ext: { path: string; name: string }): string[] { // Try to get extension config try { const configPath = path.join(ext.path, EXTENSIONS_CONFIG_FILENAME); if (fsSync.existsSync(configPath)) { - const configContent = fsSync.readFileSync(configPath, 'utf-8'); + const configContent = fsSync.readFileSync(configPath, "utf-8"); const config = JSON.parse(configContent); if (config.commands) { @@ -220,14 +206,11 @@ export class FileCommandLoader implements ICommandLoader { } } } catch (error) { - debugLogger.warn( - `Failed to read extension config for ${ext.name}:`, - error, - ); + debugLogger.warn(`Failed to read extension config for ${ext.name}:`, error); } // Default fallback: use 'commands' directory - const defaultPath = path.join(ext.path, 'commands'); + const defaultPath = path.join(ext.path, "commands"); try { if (fsSync.existsSync(defaultPath)) { return [defaultPath]; @@ -253,7 +236,7 @@ export class FileCommandLoader implements ICommandLoader { ): Promise { let fileContent: string; try { - fileContent = await fs.readFile(filePath, 'utf-8'); + fileContent = await fs.readFile(filePath, "utf-8"); } catch (error: unknown) { debugLogger.error( `[FileCommandLoader] Failed to read file ${filePath}:`, @@ -291,7 +274,7 @@ export class FileCommandLoader implements ICommandLoader { baseDir, validDef as any, extensionName, - '.toml', + ".toml", ); } @@ -309,7 +292,7 @@ export class FileCommandLoader implements ICommandLoader { ): Promise { let fileContent: string; try { - fileContent = await fs.readFile(filePath, 'utf-8'); + fileContent = await fs.readFile(filePath, "utf-8"); } catch (error: unknown) { debugLogger.error( `[FileCommandLoader] Failed to read file ${filePath}:`, @@ -345,19 +328,12 @@ export class FileCommandLoader implements ICommandLoader { const definition: CommandDefinition = { prompt: validDef.prompt, description: - validDef.frontmatter?.description && - typeof validDef.frontmatter.description === 'string' + validDef.frontmatter?.description && typeof validDef.frontmatter.description === "string" ? validDef.frontmatter.description : undefined, }; // Use factory to create command - return createSlashCommandFromDefinition( - filePath, - baseDir, - definition, - extensionName, - '.md', - ); + return createSlashCommandFromDefinition(filePath, baseDir, definition, extensionName, ".md"); } } diff --git a/apps/airiscode-cli/src/services/McpPromptLoader.ts b/apps/airiscode-cli/src/services/McpPromptLoader.ts index 71f10737e..2ad2c8f2f 100644 --- a/apps/airiscode-cli/src/services/McpPromptLoader.ts +++ b/apps/airiscode-cli/src/services/McpPromptLoader.ts @@ -4,19 +4,16 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { Config } from '@airiscode/core'; -import { - getErrorMessage, - getMCPServerPrompts, -} from '@airiscode/core'; +import type { Config } from "@airiscode/core"; +import { getErrorMessage, getMCPServerPrompts } from "@airiscode/core"; +import type { PromptArgument } from "@modelcontextprotocol/sdk/types.js"; import type { CommandContext, SlashCommand, SlashCommandActionReturn, -} from '../ui/commands/types.js'; -import { CommandKind } from '../ui/commands/types.js'; -import type { ICommandLoader } from './types.js'; -import type { PromptArgument } from '@modelcontextprotocol/sdk/types.js'; +} from "../ui/commands/types.js"; +import { CommandKind } from "../ui/commands/types.js"; +import type { ICommandLoader } from "./types.js"; /** * Discovers and loads executable slash commands from prompts exposed by @@ -48,14 +45,14 @@ export class McpPromptLoader implements ICommandLoader { kind: CommandKind.MCP_PROMPT, subCommands: [ { - name: 'help', - description: 'Show help for this prompt', + name: "help", + description: "Show help for this prompt", kind: CommandKind.MCP_PROMPT, action: async (): Promise => { if (!prompt.arguments || prompt.arguments.length === 0) { return { - type: 'message', - messageType: 'info', + type: "message", + messageType: "info", content: `Prompt "${prompt.name}" has no arguments.`, }; } @@ -70,13 +67,11 @@ export class McpPromptLoader implements ICommandLoader { if (arg.description) { helpMessage += ` ${arg.description}\n`; } - helpMessage += ` (required: ${ - arg.required ? 'yes' : 'no' - })\n\n`; + helpMessage += ` (required: ${arg.required ? "yes" : "no"})\n\n`; } return { - type: 'message', - messageType: 'info', + type: "message", + messageType: "info", content: helpMessage, }; }, @@ -88,17 +83,17 @@ export class McpPromptLoader implements ICommandLoader { ): Promise => { if (!this.config) { return { - type: 'message', - messageType: 'error', - content: 'Config not loaded.', + type: "message", + messageType: "error", + content: "Config not loaded.", }; } const promptInputs = this.parseArgs(args, prompt.arguments); if (promptInputs instanceof Error) { return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: promptInputs.message, }; } @@ -108,61 +103,54 @@ export class McpPromptLoader implements ICommandLoader { const mcpServerConfig = mcpServers[serverName]; if (!mcpServerConfig) { return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: `MCP server config not found for '${serverName}'.`, }; } const result = await prompt.invoke(promptInputs); - if (result['error']) { + if (result["error"]) { return { - type: 'message', - messageType: 'error', - content: `Error invoking prompt: ${result['error']}`, + type: "message", + messageType: "error", + content: `Error invoking prompt: ${result["error"]}`, }; } const firstMessage = result.messages?.[0]; const content = firstMessage?.content; - if (content?.type !== 'text') { + if (content?.type !== "text") { return { - type: 'message', - messageType: 'error', - content: - 'Received an empty or invalid prompt response from the server.', + type: "message", + messageType: "error", + content: "Received an empty or invalid prompt response from the server.", }; } return { - type: 'submit_prompt', + type: "submit_prompt", content: JSON.stringify(content.text), }; } catch (error) { return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: `Error: ${getErrorMessage(error)}`, }; } }, - completion: async ( - commandContext: CommandContext, - partialArg: string, - ) => { + completion: async (commandContext: CommandContext, partialArg: string) => { const invocation = commandContext.invocation; if (!prompt || !prompt.arguments || !invocation) { return []; } - const indexOfFirstSpace = invocation.raw.indexOf(' ') + 1; + const indexOfFirstSpace = invocation.raw.indexOf(" ") + 1; let promptInputs = indexOfFirstSpace === 0 ? {} - : this.parseArgs( - invocation.raw.substring(indexOfFirstSpace), - prompt.arguments, - ); + : this.parseArgs(invocation.raw.substring(indexOfFirstSpace), prompt.arguments); if (promptInputs instanceof Error) { promptInputs = {}; } @@ -243,7 +231,7 @@ export class McpPromptLoader implements ICommandLoader { while ((match = namedArgRegex.exec(userArgs)) !== null) { const key = match[1]; // Extract the quoted or unquoted argument and remove escape chars. - const value = (match[2] ?? match[3]).replace(/\\(.)/g, '$1'); + const value = (match[2] ?? match[3]).replace(/\\(.)/g, "$1"); argValues[key] = value; // Capture text between matches as potential positional args if (match.index > lastIndex) { @@ -257,13 +245,13 @@ export class McpPromptLoader implements ICommandLoader { positionalParts.push(userArgs.substring(lastIndex)); } - const positionalArgsString = positionalParts.join('').trim(); + const positionalArgsString = positionalParts.join("").trim(); // extracts either quoted strings or non-quoted sequences of non-space characters. const positionalArgRegex = /(?:"((?:\\.|[^"\\])*)"|([^ ]+))/g; const positionalArgs: string[] = []; while ((match = positionalArgRegex.exec(positionalArgsString)) !== null) { // Extract the quoted or unquoted argument and remove escape chars. - positionalArgs.push((match[1] ?? match[2]).replace(/\\(.)/g, '$1')); + positionalArgs.push((match[1] ?? match[2]).replace(/\\(.)/g, "$1")); } if (!promptArgs) { @@ -275,14 +263,12 @@ export class McpPromptLoader implements ICommandLoader { } } - const unfilledArgs = promptArgs.filter( - (arg) => arg.required && !promptInputs[arg.name], - ); + const unfilledArgs = promptArgs.filter((arg) => arg.required && !promptInputs[arg.name]); if (unfilledArgs.length === 1) { // If we have only one unfilled arg, we don't require quotes we just // join all the given arguments together as if they were quoted. - promptInputs[unfilledArgs[0].name] = positionalArgs.join(' '); + promptInputs[unfilledArgs[0].name] = positionalArgs.join(" "); } else { const missingArgs: string[] = []; for (let i = 0; i < unfilledArgs.length; i++) { @@ -293,9 +279,7 @@ export class McpPromptLoader implements ICommandLoader { } } if (missingArgs.length > 0) { - const missingArgNames = missingArgs - .map((name) => `--${name}`) - .join(', '); + const missingArgNames = missingArgs.map((name) => `--${name}`).join(", "); return new Error(`Missing required argument(s): ${missingArgNames}`); } } diff --git a/apps/airiscode-cli/src/services/command-factory.ts b/apps/airiscode-cli/src/services/command-factory.ts index 70c52650a..9e4298a3c 100644 --- a/apps/airiscode-cli/src/services/command-factory.ts +++ b/apps/airiscode-cli/src/services/command-factory.ts @@ -9,36 +9,30 @@ * objects from parsed command definitions (TOML or Markdown). */ -import path from 'node:path'; -import { createDebugLogger } from '@airiscode/core'; +import path from "node:path"; +import { createDebugLogger } from "@airiscode/core"; import type { CommandContext, SlashCommand, SlashCommandActionReturn, -} from '../ui/commands/types.js'; -import { CommandKind } from '../ui/commands/types.js'; -import { DefaultArgumentProcessor } from './prompt-processors/argumentProcessor.js'; -import type { - IPromptProcessor, - PromptPipelineContent, -} from './prompt-processors/types.js'; +} from "../ui/commands/types.js"; +import { CommandKind } from "../ui/commands/types.js"; +import { DefaultArgumentProcessor } from "./prompt-processors/argumentProcessor.js"; +import { AtFileProcessor } from "./prompt-processors/atFileProcessor.js"; +import { ConfirmationRequiredError, ShellProcessor } from "./prompt-processors/shellProcessor.js"; +import type { IPromptProcessor, PromptPipelineContent } from "./prompt-processors/types.js"; import { - SHORTHAND_ARGS_PLACEHOLDER, - SHELL_INJECTION_TRIGGER, AT_FILE_INJECTION_TRIGGER, -} from './prompt-processors/types.js'; -import { - ConfirmationRequiredError, - ShellProcessor, -} from './prompt-processors/shellProcessor.js'; -import { AtFileProcessor } from './prompt-processors/atFileProcessor.js'; + SHELL_INJECTION_TRIGGER, + SHORTHAND_ARGS_PLACEHOLDER, +} from "./prompt-processors/types.js"; export interface CommandDefinition { prompt: string; description?: string; } -const debugLogger = createDebugLogger('COMMAND_FACTORY'); +const debugLogger = createDebugLogger("COMMAND_FACTORY"); /** * Creates a SlashCommand from a parsed command definition. @@ -68,8 +62,8 @@ export function createSlashCommandFromDefinition( // Sanitize each path segment to prevent ambiguity. Since ':' is our // namespace separator, we replace any literal colons in filenames // with underscores to avoid naming conflicts. - .map((segment) => segment.replaceAll(':', '_')) - .join(':'); + .map((segment) => segment.replaceAll(":", "_")) + .join(":"); // Add extension name tag for extension commands const defaultDescription = `Custom command from ${path.basename(filePath)}`; @@ -80,12 +74,8 @@ export function createSlashCommandFromDefinition( const processors: IPromptProcessor[] = []; const usesArgs = definition.prompt.includes(SHORTHAND_ARGS_PLACEHOLDER); - const usesShellInjection = definition.prompt.includes( - SHELL_INJECTION_TRIGGER, - ); - const usesAtFileInjection = definition.prompt.includes( - AT_FILE_INJECTION_TRIGGER, - ); + const usesShellInjection = definition.prompt.includes(SHELL_INJECTION_TRIGGER); + const usesAtFileInjection = definition.prompt.includes(AT_FILE_INJECTION_TRIGGER); // 1. @-File Injection (Security First). // This runs first to ensure we're not executing shell commands that @@ -111,30 +101,25 @@ export function createSlashCommandFromDefinition( description, kind: CommandKind.FILE, extensionName, - action: async ( - context: CommandContext, - _args: string, - ): Promise => { + action: async (context: CommandContext, _args: string): Promise => { if (!context.invocation) { debugLogger.error( `[FileCommandLoader] Critical error: Command '${baseCommandName}' was executed without invocation context.`, ); return { - type: 'submit_prompt', + type: "submit_prompt", content: [{ text: definition.prompt }], // Fallback to unprocessed prompt }; } try { - let processedContent: PromptPipelineContent = [ - { text: definition.prompt }, - ]; + let processedContent: PromptPipelineContent = [{ text: definition.prompt }]; for (const processor of processors) { processedContent = await processor.process(processedContent, context); } return { - type: 'submit_prompt', + type: "submit_prompt", content: processedContent, }; } catch (e) { @@ -142,7 +127,7 @@ export function createSlashCommandFromDefinition( if (e instanceof ConfirmationRequiredError) { // Halt and request confirmation from the UI layer. return { - type: 'confirm_shell_commands', + type: "confirm_shell_commands", commandsToConfirm: e.commandsToConfirm, originalInvocation: { raw: context.invocation.raw, diff --git a/apps/airiscode-cli/src/services/command-migration-tool.ts b/apps/airiscode-cli/src/services/command-migration-tool.ts index 706b9575e..df786a1f0 100644 --- a/apps/airiscode-cli/src/services/command-migration-tool.ts +++ b/apps/airiscode-cli/src/services/command-migration-tool.ts @@ -8,11 +8,11 @@ * Tool for migrating TOML commands to Markdown format. */ -import { promises as fs } from 'node:fs'; -import path from 'node:path'; -import { glob } from 'glob'; -import { convertTomlToMarkdown } from '@airiscode/core'; -import { t } from '../i18n/index.js'; +import { promises as fs } from "node:fs"; +import path from "node:path"; +import { convertTomlToMarkdown } from "@airiscode/core"; +import { glob } from "glob"; +import { t } from "../i18n/index.js"; export interface MigrationResult { success: boolean; @@ -34,9 +34,7 @@ export interface MigrationOptions { * @param commandDir Directory to scan * @returns Array of TOML file paths (relative to commandDir) */ -export async function detectTomlCommands( - commandDir: string, -): Promise { +export async function detectTomlCommands(commandDir: string): Promise { try { await fs.access(commandDir); } catch { @@ -44,7 +42,7 @@ export async function detectTomlCommands( return []; } - const tomlFiles = await glob('**/*.toml', { + const tomlFiles = await glob("**/*.toml", { cwd: commandDir, nodir: true, dot: false, @@ -58,9 +56,7 @@ export async function detectTomlCommands( * @param options Migration options * @returns Migration result with details */ -export async function migrateTomlCommands( - options: MigrationOptions, -): Promise { +export async function migrateTomlCommands(options: MigrationOptions): Promise { const { commandDir, createBackup = true, deleteOriginal = false } = options; const result: MigrationResult = { @@ -82,31 +78,31 @@ export async function migrateTomlCommands( try { // Read TOML file - const tomlContent = await fs.readFile(tomlPath, 'utf-8'); + const tomlContent = await fs.readFile(tomlPath, "utf-8"); // Convert to Markdown const markdownContent = convertTomlToMarkdown(tomlContent); // Generate Markdown file path (same location, .md extension) - const markdownPath = tomlPath.replace(/\.toml$/, '.md'); + const markdownPath = tomlPath.replace(/\.toml$/, ".md"); // Check if Markdown file already exists try { await fs.access(markdownPath); throw new Error( - t('Markdown file already exists: {{filename}}', { + t("Markdown file already exists: {{filename}}", { filename: path.basename(markdownPath), }), ); } catch (error) { - if ((error as NodeJS.ErrnoException).code !== 'ENOENT') { + if ((error as NodeJS.ErrnoException).code !== "ENOENT") { throw error; } // File doesn't exist, continue } // Write Markdown file - await fs.writeFile(markdownPath, markdownContent, 'utf-8'); + await fs.writeFile(markdownPath, markdownContent, "utf-8"); // Backup original if requested (rename to .toml.backup) if (createBackup) { @@ -137,37 +133,37 @@ export async function migrateTomlCommands( */ export function generateMigrationPrompt(tomlFiles: string[]): string { if (tomlFiles.length === 0) { - return ''; + return ""; } const count = tomlFiles.length; const moreCount = tomlFiles.length - 3; const fileList = tomlFiles.length <= 5 - ? tomlFiles.map((f) => ` - ${f}`).join('\n') - : ` - ${tomlFiles.slice(0, 3).join('\n - ')}\n - ${t('... and {{count}} more', { count: String(moreCount) })}`; + ? tomlFiles.map((f) => ` - ${f}`).join("\n") + : ` - ${tomlFiles.slice(0, 3).join("\n - ")}\n - ${t("... and {{count}} more", { count: String(moreCount) })}`; return ` -⚠️ ${t('TOML Command Format Deprecation Notice')} +⚠️ ${t("TOML Command Format Deprecation Notice")} -${t('Found {{count}} command file(s) in TOML format:', { count: String(count) })} +${t("Found {{count}} command file(s) in TOML format:", { count: String(count) })} ${fileList} -${t('The TOML format for commands is being deprecated in favor of Markdown format.')} -${t('Markdown format is more readable and easier to edit.')} +${t("The TOML format for commands is being deprecated in favor of Markdown format.")} +${t("Markdown format is more readable and easier to edit.")} -${t('You can migrate these files automatically using:')} +${t("You can migrate these files automatically using:")} airiscode migrate-commands -${t('Or manually convert each file:')} +${t("Or manually convert each file:")} - ${t('TOML: prompt = "..." / description = "..."')} - - ${t('Markdown: YAML frontmatter + content')} + - ${t("Markdown: YAML frontmatter + content")} -${t('The migration tool will:')} - ✓ ${t('Convert TOML files to Markdown')} - ✓ ${t('Create backups of original files')} - ✓ ${t('Preserve all command functionality')} +${t("The migration tool will:")} + ✓ ${t("Convert TOML files to Markdown")} + ✓ ${t("Create backups of original files")} + ✓ ${t("Preserve all command functionality")} -${t('TOML format will continue to work for now, but migration is recommended.')} +${t("TOML format will continue to work for now, but migration is recommended.")} `.trim(); } diff --git a/apps/airiscode-cli/src/services/insight/generators/DataProcessor.ts b/apps/airiscode-cli/src/services/insight/generators/DataProcessor.ts index 6cd5db1b3..49ed6487c 100644 --- a/apps/airiscode-cli/src/services/insight/generators/DataProcessor.ts +++ b/apps/airiscode-cli/src/services/insight/generators/DataProcessor.ts @@ -4,38 +4,36 @@ * SPDX-License-Identifier: Apache-2.0 */ -import fs from 'fs/promises'; -import path from 'path'; import { - read as readJsonlFile, + type ChatRecord, + type Config, createDebugLogger, -} from '@airiscode/core'; -import pLimit from 'p-limit'; -import type { - InsightData, - HeatMapData, - StreakData, - SessionFacets, - InsightProgressCallback, -} from '../types/StaticInsightTypes.js'; + getInsightPrompt, + read as readJsonlFile, +} from "@airiscode/core"; +import fs from "fs/promises"; +import pLimit from "p-limit"; +import path from "path"; import type { - QualitativeInsights, - InsightImpressiveWorkflows, - InsightProjectAreas, - InsightFutureOpportunities, + InsightAtAGlance, InsightFrictionPoints, - InsightMemorableMoment, + InsightFutureOpportunities, + InsightImpressiveWorkflows, InsightImprovements, InsightInteractionStyle, - InsightAtAGlance, -} from '../types/QualitativeInsightTypes.js'; -import { - getInsightPrompt, - type Config, - type ChatRecord, -} from '@airiscode/core'; + InsightMemorableMoment, + InsightProjectAreas, + QualitativeInsights, +} from "../types/QualitativeInsightTypes.js"; +import type { + HeatMapData, + InsightData, + InsightProgressCallback, + SessionFacets, + StreakData, +} from "../types/StaticInsightTypes.js"; -const logger = createDebugLogger('DataProcessor'); +const logger = createDebugLogger("DataProcessor"); const CONCURRENCY_LIMIT = 4; @@ -44,32 +42,28 @@ export class DataProcessor { // Helper function to format date as YYYY-MM-DD private formatDate(date: Date): string { - return date.toISOString().split('T')[0]; + return date.toISOString().split("T")[0]; } // Format chat records for LLM analysis private formatRecordsForAnalysis(records: ChatRecord[]): string { - let output = ''; - const sessionStart = - records.length > 0 ? new Date(records[0].timestamp) : new Date(); + let output = ""; + const sessionStart = records.length > 0 ? new Date(records[0].timestamp) : new Date(); - output += `Session: ${records[0]?.sessionId || 'unknown'}\n`; + output += `Session: ${records[0]?.sessionId || "unknown"}\n`; output += `Date: ${sessionStart.toISOString()}\n`; output += `Duration: ${records.length} turns\n\n`; for (const record of records) { - if (record.type === 'user') { - const text = - record.message?.parts - ?.map((p) => ('text' in p ? p.text : '')) - .join('') || ''; + if (record.type === "user") { + const text = record.message?.parts?.map((p) => ("text" in p ? p.text : "")).join("") || ""; output += `[User]: ${text}\n`; - } else if (record.type === 'assistant') { + } else if (record.type === "assistant") { if (record.message?.parts) { for (const part of record.message.parts) { - if ('text' in part && part.text) { + if ("text" in part && part.text) { output += `[Assistant]: ${part.text}\n`; - } else if ('functionCall' in part) { + } else if ("functionCall" in part) { const call = part.functionCall; if (call) { output += `[Tool: ${call.name}]\n`; @@ -88,9 +82,9 @@ export class DataProcessor { let hasAssistant = false; for (const record of records) { - if (record.type === 'user') { + if (record.type === "user") { hasUser = true; - } else if (record.type === 'assistant') { + } else if (record.type === "assistant") { hasAssistant = true; } @@ -103,103 +97,101 @@ export class DataProcessor { } // Analyze a single session using LLM - private async analyzeSession( - records: ChatRecord[], - ): Promise { + private async analyzeSession(records: ChatRecord[]): Promise { if (records.length === 0) return null; const INSIGHT_SCHEMA = { - type: 'object', + type: "object", properties: { underlying_goal: { - type: 'string', - description: 'What the user fundamentally wanted to achieve', + type: "string", + description: "What the user fundamentally wanted to achieve", }, goal_categories: { - type: 'object', - additionalProperties: { type: 'number' }, + type: "object", + additionalProperties: { type: "number" }, }, outcome: { - type: 'string', + type: "string", enum: [ - 'fully_achieved', - 'mostly_achieved', - 'partially_achieved', - 'not_achieved', - 'unclear_from_transcript', + "fully_achieved", + "mostly_achieved", + "partially_achieved", + "not_achieved", + "unclear_from_transcript", ], }, user_satisfaction_counts: { - type: 'object', - additionalProperties: { type: 'number' }, + type: "object", + additionalProperties: { type: "number" }, }, Qwen_helpfulness: { - type: 'string', + type: "string", enum: [ - 'unhelpful', - 'slightly_helpful', - 'moderately_helpful', - 'very_helpful', - 'essential', + "unhelpful", + "slightly_helpful", + "moderately_helpful", + "very_helpful", + "essential", ], }, session_type: { - type: 'string', + type: "string", enum: [ - 'single_task', - 'multi_task', - 'iterative_refinement', - 'exploration', - 'quick_question', + "single_task", + "multi_task", + "iterative_refinement", + "exploration", + "quick_question", ], }, friction_counts: { - type: 'object', - additionalProperties: { type: 'number' }, + type: "object", + additionalProperties: { type: "number" }, }, friction_detail: { - type: 'string', - description: 'One sentence describing friction or empty', + type: "string", + description: "One sentence describing friction or empty", }, primary_success: { - type: 'string', + type: "string", enum: [ - 'none', - 'fast_accurate_search', - 'correct_code_edits', - 'good_explanations', - 'proactive_help', - 'multi_file_changes', - 'good_debugging', + "none", + "fast_accurate_search", + "correct_code_edits", + "good_explanations", + "proactive_help", + "multi_file_changes", + "good_debugging", ], }, brief_summary: { - type: 'string', - description: 'One sentence: what user wanted and whether they got it', + type: "string", + description: "One sentence: what user wanted and whether they got it", }, }, required: [ - 'underlying_goal', - 'goal_categories', - 'outcome', - 'user_satisfaction_counts', - 'Qwen_helpfulness', - 'session_type', - 'friction_counts', - 'friction_detail', - 'primary_success', - 'brief_summary', + "underlying_goal", + "goal_categories", + "outcome", + "user_satisfaction_counts", + "Qwen_helpfulness", + "session_type", + "friction_counts", + "friction_detail", + "primary_success", + "brief_summary", ], }; const sessionText = this.formatRecordsForAnalysis(records); - const prompt = `${getInsightPrompt('analysis')}\n\nSESSION:\n${sessionText}`; + const prompt = `${getInsightPrompt("analysis")}\n\nSESSION:\n${sessionText}`; try { const result = await this.config.getBaseLlmClient().generateJson({ // Use the configured model model: this.config.getModel(), - contents: [{ role: 'user', parts: [{ text: prompt }] }], + contents: [{ role: "user", parts: [{ text: prompt }] }], schema: INSIGHT_SCHEMA, abortSignal: AbortSignal.timeout(600000), // 10 minute timeout per session }); @@ -213,10 +205,7 @@ export class DataProcessor { session_id: records[0].sessionId, }; } catch (error) { - logger.error( - `Failed to analyze session ${records[0]?.sessionId}:`, - error, - ); + logger.error(`Failed to analyze session ${records[0]?.sessionId}:`, error); return null; } } @@ -284,32 +273,23 @@ export class DataProcessor { facetsOutputDir?: string, onProgress?: InsightProgressCallback, ): Promise { - if (onProgress) onProgress('Scanning chat history...', 0); + if (onProgress) onProgress("Scanning chat history...", 0); const allChatFiles = await this.scanChatFiles(baseDir); - if (onProgress) onProgress('Crunching the numbers', 10); + if (onProgress) onProgress("Crunching the numbers", 10); const metrics = await this.generateMetrics(allChatFiles, onProgress); - if (onProgress) onProgress('Preparing sessions...', 20); - const facets = await this.generateFacets( - allChatFiles, - facetsOutputDir, - onProgress, - ); + if (onProgress) onProgress("Preparing sessions...", 20); + const facets = await this.generateFacets(allChatFiles, facetsOutputDir, onProgress); - if (onProgress) onProgress('Generating personalized insights...', 80); + if (onProgress) onProgress("Generating personalized insights...", 80); const qualitative = await this.generateQualitativeInsights(metrics, facets); // Aggregate satisfaction, friction, success and outcome data from facets - const { - satisfactionAgg, - frictionAgg, - primarySuccessAgg, - outcomesAgg, - goalsAgg, - } = this.aggregateFacetsData(facets); + const { satisfactionAgg, frictionAgg, primarySuccessAgg, outcomesAgg, goalsAgg } = + this.aggregateFacetsData(facets); - if (onProgress) onProgress('Assembling report...', 100); + if (onProgress) onProgress("Assembling report...", 100); return { ...metrics, @@ -348,7 +328,7 @@ export class DataProcessor { }); // Aggregate primary success - if (facet.primary_success && facet.primary_success !== 'none') { + if (facet.primary_success && facet.primary_success !== "none") { primarySuccessAgg[facet.primary_success] = (primarySuccessAgg[facet.primary_success] || 0) + 1; } @@ -374,14 +354,14 @@ export class DataProcessor { } private async generateQualitativeInsights( - metrics: Omit, + metrics: Omit, facets: SessionFacets[], ): Promise { if (facets.length === 0) { return undefined; } - logger.info('Generating qualitative insights...'); + logger.info("Generating qualitative insights..."); const commonData = this.prepareCommonPromptData(metrics, facets); @@ -393,13 +373,13 @@ export class DataProcessor { try { const result = await this.config.getBaseLlmClient().generateJson({ model: this.config.getModel(), - contents: [{ role: 'user', parts: [{ text: prompt }] }], + contents: [{ role: "user", parts: [{ text: prompt }] }], schema, abortSignal: AbortSignal.timeout(600000), }); return result as T; } catch (error) { - logger.error('Failed to generate insight:', error); + logger.error("Failed to generate insight:", error); return undefined; } }; @@ -410,173 +390,163 @@ export class DataProcessor { // 1. Impressive Workflows const schemaImpressiveWorkflows = { - type: 'object', + type: "object", properties: { - intro: { type: 'string' }, + intro: { type: "string" }, impressive_workflows: { - type: 'array', + type: "array", items: { - type: 'object', + type: "object", properties: { - title: { type: 'string' }, - description: { type: 'string' }, + title: { type: "string" }, + description: { type: "string" }, }, - required: ['title', 'description'], + required: ["title", "description"], }, }, }, - required: ['intro', 'impressive_workflows'], + required: ["intro", "impressive_workflows"], }; // 2. Project Areas const schemaProjectAreas = { - type: 'object', + type: "object", properties: { areas: { - type: 'array', + type: "array", items: { - type: 'object', + type: "object", properties: { - name: { type: 'string' }, - session_count: { type: 'number' }, - description: { type: 'string' }, + name: { type: "string" }, + session_count: { type: "number" }, + description: { type: "string" }, }, - required: ['name', 'session_count', 'description'], + required: ["name", "session_count", "description"], }, }, }, - required: ['areas'], + required: ["areas"], }; // 3. Future Opportunities const schemaFutureOpportunities = { - type: 'object', + type: "object", properties: { - intro: { type: 'string' }, + intro: { type: "string" }, opportunities: { - type: 'array', + type: "array", items: { - type: 'object', + type: "object", properties: { - title: { type: 'string' }, - whats_possible: { type: 'string' }, - how_to_try: { type: 'string' }, - copyable_prompt: { type: 'string' }, + title: { type: "string" }, + whats_possible: { type: "string" }, + how_to_try: { type: "string" }, + copyable_prompt: { type: "string" }, }, - required: [ - 'title', - 'whats_possible', - 'how_to_try', - 'copyable_prompt', - ], + required: ["title", "whats_possible", "how_to_try", "copyable_prompt"], }, }, }, - required: ['intro', 'opportunities'], + required: ["intro", "opportunities"], }; // 4. Friction Points const schemaFrictionPoints = { - type: 'object', + type: "object", properties: { - intro: { type: 'string' }, + intro: { type: "string" }, categories: { - type: 'array', + type: "array", items: { - type: 'object', + type: "object", properties: { - category: { type: 'string' }, - description: { type: 'string' }, - examples: { type: 'array', items: { type: 'string' } }, + category: { type: "string" }, + description: { type: "string" }, + examples: { type: "array", items: { type: "string" } }, }, - required: ['category', 'description', 'examples'], + required: ["category", "description", "examples"], }, }, }, - required: ['intro', 'categories'], + required: ["intro", "categories"], }; // 5. Memorable Moment const schemaMemorableMoment = { - type: 'object', + type: "object", properties: { - headline: { type: 'string' }, - detail: { type: 'string' }, + headline: { type: "string" }, + detail: { type: "string" }, }, - required: ['headline', 'detail'], + required: ["headline", "detail"], }; // 6. Improvements const schemaImprovements = { - type: 'object', + type: "object", properties: { Qwen_md_additions: { - type: 'array', + type: "array", items: { - type: 'object', + type: "object", properties: { - addition: { type: 'string' }, - why: { type: 'string' }, - prompt_scaffold: { type: 'string' }, + addition: { type: "string" }, + why: { type: "string" }, + prompt_scaffold: { type: "string" }, }, - required: ['addition', 'why', 'prompt_scaffold'], + required: ["addition", "why", "prompt_scaffold"], }, }, features_to_try: { - type: 'array', + type: "array", items: { - type: 'object', + type: "object", properties: { - feature: { type: 'string' }, - one_liner: { type: 'string' }, - why_for_you: { type: 'string' }, - example_code: { type: 'string' }, + feature: { type: "string" }, + one_liner: { type: "string" }, + why_for_you: { type: "string" }, + example_code: { type: "string" }, }, - required: ['feature', 'one_liner', 'why_for_you', 'example_code'], + required: ["feature", "one_liner", "why_for_you", "example_code"], }, }, usage_patterns: { - type: 'array', + type: "array", items: { - type: 'object', + type: "object", properties: { - title: { type: 'string' }, - suggestion: { type: 'string' }, - detail: { type: 'string' }, - copyable_prompt: { type: 'string' }, + title: { type: "string" }, + suggestion: { type: "string" }, + detail: { type: "string" }, + copyable_prompt: { type: "string" }, }, - required: ['title', 'suggestion', 'detail', 'copyable_prompt'], + required: ["title", "suggestion", "detail", "copyable_prompt"], }, }, }, - required: ['Qwen_md_additions', 'features_to_try', 'usage_patterns'], + required: ["Qwen_md_additions", "features_to_try", "usage_patterns"], }; // 7. Interaction Style const schemaInteractionStyle = { - type: 'object', + type: "object", properties: { - narrative: { type: 'string' }, - key_pattern: { type: 'string' }, + narrative: { type: "string" }, + key_pattern: { type: "string" }, }, - required: ['narrative', 'key_pattern'], + required: ["narrative", "key_pattern"], }; // 8. At A Glance const schemaAtAGlance = { - type: 'object', + type: "object", properties: { - whats_working: { type: 'string' }, - whats_hindering: { type: 'string' }, - quick_wins: { type: 'string' }, - ambitious_workflows: { type: 'string' }, + whats_working: { type: "string" }, + whats_hindering: { type: "string" }, + quick_wins: { type: "string" }, + ambitious_workflows: { type: "string" }, }, - required: [ - 'whats_working', - 'whats_hindering', - 'quick_wins', - 'ambitious_workflows', - ], + required: ["whats_working", "whats_hindering", "quick_wins", "ambitious_workflows"], }; const limit = pLimit(CONCURRENCY_LIMIT); @@ -594,52 +564,41 @@ export class DataProcessor { ] = await Promise.all([ limit(() => generate( - getInsightPrompt('impressive_workflows'), + getInsightPrompt("impressive_workflows"), schemaImpressiveWorkflows, ), ), limit(() => - generate( - getInsightPrompt('project_areas'), - schemaProjectAreas, - ), + generate(getInsightPrompt("project_areas"), schemaProjectAreas), ), limit(() => generate( - getInsightPrompt('future_opportunities'), + getInsightPrompt("future_opportunities"), schemaFutureOpportunities, ), ), limit(() => generate( - getInsightPrompt('friction_points'), + getInsightPrompt("friction_points"), schemaFrictionPoints, ), ), limit(() => generate( - getInsightPrompt('memorable_moment'), + getInsightPrompt("memorable_moment"), schemaMemorableMoment, ), ), limit(() => - generate( - getInsightPrompt('improvements'), - schemaImprovements, - ), + generate(getInsightPrompt("improvements"), schemaImprovements), ), limit(() => generate( - getInsightPrompt('interaction_style'), + getInsightPrompt("interaction_style"), schemaInteractionStyle, ), ), - limit(() => - generate( - getInsightPrompt('at_a_glance'), - schemaAtAGlance, - ), - ), + limit(() => generate(getInsightPrompt("at_a_glance"), schemaAtAGlance)), ]); logger.debug( @@ -670,13 +629,13 @@ export class DataProcessor { atAGlance, }; } catch (e) { - logger.error('Error generating qualitative insights:', e); + logger.error("Error generating qualitative insights:", e); return undefined; } } private prepareCommonPromptData( - metrics: Omit, + metrics: Omit, facets: SessionFacets[], ): string { // 1. DATA section @@ -706,9 +665,8 @@ export class DataProcessor { }); // Aggregate success (primary_success) - if (facet.primary_success && facet.primary_success !== 'none') { - successAgg[facet.primary_success] = - (successAgg[facet.primary_success] || 0) + 1; + if (facet.primary_success && facet.primary_success !== "none") { + successAgg[facet.primary_success] = (successAgg[facet.primary_success] || 0) + 1; } }); @@ -720,8 +678,8 @@ export class DataProcessor { sessions: metrics.totalSessions || facets.length, analyzed: facets.length, date_range: { - start: Object.keys(metrics.heatmap).sort()[0] || 'N/A', - end: Object.keys(metrics.heatmap).sort().pop() || 'N/A', + start: Object.keys(metrics.heatmap).sort()[0] || "N/A", + end: Object.keys(metrics.heatmap).sort().pop() || "N/A", }, messages: metrics.totalMessages || 0, hours: metrics.totalHours || 0, @@ -735,15 +693,13 @@ export class DataProcessor { }; // 2. SESSION SUMMARIES section - const sessionSummaries = facets - .map((f) => `- ${f.brief_summary}`) - .join('\n'); + const sessionSummaries = facets.map((f) => `- ${f.brief_summary}`).join("\n"); // 3. FRICTION DETAILS section const frictionDetails = facets .filter((f) => f.friction_detail && f.friction_detail.trim().length > 0) .map((f) => `- ${f.friction_detail}`) - .join('\n'); + .join("\n"); return `DATA: ${JSON.stringify(dataObj, null, 2)} @@ -758,9 +714,7 @@ USER INSTRUCTIONS TO Qwen: None captured`; } - private async scanChatFiles( - baseDir: string, - ): Promise> { + private async scanChatFiles(baseDir: string): Promise> { const allChatFiles: Array<{ path: string; mtime: number }> = []; try { @@ -774,12 +728,12 @@ None captured`; // Only process if it's a directory if (stats.isDirectory()) { - const chatsDir = path.join(projectPath, 'chats'); + const chatsDir = path.join(projectPath, "chats"); try { // Get all chat files in the chats directory const files = await fs.readdir(chatsDir); - const chatFiles = files.filter((file) => file.endsWith('.jsonl')); + const chatFiles = files.filter((file) => file.endsWith(".jsonl")); for (const file of chatFiles) { const filePath = path.join(chatsDir, file); @@ -793,10 +747,8 @@ None captured`; } } } catch (error) { - if ((error as NodeJS.ErrnoException).code !== 'ENOENT') { - logger.error( - `Error reading chats directory for project ${projectDir}: ${error}`, - ); + if ((error as NodeJS.ErrnoException).code !== "ENOENT") { + logger.error(`Error reading chats directory for project ${projectDir}: ${error}`); } // Continue to next project if chats directory doesn't exist continue; @@ -804,7 +756,7 @@ None captured`; } } } catch (error) { - if ((error as NodeJS.ErrnoException).code === 'ENOENT') { + if ((error as NodeJS.ErrnoException).code === "ENOENT") { // Base directory doesn't exist, return empty logger.info(`Base directory does not exist: ${baseDir}`); } else { @@ -818,7 +770,7 @@ None captured`; private async generateMetrics( files: Array<{ path: string; mtime: number }>, onProgress?: InsightProgressCallback, - ): Promise> { + ): Promise> { // Initialize data structures const heatmap: HeatMapData = {}; const activeHours: { [hour: number]: number } = {}; @@ -850,9 +802,8 @@ None captured`; const hour = timestamp.getHours(); // Count user messages and slash commands (actual user interactions) - const isUserMessage = record.type === 'user'; - const isSlashCommand = - record.type === 'system' && record.subtype === 'slash_command'; + const isUserMessage = record.type === "user"; + const isSlashCommand = record.type === "system" && record.subtype === "slash_command"; if (isUserMessage || isSlashCommand) { totalMessages++; @@ -870,9 +821,9 @@ None captured`; sessionEndTimes[record.sessionId] = timestamp; // Track tool usage - if (record.type === 'assistant' && record.message?.parts) { + if (record.type === "assistant" && record.message?.parts) { for (const part of record.message.parts) { - if ('functionCall' in part) { + if ("functionCall" in part) { const name = part.functionCall!.name!; toolUsage[name] = (toolUsage[name] || 0) + 1; } @@ -880,17 +831,10 @@ None captured`; } // Track lines and files from tool results - if ( - record.type === 'tool_result' && - record.toolCallResult?.resultDisplay - ) { + if (record.type === "tool_result" && record.toolCallResult?.resultDisplay) { const display = record.toolCallResult.resultDisplay; // Check if it matches FileDiff shape - if ( - typeof display === 'object' && - display !== null && - 'fileName' in display - ) { + if (typeof display === "object" && display !== null && "fileName" in display) { // Cast to any to avoid importing FileDiff type which might not be available here const diff = display as { fileName: unknown; @@ -899,7 +843,7 @@ None captured`; model_removed_lines?: number; }; }; - if (typeof diff.fileName === 'string') { + if (typeof diff.fileName === "string") { uniqueFiles.add(diff.fileName); } @@ -911,10 +855,7 @@ None captured`; } } } catch (error) { - logger.error( - `Failed to process metrics for file ${fileInfo.path}:`, - error, - ); + logger.error(`Failed to process metrics for file ${fileInfo.path}:`, error); // Continue to next file } } @@ -923,10 +864,7 @@ None captured`; if (onProgress) { const percentComplete = batchEnd / totalFiles; const overallProgress = 10 + Math.round(percentComplete * 10); - onProgress( - `Crunching the numbers (${batchEnd}/${totalFiles})`, - overallProgress, - ); + onProgress(`Crunching the numbers (${batchEnd}/${totalFiles})`, overallProgress); } // Yield to event loop to allow GC and UI updates @@ -968,8 +906,8 @@ None captured`; if (date > latestTimestamp) { latestTimestamp = date; latestActiveTime = date.toLocaleTimeString([], { - hour: '2-digit', - minute: '2-digit', + hour: "2-digit", + minute: "2-digit", }); } } @@ -1024,16 +962,11 @@ None captured`; } eligibleSessions.push({ fileInfo, records }); } catch (e) { - logger.error( - `Error reading session file ${fileInfo.path} for facet eligibility:`, - e, - ); + logger.error(`Error reading session file ${fileInfo.path} for facet eligibility:`, e); } } - logger.info( - `Analyzing ${eligibleSessions.length} eligible recent sessions with LLM...`, - ); + logger.info(`Analyzing ${eligibleSessions.length} eligible recent sessions with LLM...`); // Create a limit function with concurrency of 4 to avoid 429 errors const limit = pLimit(CONCURRENCY_LIMIT); @@ -1049,30 +982,20 @@ None captured`; if (records.length > 0 && facetsOutputDir) { const sessionId = records[0].sessionId; if (sessionId) { - const existingFacetPath = path.join( - facetsOutputDir, - `${sessionId}.json`, - ); + const existingFacetPath = path.join(facetsOutputDir, `${sessionId}.json`); try { // Check if file exists and is readable - const existingData = await fs.readFile( - existingFacetPath, - 'utf-8', - ); + const existingData = await fs.readFile(existingFacetPath, "utf-8"); const existingFacet = JSON.parse(existingData); completed++; if (onProgress) { const percent = 20 + Math.round((completed / total) * 60); - onProgress( - 'Analyzing sessions', - percent, - `${completed}/${total}`, - ); + onProgress("Analyzing sessions", percent, `${completed}/${total}`); } return existingFacet; } catch (readError) { // File doesn't exist or is invalid, proceed to analyze - if ((readError as NodeJS.ErrnoException).code !== 'ENOENT') { + if ((readError as NodeJS.ErrnoException).code !== "ENOENT") { logger.warn( `Failed to read existing facet for ${sessionId}, regenerating:`, readError, @@ -1086,15 +1009,8 @@ None captured`; if (facet && facetsOutputDir) { try { - const facetPath = path.join( - facetsOutputDir, - `${facet.session_id}.json`, - ); - await fs.writeFile( - facetPath, - JSON.stringify(facet, null, 2), - 'utf-8', - ); + const facetPath = path.join(facetsOutputDir, `${facet.session_id}.json`); + await fs.writeFile(facetPath, JSON.stringify(facet, null, 2), "utf-8"); } catch (writeError) { logger.error( `Failed to write facet file for session ${facet.session_id}:`, @@ -1106,7 +1022,7 @@ None captured`; completed++; if (onProgress) { const percent = 20 + Math.round((completed / total) * 60); - onProgress('Analyzing sessions', percent, `${completed}/${total}`); + onProgress("Analyzing sessions", percent, `${completed}/${total}`); } return facet; @@ -1115,7 +1031,7 @@ None captured`; completed++; if (onProgress) { const percent = 20 + Math.round((completed / total) * 60); - onProgress('Analyzing sessions', percent, `${completed}/${total}`); + onProgress("Analyzing sessions", percent, `${completed}/${total}`); } return null; } @@ -1123,9 +1039,7 @@ None captured`; ); const sessionFacetsWithNulls = await Promise.all(analysisPromises); - const facets = sessionFacetsWithNulls.filter( - (f): f is SessionFacets => f !== null, - ); + const facets = sessionFacetsWithNulls.filter((f): f is SessionFacets => f !== null); return facets; } } diff --git a/apps/airiscode-cli/src/services/insight/generators/StaticInsightGenerator.ts b/apps/airiscode-cli/src/services/insight/generators/StaticInsightGenerator.ts index 371c72369..b430580cf 100644 --- a/apps/airiscode-cli/src/services/insight/generators/StaticInsightGenerator.ts +++ b/apps/airiscode-cli/src/services/insight/generators/StaticInsightGenerator.ts @@ -4,16 +4,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -import fs from 'fs/promises'; -import path from 'path'; -import { DataProcessor } from './DataProcessor.js'; -import { TemplateRenderer } from './TemplateRenderer.js'; -import type { - InsightData, - InsightProgressCallback, -} from '../types/StaticInsightTypes.js'; - -import { updateSymlink, Storage, type Config } from '@airiscode/core'; +import { type Config, Storage, updateSymlink } from "@airiscode/core"; +import fs from "fs/promises"; +import path from "path"; +import type { InsightData, InsightProgressCallback } from "../types/StaticInsightTypes.js"; +import { DataProcessor } from "./DataProcessor.js"; +import { TemplateRenderer } from "./TemplateRenderer.js"; export class StaticInsightGenerator { private dataProcessor: DataProcessor; @@ -26,7 +22,7 @@ export class StaticInsightGenerator { // Ensure the output directory exists private async ensureOutputDirectory(): Promise { - const outputDir = path.join(Storage.getRuntimeBaseDir(), 'insights'); + const outputDir = path.join(Storage.getRuntimeBaseDir(), "insights"); await fs.mkdir(outputDir, { recursive: true }); return outputDir; } @@ -34,8 +30,8 @@ export class StaticInsightGenerator { // Generate timestamped filename with collision detection private async generateOutputPath(outputDir: string): Promise { const now = new Date(); - const date = now.toISOString().split('T')[0]; // YYYY-MM-DD - const time = now.toTimeString().slice(0, 8).replace(/:/g, ''); // HHMMSS + const date = now.toISOString().split("T")[0]; // YYYY-MM-DD + const time = now.toTimeString().slice(0, 8).replace(/:/g, ""); // HHMMSS let outputPath = path.join(outputDir, `insight-${date}.html`); @@ -51,11 +47,8 @@ export class StaticInsightGenerator { return outputPath; } - private async updateInsightSymlink( - outputDir: string, - targetPath: string, - ): Promise { - const latestPath = path.join(outputDir, 'insight.html'); + private async updateInsightSymlink(outputDir: string, targetPath: string): Promise { + const latestPath = path.join(outputDir, "insight.html"); await updateSymlink(latestPath, targetPath); } @@ -66,7 +59,7 @@ export class StaticInsightGenerator { ): Promise { // Ensure output directory exists const outputDir = await this.ensureOutputDirectory(); - const facetsDir = path.join(outputDir, 'facets'); + const facetsDir = path.join(outputDir, "facets"); await fs.mkdir(facetsDir, { recursive: true }); // Process data @@ -83,7 +76,7 @@ export class StaticInsightGenerator { const outputPath = await this.generateOutputPath(outputDir); // Write the HTML file - await fs.writeFile(outputPath, html, 'utf-8'); + await fs.writeFile(outputPath, html, "utf-8"); await this.updateInsightSymlink(outputDir, outputPath); diff --git a/apps/airiscode-cli/src/services/insight/generators/TemplateRenderer.ts b/apps/airiscode-cli/src/services/insight/generators/TemplateRenderer.ts index d516d19d7..fb19af926 100644 --- a/apps/airiscode-cli/src/services/insight/generators/TemplateRenderer.ts +++ b/apps/airiscode-cli/src/services/insight/generators/TemplateRenderer.ts @@ -4,9 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -const INSIGHT_JS = ''; -const INSIGHT_CSS = ''; -import type { InsightData } from '../types/StaticInsightTypes.js'; +const INSIGHT_JS = ""; +const INSIGHT_CSS = ""; + +import type { InsightData } from "../types/StaticInsightTypes.js"; export class TemplateRenderer { // Render the complete HTML file diff --git a/apps/airiscode-cli/src/services/insight/types/StaticInsightTypes.ts b/apps/airiscode-cli/src/services/insight/types/StaticInsightTypes.ts index f1cee1b36..d23b9cfcd 100644 --- a/apps/airiscode-cli/src/services/insight/types/StaticInsightTypes.ts +++ b/apps/airiscode-cli/src/services/insight/types/StaticInsightTypes.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { QualitativeInsights } from './QualitativeInsightTypes.js'; +import type { QualitativeInsights } from "./QualitativeInsightTypes.js"; export interface HeatMapData { [date: string]: number; @@ -44,34 +44,34 @@ export interface SessionFacets { underlying_goal: string; goal_categories: Record; outcome: - | 'fully_achieved' - | 'mostly_achieved' - | 'partially_achieved' - | 'not_achieved' - | 'unclear_from_transcript'; + | "fully_achieved" + | "mostly_achieved" + | "partially_achieved" + | "not_achieved" + | "unclear_from_transcript"; user_satisfaction_counts: Record; Qwen_helpfulness: - | 'unhelpful' - | 'slightly_helpful' - | 'moderately_helpful' - | 'very_helpful' - | 'essential'; + | "unhelpful" + | "slightly_helpful" + | "moderately_helpful" + | "very_helpful" + | "essential"; session_type: - | 'single_task' - | 'multi_task' - | 'iterative_refinement' - | 'exploration' - | 'quick_question'; + | "single_task" + | "multi_task" + | "iterative_refinement" + | "exploration" + | "quick_question"; friction_counts: Record; friction_detail: string; primary_success: - | 'none' - | 'fast_accurate_search' - | 'correct_code_edits' - | 'good_explanations' - | 'proactive_help' - | 'multi_file_changes' - | 'good_debugging'; + | "none" + | "fast_accurate_search" + | "correct_code_edits" + | "good_explanations" + | "proactive_help" + | "multi_file_changes" + | "good_debugging"; brief_summary: string; } @@ -83,8 +83,4 @@ export interface StaticInsightTemplateData { generatedTime: string; } -export type InsightProgressCallback = ( - stage: string, - progress: number, - detail?: string, -) => void; +export type InsightProgressCallback = (stage: string, progress: number, detail?: string) => void; diff --git a/apps/airiscode-cli/src/services/markdown-command-parser.ts b/apps/airiscode-cli/src/services/markdown-command-parser.ts index 5fe1a3c04..a74ebbf69 100644 --- a/apps/airiscode-cli/src/services/markdown-command-parser.ts +++ b/apps/airiscode-cli/src/services/markdown-command-parser.ts @@ -4,11 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { z } from 'zod'; -import { - parse as parseYaml, - normalizeContent, -} from '@airiscode/core'; +import { normalizeContent, parse as parseYaml } from "@airiscode/core"; +import { z } from "zod"; /** * Defines the Zod schema for a Markdown command definition file. @@ -21,8 +18,8 @@ export const MarkdownCommandDefSchema = z.object({ }) .optional(), prompt: z.string({ - required_error: 'The prompt content is required.', - invalid_type_error: 'The prompt content must be a string.', + required_error: "The prompt content is required.", + invalid_type_error: "The prompt content must be a string.", }), }); @@ -48,7 +45,7 @@ export function parseMarkdownCommand(content: string): MarkdownCommandDef { }; } - const [, frontmatterYaml = '', body] = match; + const [, frontmatterYaml = "", body] = match; // Parse YAML frontmatter if not empty let frontmatter: Record | undefined; diff --git a/apps/airiscode-cli/src/services/prompt-processors/argumentProcessor.ts b/apps/airiscode-cli/src/services/prompt-processors/argumentProcessor.ts index fa848ccce..4e45e315f 100644 --- a/apps/airiscode-cli/src/services/prompt-processors/argumentProcessor.ts +++ b/apps/airiscode-cli/src/services/prompt-processors/argumentProcessor.ts @@ -4,9 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { appendToLastTextPart } from '@airiscode/core'; -import type { IPromptProcessor, PromptPipelineContent } from './types.js'; -import type { CommandContext } from '../../ui/commands/types.js'; +import { appendToLastTextPart } from "@airiscode/core"; +import type { CommandContext } from "../../ui/commands/types.js"; +import type { IPromptProcessor, PromptPipelineContent } from "./types.js"; /** * Appends the user's full command invocation to the prompt if arguments are diff --git a/apps/airiscode-cli/src/services/prompt-processors/atFileProcessor.ts b/apps/airiscode-cli/src/services/prompt-processors/atFileProcessor.ts index d77221a77..2c321423d 100644 --- a/apps/airiscode-cli/src/services/prompt-processors/atFileProcessor.ts +++ b/apps/airiscode-cli/src/services/prompt-processors/atFileProcessor.ts @@ -4,21 +4,17 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { - flatMapTextParts, - readPathFromWorkspace, - createDebugLogger, -} from '@airiscode/core'; -import type { CommandContext } from '../../ui/commands/types.js'; -import { MessageType } from '../../ui/types.js'; +import { createDebugLogger, flatMapTextParts, readPathFromWorkspace } from "@airiscode/core"; +import type { CommandContext } from "../../ui/commands/types.js"; +import { MessageType } from "../../ui/types.js"; +import { extractInjections } from "./injectionParser.js"; import { AT_FILE_INJECTION_TRIGGER, type IPromptProcessor, type PromptPipelineContent, -} from './types.js'; -import { extractInjections } from './injectionParser.js'; +} from "./types.js"; -const debugLogger = createDebugLogger('AT_FILE_PROCESSOR'); +const debugLogger = createDebugLogger("AT_FILE_PROCESSOR"); export class AtFileProcessor implements IPromptProcessor { constructor(private readonly commandName?: string) {} @@ -37,11 +33,7 @@ export class AtFileProcessor implements IPromptProcessor { return [{ text }]; } - const injections = extractInjections( - text, - AT_FILE_INJECTION_TRIGGER, - this.commandName, - ); + const injections = extractInjections(text, AT_FILE_INJECTION_TRIGGER, this.commandName); if (injections.length === 0) { return [{ text }]; } @@ -60,29 +52,17 @@ export class AtFileProcessor implements IPromptProcessor { const fileContentParts = await readPathFromWorkspace(pathStr, config); if (fileContentParts.length === 0) { const uiMessage = `File '@{${pathStr}}' was ignored by .gitignore or .airiscodeigenore and was not included in the prompt.`; - context.ui.addItem( - { type: MessageType.INFO, text: uiMessage }, - Date.now(), - ); + context.ui.addItem({ type: MessageType.INFO, text: uiMessage }, Date.now()); } output.push(...fileContentParts); } catch (error) { - const message = - error instanceof Error ? error.message : String(error); + const message = error instanceof Error ? error.message : String(error); const uiMessage = `Failed to inject content for '@{${pathStr}}': ${message}`; - debugLogger.error( - `[AtFileProcessor] ${uiMessage}. Leaving placeholder in prompt.`, - ); - context.ui.addItem( - { type: MessageType.ERROR, text: uiMessage }, - Date.now(), - ); + debugLogger.error(`[AtFileProcessor] ${uiMessage}. Leaving placeholder in prompt.`); + context.ui.addItem({ type: MessageType.ERROR, text: uiMessage }, Date.now()); - const placeholder = text.substring( - injection.startIndex, - injection.endIndex, - ); + const placeholder = text.substring(injection.startIndex, injection.endIndex); output.push({ text: placeholder }); } lastIndex = injection.endIndex; diff --git a/apps/airiscode-cli/src/services/prompt-processors/injectionParser.ts b/apps/airiscode-cli/src/services/prompt-processors/injectionParser.ts index 52d3226db..b31e9f7c3 100644 --- a/apps/airiscode-cli/src/services/prompt-processors/injectionParser.ts +++ b/apps/airiscode-cli/src/services/prompt-processors/injectionParser.ts @@ -50,15 +50,12 @@ export function extractInjections( while (currentIndex < prompt.length) { const char = prompt[currentIndex]; - if (char === '{') { + if (char === "{") { braceCount++; - } else if (char === '}') { + } else if (char === "}") { braceCount--; if (braceCount === 0) { - const injectionContent = prompt.substring( - startIndex + trigger.length, - currentIndex, - ); + const injectionContent = prompt.substring(startIndex + trigger.length, currentIndex); const endIndex = currentIndex + 1; injections.push({ @@ -77,7 +74,7 @@ export function extractInjections( // Check if the inner loop finished without finding the closing brace. if (!foundEnd) { - const contextInfo = contextName ? ` in command '${contextName}'` : ''; + const contextInfo = contextName ? ` in command '${contextName}'` : ""; // Enforce strict parsing (Comment 1) and clarify limitations (Comment 2). throw new Error( `Invalid syntax${contextInfo}: Unclosed injection starting at index ${startIndex} ('${trigger}'). Ensure braces are balanced. Paths or commands with unbalanced braces are not supported directly.`, diff --git a/apps/airiscode-cli/src/services/prompt-processors/shellProcessor.ts b/apps/airiscode-cli/src/services/prompt-processors/shellProcessor.ts index e7788e991..166b071ed 100644 --- a/apps/airiscode-cli/src/services/prompt-processors/shellProcessor.ts +++ b/apps/airiscode-cli/src/services/prompt-processors/shellProcessor.ts @@ -6,22 +6,19 @@ import { ApprovalMode, + checkArgumentSafety, checkCommandPermissions, escapeShellArg, + flatMapTextParts, getShellConfiguration, ShellExecutionService, - flatMapTextParts, - checkArgumentSafety, -} from '@airiscode/core'; +} from "@airiscode/core"; -import type { CommandContext } from '../../ui/commands/types.js'; -import type { IPromptProcessor, PromptPipelineContent } from './types.js'; -import { - SHELL_INJECTION_TRIGGER, - SHORTHAND_ARGS_PLACEHOLDER, -} from './types.js'; -import { extractInjections, type Injection } from './injectionParser.js'; -import { themeManager } from '../../ui/themes/theme-manager.js'; +import type { CommandContext } from "../../ui/commands/types.js"; +import { themeManager } from "../../ui/themes/theme-manager.js"; +import { extractInjections, type Injection } from "./injectionParser.js"; +import type { IPromptProcessor, PromptPipelineContent } from "./types.js"; +import { SHELL_INJECTION_TRIGGER, SHORTHAND_ARGS_PLACEHOLDER } from "./types.js"; export class ConfirmationRequiredError extends Error { constructor( @@ -29,7 +26,7 @@ export class ConfirmationRequiredError extends Error { public commandsToConfirm: string[], ) { super(message); - this.name = 'ConfirmationRequiredError'; + this.name = "ConfirmationRequiredError"; } } @@ -59,21 +56,17 @@ export class ShellProcessor implements IPromptProcessor { prompt: PromptPipelineContent, context: CommandContext, ): Promise { - return flatMapTextParts(prompt, (text) => - this.processString(text, context), - ); + return flatMapTextParts(prompt, (text) => this.processString(text, context)); } private async processString( prompt: string, context: CommandContext, ): Promise { - const userArgsRaw = context.invocation?.args || ''; + const userArgsRaw = context.invocation?.args || ""; if (!prompt.includes(SHELL_INJECTION_TRIGGER)) { - return [ - { text: prompt.replaceAll(SHORTHAND_ARGS_PLACEHOLDER, userArgsRaw) }, - ]; + return [{ text: prompt.replaceAll(SHORTHAND_ARGS_PLACEHOLDER, userArgsRaw) }]; } const config = context.services.config; @@ -84,46 +77,35 @@ export class ShellProcessor implements IPromptProcessor { } const { sessionShellAllowlist } = context.session; - const injections = extractInjections( - prompt, - SHELL_INJECTION_TRIGGER, - this.commandName, - ); + const injections = extractInjections(prompt, SHELL_INJECTION_TRIGGER, this.commandName); // If extractInjections found no closed blocks (and didn't throw), treat as raw. if (injections.length === 0) { - return [ - { text: prompt.replaceAll(SHORTHAND_ARGS_PLACEHOLDER, userArgsRaw) }, - ]; + return [{ text: prompt.replaceAll(SHORTHAND_ARGS_PLACEHOLDER, userArgsRaw) }]; } const { shell } = getShellConfiguration(); const userArgsEscaped = escapeShellArg(userArgsRaw, shell); // Check safety of the value that will be used for $ARGUMENTS (after removing outer quotes) - let userArgsForArgumentsPlaceholder = userArgsRaw.replace( - /^'([\s\S]*?)'$/, - '$1', - ); + let userArgsForArgumentsPlaceholder = userArgsRaw.replace(/^'([\s\S]*?)'$/, "$1"); const argumentSafety = checkArgumentSafety(userArgsForArgumentsPlaceholder); if (!argumentSafety.isSafe) { userArgsForArgumentsPlaceholder = userArgsEscaped; } - const resolvedInjections: ResolvedShellInjection[] = injections.map( - (injection) => { - const command = injection.content; + const resolvedInjections: ResolvedShellInjection[] = injections.map((injection) => { + const command = injection.content; - if (command === '') { - return { ...injection, resolvedCommand: undefined }; - } + if (command === "") { + return { ...injection, resolvedCommand: undefined }; + } - const resolvedCommand = command - .replaceAll(SHORTHAND_ARGS_PLACEHOLDER, userArgsEscaped) // Replace {{args}} - .replaceAll('$ARGUMENTS', userArgsForArgumentsPlaceholder); - return { ...injection, resolvedCommand }; - }, - ); + const resolvedCommand = command + .replaceAll(SHORTHAND_ARGS_PLACEHOLDER, userArgsEscaped) // Replace {{args}} + .replaceAll("$ARGUMENTS", userArgsForArgumentsPlaceholder); + return { ...injection, resolvedCommand }; + }); const commandsToConfirm = new Set(); for (const injection of resolvedInjections) { @@ -137,14 +119,12 @@ export class ShellProcessor implements IPromptProcessor { // Determine if this command is explicitly auto-approved via PermissionManager const pm = config.getPermissionManager?.(); - const isAllowedBySettings = pm - ? (await pm.isCommandAllowed(command)) === 'allow' - : false; + const isAllowedBySettings = pm ? (await pm.isCommandAllowed(command)) === "allow" : false; if (!allAllowed) { if (isHardDenial) { throw new Error( - `${this.commandName} cannot be run. Blocked command: "${command}". Reason: ${blockReason || 'Blocked by configuration.'}`, + `${this.commandName} cannot be run. Blocked command: "${command}". Reason: ${blockReason || "Blocked by configuration."}`, ); } @@ -165,21 +145,18 @@ export class ShellProcessor implements IPromptProcessor { // Handle confirmation requirements. if (commandsToConfirm.size > 0) { throw new ConfirmationRequiredError( - 'Shell command confirmation required', + "Shell command confirmation required", Array.from(commandsToConfirm), ); } - let processedPrompt = ''; + let processedPrompt = ""; let lastIndex = 0; for (const injection of resolvedInjections) { // Append the text segment BEFORE the injection, substituting {{args}} with RAW input. const segment = prompt.substring(lastIndex, injection.startIndex); - processedPrompt += segment.replaceAll( - SHORTHAND_ARGS_PLACEHOLDER, - userArgsRaw, - ); + processedPrompt += segment.replaceAll(SHORTHAND_ARGS_PLACEHOLDER, userArgsRaw); // Execute the resolved command (which already has ESCAPED input). if (injection.resolvedCommand) { @@ -213,10 +190,7 @@ export class ShellProcessor implements IPromptProcessor { // Append a status message if the command did not succeed. if (executionResult.aborted) { processedPrompt += `\n[Shell command '${injection.resolvedCommand}' aborted]`; - } else if ( - executionResult.exitCode !== 0 && - executionResult.exitCode !== null - ) { + } else if (executionResult.exitCode !== 0 && executionResult.exitCode !== null) { processedPrompt += `\n[Shell command '${injection.resolvedCommand}' exited with code ${executionResult.exitCode}]`; } else if (executionResult.signal !== null) { processedPrompt += `\n[Shell command '${injection.resolvedCommand}' terminated by signal ${executionResult.signal}]`; @@ -228,10 +202,7 @@ export class ShellProcessor implements IPromptProcessor { // Append the remaining text AFTER the last injection, substituting {{args}} with RAW input. const finalSegment = prompt.substring(lastIndex); - processedPrompt += finalSegment.replaceAll( - SHORTHAND_ARGS_PLACEHOLDER, - userArgsRaw, - ); + processedPrompt += finalSegment.replaceAll(SHORTHAND_ARGS_PLACEHOLDER, userArgsRaw); return [{ text: processedPrompt }]; } diff --git a/apps/airiscode-cli/src/services/prompt-processors/types.ts b/apps/airiscode-cli/src/services/prompt-processors/types.ts index 9e67396ff..207a7b47e 100644 --- a/apps/airiscode-cli/src/services/prompt-processors/types.ts +++ b/apps/airiscode-cli/src/services/prompt-processors/types.ts @@ -4,8 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { CommandContext } from '../../ui/commands/types.js'; -import type { PartUnion } from '@airiscode/core'; +import type { PartUnion } from "@airiscode/core"; +import type { CommandContext } from "../../ui/commands/types.js"; /** * Defines the input/output type for prompt processors. @@ -30,10 +30,7 @@ export interface IPromptProcessor { * @returns A promise that resolves to the transformed prompt string, which * will be passed to the next processor or, if it's the last one, sent to the model. */ - process( - prompt: PromptPipelineContent, - context: CommandContext, - ): Promise; + process(prompt: PromptPipelineContent, context: CommandContext): Promise; } /** @@ -41,14 +38,14 @@ export interface IPromptProcessor { * When used outside of !{...}, arguments are injected raw. * When used inside !{...}, arguments are shell-escaped. */ -export const SHORTHAND_ARGS_PLACEHOLDER = '{{args}}'; +export const SHORTHAND_ARGS_PLACEHOLDER = "{{args}}"; /** * The trigger string for shell command injection in custom commands. */ -export const SHELL_INJECTION_TRIGGER = '!{'; +export const SHELL_INJECTION_TRIGGER = "!{"; /** * The trigger string for at file injection in custom commands. */ -export const AT_FILE_INJECTION_TRIGGER = '@{'; +export const AT_FILE_INJECTION_TRIGGER = "@{"; diff --git a/apps/airiscode-cli/src/services/types.ts b/apps/airiscode-cli/src/services/types.ts index 13a87687e..824d8e7e0 100644 --- a/apps/airiscode-cli/src/services/types.ts +++ b/apps/airiscode-cli/src/services/types.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { SlashCommand } from '../ui/commands/types.js'; +import type { SlashCommand } from "../ui/commands/types.js"; /** * Defines the contract for any class that can load and provide slash commands. diff --git a/apps/airiscode-cli/src/test-utils/customMatchers.ts b/apps/airiscode-cli/src/test-utils/customMatchers.ts index 2a1b275ad..8e1d54784 100644 --- a/apps/airiscode-cli/src/test-utils/customMatchers.ts +++ b/apps/airiscode-cli/src/test-utils/customMatchers.ts @@ -12,9 +12,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { Assertion } from 'vitest'; -import { expect } from 'vitest'; -import type { TextBuffer } from '../ui/components/shared/text-buffer.js'; +import type { Assertion } from "vitest"; +import { expect } from "vitest"; +import type { TextBuffer } from "../ui/components/shared/text-buffer.js"; // RegExp to detect invalid characters: backspace, and ANSI escape codes // eslint-disable-next-line no-control-regex @@ -28,7 +28,7 @@ function toHaveOnlyValidCharacters(this: Assertion, buffer: TextBuffer) { for (let i = 0; i < buffer.lines.length; i++) { const line = buffer.lines[i]; - if (line.includes('\n')) { + if (line.includes("\n")) { pass = false; invalidLines.push({ line: i, content: line }); break; // Fail fast on newlines @@ -42,11 +42,11 @@ function toHaveOnlyValidCharacters(this: Assertion, buffer: TextBuffer) { return { pass, message: () => - `Expected buffer ${isNot ? 'not ' : ''}to have only valid characters, but found invalid characters in lines:\n${invalidLines + `Expected buffer ${isNot ? "not " : ""}to have only valid characters, but found invalid characters in lines:\n${invalidLines .map((l) => ` [${l.line}]: "${l.content}"`) /* This line was changed */ - .join('\n')}`, + .join("\n")}`, actual: buffer.lines, - expected: 'Lines with no line breaks, backspaces, or escape codes.', + expected: "Lines with no line breaks, backspaces, or escape codes.", }; } @@ -56,7 +56,7 @@ expect.extend({ } as any); // Extend Vitest's `expect` interface with the custom matcher's type definition. -declare module 'vitest' { +declare module "vitest" { interface Assertion { toHaveOnlyValidCharacters(): T; } diff --git a/apps/airiscode-cli/src/test-utils/mockCommandContext.ts b/apps/airiscode-cli/src/test-utils/mockCommandContext.ts index 4901aa121..daa55f46b 100644 --- a/apps/airiscode-cli/src/test-utils/mockCommandContext.ts +++ b/apps/airiscode-cli/src/test-utils/mockCommandContext.ts @@ -4,12 +4,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { vi } from 'vitest'; -import type { CommandContext } from '../ui/commands/types.js'; -import type { LoadedSettings } from '../config/settings.js'; -import type { GitService } from '@airiscode/core'; -import type { SessionStatsState } from '../ui/contexts/SessionContext.js'; -import { ToolCallDecision } from '../ui/contexts/SessionContext.js'; +import type { GitService } from "@airiscode/core"; +import { vi } from "vitest"; +import type { LoadedSettings } from "../config/settings.js"; +import type { CommandContext } from "../ui/commands/types.js"; +import type { SessionStatsState } from "../ui/contexts/SessionContext.js"; +import { ToolCallDecision } from "../ui/contexts/SessionContext.js"; // A utility type to make all properties of an object, and its nested objects, partial. type DeepPartial = T extends object @@ -30,9 +30,9 @@ export const createMockCommandContext = ( ): CommandContext => { const defaultMocks: CommandContext = { invocation: { - raw: '', - name: '', - args: '', + raw: "", + name: "", + args: "", }, services: { config: null, @@ -70,7 +70,7 @@ export const createMockCommandContext = ( sessionShellAllowlist: new Set(), startNewSession: vi.fn(), stats: { - sessionId: '', + sessionId: "", sessionStartTime: new Date(), lastPromptTokenCount: 0, metrics: { @@ -100,14 +100,14 @@ export const createMockCommandContext = ( const output = { ...target }; for (const key in source) { - if (Object.prototype.hasOwnProperty.call(source, key)) { + if (Object.hasOwn(source, key)) { const sourceValue = source[key]; const targetValue = output[key]; if ( // We only want to recursivlty merge plain objects - Object.prototype.toString.call(sourceValue) === '[object Object]' && - Object.prototype.toString.call(targetValue) === '[object Object]' + Object.prototype.toString.call(sourceValue) === "[object Object]" && + Object.prototype.toString.call(targetValue) === "[object Object]" ) { output[key] = merge(targetValue, sourceValue); } else { diff --git a/apps/airiscode-cli/src/test-utils/render.tsx b/apps/airiscode-cli/src/test-utils/render.tsx index a827a9590..ef48b882e 100644 --- a/apps/airiscode-cli/src/test-utils/render.tsx +++ b/apps/airiscode-cli/src/test-utils/render.tsx @@ -4,20 +4,20 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { render } from 'ink-testing-library'; -import type React from 'react'; -import type { Config } from '@airiscode/core'; -import { LoadedSettings } from '../config/settings.js'; -import { KeypressProvider } from '../ui/contexts/KeypressContext.js'; -import { SettingsContext } from '../ui/contexts/SettingsContext.js'; -import { ShellFocusContext } from '../ui/contexts/ShellFocusContext.js'; -import { ConfigContext } from '../ui/contexts/ConfigContext.js'; +import type { Config } from "@airiscode/core"; +import { render } from "ink-testing-library"; +import type React from "react"; +import { LoadedSettings } from "../config/settings.js"; +import { ConfigContext } from "../ui/contexts/ConfigContext.js"; +import { KeypressProvider } from "../ui/contexts/KeypressContext.js"; +import { SettingsContext } from "../ui/contexts/SettingsContext.js"; +import { ShellFocusContext } from "../ui/contexts/ShellFocusContext.js"; const mockSettings = new LoadedSettings( - { path: '', settings: {}, originalSettings: {} }, - { path: '', settings: {}, originalSettings: {} }, - { path: '', settings: {}, originalSettings: {} }, - { path: '', settings: {}, originalSettings: {} }, + { path: "", settings: {}, originalSettings: {} }, + { path: "", settings: {}, originalSettings: {} }, + { path: "", settings: {}, originalSettings: {} }, + { path: "", settings: {}, originalSettings: {} }, true, new Set(), ); @@ -38,9 +38,7 @@ export const renderWithProviders = ( - - {component} - + {component} , diff --git a/apps/airiscode-cli/src/ui/App.tsx b/apps/airiscode-cli/src/ui/App.tsx index 54684a8c2..d010818b7 100644 --- a/apps/airiscode-cli/src/ui/App.tsx +++ b/apps/airiscode-cli/src/ui/App.tsx @@ -4,12 +4,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { useIsScreenReaderEnabled } from 'ink'; -import { useUIState } from './contexts/UIStateContext.js'; -import { StreamingContext } from './contexts/StreamingContext.js'; -import { QuittingDisplay } from './components/QuittingDisplay.js'; -import { ScreenReaderAppLayout } from './layouts/ScreenReaderAppLayout.js'; -import { DefaultAppLayout } from './layouts/DefaultAppLayout.js'; +import { useIsScreenReaderEnabled } from "ink"; +import { QuittingDisplay } from "./components/QuittingDisplay.js"; +import { StreamingContext } from "./contexts/StreamingContext.js"; +import { useUIState } from "./contexts/UIStateContext.js"; +import { DefaultAppLayout } from "./layouts/DefaultAppLayout.js"; +import { ScreenReaderAppLayout } from "./layouts/ScreenReaderAppLayout.js"; export const App = () => { const uiState = useUIState(); diff --git a/apps/airiscode-cli/src/ui/AppContainer.tsx b/apps/airiscode-cli/src/ui/AppContainer.tsx index f10642b12..0ccea3c21 100644 --- a/apps/airiscode-cli/src/ui/AppContainer.tsx +++ b/apps/airiscode-cli/src/ui/AppContainer.tsx @@ -4,146 +4,130 @@ * SPDX-License-Identifier: Apache-2.0 */ +import process from "node:process"; import { - useMemo, - useState, - useCallback, - useEffect, - useRef, - useLayoutEffect, -} from 'react'; -import { type DOMElement, measureElement } from 'ink'; -import { App } from './App.js'; -import { AppContext } from './contexts/AppContext.js'; -import { UIStateContext, type UIState } from './contexts/UIStateContext.js'; -import { - UIActionsContext, - type UIActions, -} from './contexts/UIActionsContext.js'; -import { ConfigContext } from './contexts/ConfigContext.js'; -import { - type HistoryItem, - ToolCallStatus, - type HistoryItemWithoutId, -} from './types.js'; -import { MessageType, StreamingState } from './types.js'; -import { - type EditorType, + ApprovalMode, + abortSpeculation, + acceptSpeculation, type Config, - type IdeInfo, - type IdeContext, - IdeClient, - ideContextStore, createDebugLogger, - getErrorMessage, - getAllGeminiMdFilenames, - ShellExecutionService, - Storage, - SessionEndReason, - SessionStartSource, + type EditorType, generatePromptSuggestion, + getAllGeminiMdFilenames, + getErrorMessage, + IDLE_SPECULATION, + IdeClient, + type IdeContext, + type IdeInfo, + ideContextStore, logPromptSuggestion, - PromptSuggestionEvent, logSpeculation, + type PermissionMode, + PromptSuggestionEvent, + SessionEndReason, + SessionStartSource, + ShellExecutionService, SpeculationEvent, - startSpeculation, - acceptSpeculation, - abortSpeculation, type SpeculationState, - IDLE_SPECULATION, - ApprovalMode, - type PermissionMode, -} from '@airiscode/core'; -import { buildResumedHistoryItems } from './utils/resumeHistoryUtils.js'; -import { validateAuthMethod } from '../config/auth.js'; -import { loadHierarchicalGeminiMemory } from '../config/config.js'; -import process from 'node:process'; -import { useHistory } from './hooks/useHistoryManager.js'; -import { useMemoryMonitor } from './hooks/useMemoryMonitor.js'; -import { useThemeCommand } from './hooks/useThemeCommand.js'; -import { useFeedbackDialog } from './hooks/useFeedbackDialog.js'; -import { useAuthCommand } from './auth/useAuth.js'; -import { useEditorSettings } from './hooks/useEditorSettings.js'; -import { useSettingsCommand } from './hooks/useSettingsCommand.js'; -import { useModelCommand } from './hooks/useModelCommand.js'; -import { useArenaCommand } from './hooks/useArenaCommand.js'; -import { useApprovalModeCommand } from './hooks/useApprovalModeCommand.js'; -import { useResumeCommand } from './hooks/useResumeCommand.js'; -import { useSlashCommandProcessor } from './hooks/slashCommandProcessor.js'; -import { useVimMode } from './contexts/VimModeContext.js'; -import { VerboseModeProvider } from './contexts/VerboseModeContext.js'; -import { useTerminalSize } from './hooks/useTerminalSize.js'; -import { calculatePromptWidths } from './components/InputPrompt.js'; -import { useStdin, useStdout } from 'ink'; + Storage, + startSpeculation, +} from "@airiscode/core"; +import { type DOMElement, measureElement, useStdin, useStdout } from "ink"; +import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; +import { validateAuthMethod } from "../config/auth.js"; +import { loadHierarchicalGeminiMemory } from "../config/config.js"; +import { App } from "./App.js"; +import { useAuthCommand } from "./auth/useAuth.js"; +import { calculatePromptWidths } from "./components/InputPrompt.js"; +import { AppContext } from "./contexts/AppContext.js"; +import { ConfigContext } from "./contexts/ConfigContext.js"; +import { type UIActions, UIActionsContext } from "./contexts/UIActionsContext.js"; +import { type UIState, UIStateContext } from "./contexts/UIStateContext.js"; +import { VerboseModeProvider } from "./contexts/VerboseModeContext.js"; +import { useVimMode } from "./contexts/VimModeContext.js"; +import { useSlashCommandProcessor } from "./hooks/slashCommandProcessor.js"; +import { useApprovalModeCommand } from "./hooks/useApprovalModeCommand.js"; +import { useArenaCommand } from "./hooks/useArenaCommand.js"; +import { useEditorSettings } from "./hooks/useEditorSettings.js"; +import { useFeedbackDialog } from "./hooks/useFeedbackDialog.js"; +import { useHistory } from "./hooks/useHistoryManager.js"; +import { useMemoryMonitor } from "./hooks/useMemoryMonitor.js"; +import { useModelCommand } from "./hooks/useModelCommand.js"; +import { useResumeCommand } from "./hooks/useResumeCommand.js"; +import { useSettingsCommand } from "./hooks/useSettingsCommand.js"; +import { useTerminalSize } from "./hooks/useTerminalSize.js"; +import { useThemeCommand } from "./hooks/useThemeCommand.js"; +import { + type HistoryItem, + type HistoryItemWithoutId, + MessageType, + StreamingState, + ToolCallStatus, +} from "./types.js"; +import { buildResumedHistoryItems } from "./utils/resumeHistoryUtils.js"; + // Minimal ansi-escapes replacement (only clearTerminal used) const ansiEscapes = { - clearTerminal: - process.platform === 'win32' - ? '\x1b[2J\x1b[0f' - : '\x1b[2J\x1b[3J\x1b[H', + clearTerminal: process.platform === "win32" ? "\x1b[2J\x1b[0f" : "\x1b[2J\x1b[3J\x1b[H", }; -import * as fs from 'node:fs'; -import { basename } from 'node:path'; -import { computeWindowTitle } from '../utils/windowTitle.js'; -import { clearScreen } from '../utils/stdioHelpers.js'; -import { useTextBuffer } from './components/shared/text-buffer.js'; -import { useLogger } from './hooks/useLogger.js'; -import { useGeminiStream } from './hooks/useGeminiStream.js'; -import { useVim } from './hooks/vim.js'; -import { isBtwCommand } from './utils/commandUtils.js'; -import { type LoadedSettings, SettingScope } from '../config/settings.js'; -import { type InitializationResult } from '../core/initializer.js'; -import { useFocus } from './hooks/useFocus.js'; -import { useBracketedPaste } from './hooks/useBracketedPaste.js'; -import { useKeypress, type Key } from './hooks/useKeypress.js'; -import { keyMatchers, Command } from './keyMatchers.js'; -import { useLoadingIndicator } from './hooks/useLoadingIndicator.js'; -import { useFolderTrust } from './hooks/useFolderTrust.js'; -import { useIdeTrustListener } from './hooks/useIdeTrustListener.js'; -import { type IdeIntegrationNudgeResult } from './IdeIntegrationNudge.js'; -import { type CommandMigrationNudgeResult } from './CommandFormatMigrationNudge.js'; -import { useCommandMigration } from './hooks/useCommandMigration.js'; -import { migrateTomlCommands } from '../services/command-migration-tool.js'; -import { type UpdateObject } from './utils/updateCheck.js'; -import { setUpdateHandler } from '../utils/handleAutoUpdate.js'; -import { registerCleanup, runExitCleanup } from '../utils/cleanup.js'; -import { useMessageQueue } from './hooks/useMessageQueue.js'; -import { useAutoAcceptIndicator } from './hooks/useAutoAcceptIndicator.js'; -import { useSessionStats } from './contexts/SessionContext.js'; -import { useGitBranchName } from './hooks/useGitBranchName.js'; + +import * as fs from "node:fs"; +import { basename } from "node:path"; +import { requestConsentInteractive, requestConsentOrFail } from "../commands/extensions/consent.js"; +import { type LoadedSettings, SettingScope } from "../config/settings.js"; +import { type InitializationResult } from "../core/initializer.js"; +import { t } from "../i18n/index.js"; +import { migrateTomlCommands } from "../services/command-migration-tool.js"; +import { registerCleanup, runExitCleanup } from "../utils/cleanup.js"; +import { setUpdateHandler } from "../utils/handleAutoUpdate.js"; +import { clearScreen } from "../utils/stdioHelpers.js"; +import { computeWindowTitle } from "../utils/windowTitle.js"; +import { type CommandMigrationNudgeResult } from "./CommandFormatMigrationNudge.js"; +import { useTextBuffer } from "./components/shared/text-buffer.js"; +import { useAgentViewState } from "./contexts/AgentViewContext.js"; +import { useSessionStats } from "./contexts/SessionContext.js"; +import { ShellFocusContext } from "./contexts/ShellFocusContext.js"; +import { useAgentsManagerDialog } from "./hooks/useAgentsManagerDialog.js"; +import { useAttentionNotifications } from "./hooks/useAttentionNotifications.js"; +import { useAutoAcceptIndicator } from "./hooks/useAutoAcceptIndicator.js"; +import { useBracketedPaste } from "./hooks/useBracketedPaste.js"; +import { useCodingPlanUpdates } from "./hooks/useCodingPlanUpdates.js"; +import { useCommandMigration } from "./hooks/useCommandMigration.js"; +import { useDialogClose } from "./hooks/useDialogClose.js"; +import { useExtensionsManagerDialog } from "./hooks/useExtensionsManagerDialog.js"; import { - useExtensionUpdates, useConfirmUpdateRequests, - useSettingInputRequests, + useExtensionUpdates, usePluginChoiceRequests, -} from './hooks/useExtensionUpdates.js'; -import { useCodingPlanUpdates } from './hooks/useCodingPlanUpdates.js'; -import { ShellFocusContext } from './contexts/ShellFocusContext.js'; -import { useAgentViewState } from './contexts/AgentViewContext.js'; -import { t } from '../i18n/index.js'; -import { useWelcomeBack } from './hooks/useWelcomeBack.js'; -import { useDialogClose } from './hooks/useDialogClose.js'; -import { useInitializationAuthError } from './hooks/useInitializationAuthError.js'; -import { useSubagentCreateDialog } from './hooks/useSubagentCreateDialog.js'; -import { useAgentsManagerDialog } from './hooks/useAgentsManagerDialog.js'; -import { useExtensionsManagerDialog } from './hooks/useExtensionsManagerDialog.js'; -import { useMcpDialog } from './hooks/useMcpDialog.js'; -import { useHooksDialog } from './hooks/useHooksDialog.js'; -import { useAttentionNotifications } from './hooks/useAttentionNotifications.js'; -import { - requestConsentInteractive, - requestConsentOrFail, -} from '../commands/extensions/consent.js'; + useSettingInputRequests, +} from "./hooks/useExtensionUpdates.js"; +import { useFocus } from "./hooks/useFocus.js"; +import { useFolderTrust } from "./hooks/useFolderTrust.js"; +import { useGeminiStream } from "./hooks/useGeminiStream.js"; +import { useGitBranchName } from "./hooks/useGitBranchName.js"; +import { useHooksDialog } from "./hooks/useHooksDialog.js"; +import { useIdeTrustListener } from "./hooks/useIdeTrustListener.js"; +import { useInitializationAuthError } from "./hooks/useInitializationAuthError.js"; +import { type Key, useKeypress } from "./hooks/useKeypress.js"; +import { useLoadingIndicator } from "./hooks/useLoadingIndicator.js"; +import { useLogger } from "./hooks/useLogger.js"; +import { useMcpDialog } from "./hooks/useMcpDialog.js"; +import { useMessageQueue } from "./hooks/useMessageQueue.js"; +import { useSubagentCreateDialog } from "./hooks/useSubagentCreateDialog.js"; +import { useWelcomeBack } from "./hooks/useWelcomeBack.js"; +import { useVim } from "./hooks/vim.js"; +import { type IdeIntegrationNudgeResult } from "./IdeIntegrationNudge.js"; +import { Command, keyMatchers } from "./keyMatchers.js"; +import { isBtwCommand } from "./utils/commandUtils.js"; +import { type UpdateObject } from "./utils/updateCheck.js"; const CTRL_EXIT_PROMPT_DURATION_MS = 1000; -const debugLogger = createDebugLogger('APP_CONTAINER'); +const debugLogger = createDebugLogger("APP_CONTAINER"); function isToolExecuting(pendingHistoryItems: HistoryItemWithoutId[]) { return pendingHistoryItems.some((item) => { - if (item && item.type === 'tool_group') { - return item.tools.some( - (tool) => ToolCallStatus.Executing === tool.status, - ); + if (item && item.type === "tool_group") { + return item.tools.some((tool) => ToolCallStatus.Executing === tool.status); } return false; }); @@ -173,13 +157,9 @@ export const AppContainer = (props: AppContainerProps) => { const { settings, config, initializationResult } = props; const historyManager = useHistory(); useMemoryMonitor(historyManager); - const [debugMessage, setDebugMessage] = useState(''); - const [quittingMessages, setQuittingMessages] = useState< - HistoryItem[] | null - >(null); - const [themeError, setThemeError] = useState( - initializationResult.themeError, - ); + const [debugMessage, setDebugMessage] = useState(""); + const [quittingMessages, setQuittingMessages] = useState(null); + const [themeError, setThemeError] = useState(initializationResult.themeError); const [isProcessing, setIsProcessing] = useState(false); const [embeddedShellFocused, setEmbeddedShellFocused] = useState(false); @@ -187,8 +167,7 @@ export const AppContainer = (props: AppContainerProps) => { initializationResult.geminiMdFileCount, ); const [shellModeActive, setShellModeActive] = useState(false); - const [modelSwitchedFromQuotaError, setModelSwitchedFromQuotaError] = - useState(false); + const [modelSwitchedFromQuotaError, setModelSwitchedFromQuotaError] = useState(false); const [historyRemountKey, setHistoryRemountKey] = useState(0); const [updateInfo, setUpdateInfo] = useState(null); const [isTrustedFolder, setIsTrustedFolder] = useState( @@ -200,11 +179,9 @@ export const AppContainer = (props: AppContainerProps) => { const { addConfirmUpdateExtensionRequest, confirmUpdateExtensionRequests } = useConfirmUpdateRequests(); - const { addSettingInputRequest, settingInputRequests } = - useSettingInputRequests(); + const { addSettingInputRequest, settingInputRequests } = useSettingInputRequests(); - const { addPluginChoiceRequest, pluginChoiceRequests } = - usePluginChoiceRequests(); + const { addPluginChoiceRequest, pluginChoiceRequests } = usePluginChoiceRequests(); extensionManager.setRequestConsent( requestConsentOrFail.bind(null, (description) => @@ -225,7 +202,7 @@ export const AppContainer = (props: AppContainerProps) => { resolve(pluginName); }, onCancel: () => { - reject(new Error('Plugin selection cancelled')); + reject(new Error("Plugin selection cancelled")); }, }); }), @@ -242,38 +219,28 @@ export const AppContainer = (props: AppContainerProps) => { resolve(value); }, onCancel: () => { - reject(new Error('Setting input cancelled')); + reject(new Error("Setting input cancelled")); }, }); }), ); - const { - extensionsUpdateState, - extensionsUpdateStateInternal, - dispatchExtensionStateUpdate, - } = useExtensionUpdates( - extensionManager, + const { extensionsUpdateState, extensionsUpdateStateInternal, dispatchExtensionStateUpdate } = + useExtensionUpdates(extensionManager, historyManager.addItem, config.getWorkingDir()); + + const { codingPlanUpdateRequest, dismissCodingPlanUpdate } = useCodingPlanUpdates( + settings, + config, historyManager.addItem, - config.getWorkingDir(), ); - const { codingPlanUpdateRequest, dismissCodingPlanUpdate } = - useCodingPlanUpdates(settings, config, historyManager.addItem); - const [isTrustDialogOpen, setTrustDialogOpen] = useState(false); const openTrustDialog = useCallback(() => setTrustDialogOpen(true), []); const closeTrustDialog = useCallback(() => setTrustDialogOpen(false), []); const [isPermissionsDialogOpen, setPermissionsDialogOpen] = useState(false); - const openPermissionsDialog = useCallback( - () => setPermissionsDialogOpen(true), - [], - ); - const closePermissionsDialog = useCallback( - () => setPermissionsDialogOpen(false), - [], - ); + const openPermissionsDialog = useCallback(() => setPermissionsDialogOpen(true), []); + const closePermissionsDialog = useCallback(() => setPermissionsDialogOpen(false), []); // Helper to determine the current model (polled, since Config has no model-change event). const getCurrentModel = useCallback(() => config.getModel(), [config]); @@ -295,9 +262,7 @@ export const AppContainer = (props: AppContainerProps) => { const branchName = useGitBranchName(config.getTargetDir()); // Layout measurements const mainControlsRef = useRef(null); - const originalTitleRef = useRef( - computeWindowTitle(basename(config.getTargetDir())), - ); + const originalTitleRef = useRef(computeWindowTitle(basename(config.getTargetDir()))); const lastTitleRef = useRef(null); const staticExtraHeight = 3; @@ -311,10 +276,7 @@ export const AppContainer = (props: AppContainerProps) => { const resumedSessionData = config.getResumedSessionData(); if (resumedSessionData) { - const historyItems = buildResumedHistoryItems( - resumedSessionData, - config, - ); + const historyItems = buildResumedHistoryItems(resumedSessionData, config); historyManager.loadHistory(historyItems); } @@ -329,29 +291,25 @@ export const AppContainer = (props: AppContainerProps) => { hookSystem .fireSessionStartEvent( sessionStartSource, - config.getModel() ?? '', + config.getModel() ?? "", String(config.getApprovalMode()) as PermissionMode, ) .then(() => { - debugLogger.debug('SessionStart event completed successfully'); + debugLogger.debug("SessionStart event completed successfully"); }) .catch((err) => { debugLogger.warn(`SessionStart hook failed: ${err}`); }); } else { - debugLogger.debug( - 'SessionStart: HookSystem not available, skipping event', - ); + debugLogger.debug("SessionStart: HookSystem not available, skipping event"); } })(); // Register SessionEnd cleanup for process exit registerCleanup(async () => { try { - await config - .getHookSystem() - ?.fireSessionEndEvent(SessionEndReason.PromptInputExit); - debugLogger.debug('SessionEnd event completed successfully!!!'); + await config.getHookSystem()?.fireSessionEndEvent(SessionEndReason.PromptInputExit); + debugLogger.debug("SessionEnd event completed successfully!!!"); } catch (err) { debugLogger.error(`SessionEnd hook failed: ${err}`); } @@ -386,8 +344,7 @@ export const AppContainer = (props: AppContainerProps) => { // Derive widths for InputPrompt using shared helper const { inputWidth, suggestionsWidth } = useMemo(() => { - const { inputWidth, suggestionsWidth } = - calculatePromptWidths(terminalWidth); + const { inputWidth, suggestionsWidth } = calculatePromptWidths(terminalWidth); return { inputWidth, suggestionsWidth }; }, [terminalWidth]); // Uniform width for bordered box components: accounts for margins and caps at 100 @@ -403,7 +360,7 @@ export const AppContainer = (props: AppContainerProps) => { }, []); const buffer = useTextBuffer({ - initialText: '', + initialText: "", viewport: { height: 10, width: inputWidth }, stdin, setRawMode, @@ -416,17 +373,12 @@ export const AppContainer = (props: AppContainerProps) => { const pastMessagesRaw = (await logger?.getPreviousUserMessages()) || []; const currentSessionUserMessages = historyManager.history .filter( - (item): item is HistoryItem & { type: 'user'; text: string } => - item.type === 'user' && - typeof item.text === 'string' && - item.text.trim() !== '', + (item): item is HistoryItem & { type: "user"; text: string } => + item.type === "user" && typeof item.text === "string" && item.text.trim() !== "", ) .map((item) => item.text) .reverse(); - const combinedMessages = [ - ...currentSessionUserMessages, - ...pastMessagesRaw, - ]; + const combinedMessages = [...currentSessionUserMessages, ...pastMessagesRaw]; const deduplicatedMessages: string[] = []; if (combinedMessages.length > 0) { deduplicatedMessages.push(combinedMessages[0]); @@ -446,23 +398,16 @@ export const AppContainer = (props: AppContainerProps) => { setHistoryRemountKey((prev) => prev + 1); }, [setHistoryRemountKey, stdout]); - const { - isThemeDialogOpen, - openThemeDialog, - handleThemeSelect, - handleThemeHighlight, - } = useThemeCommand( - settings, - setThemeError, - historyManager.addItem, - initializationResult.themeError, - ); + const { isThemeDialogOpen, openThemeDialog, handleThemeSelect, handleThemeHighlight } = + useThemeCommand( + settings, + setThemeError, + historyManager.addItem, + initializationResult.themeError, + ); - const { - isApprovalModeDialogOpen, - openApprovalModeDialog, - handleApprovalModeSelect, - } = useApprovalModeCommand(settings, config); + const { isApprovalModeDialogOpen, openApprovalModeDialog, handleApprovalModeSelect } = + useApprovalModeCommand(settings, config); const { setAuthState, @@ -501,7 +446,7 @@ export const AppContainer = (props: AppContainerProps) => { ) { onAuthError( t( - 'Authentication is enforced to be {{enforcedType}}, but you are currently using {{currentType}}.', + "Authentication is enforced to be {{enforcedType}}, but you are currently using {{currentType}}.", { enforcedType: String(settings.merged.security?.auth.enforcedType), currentType: String(currentAuthType), @@ -526,57 +471,36 @@ export const AppContainer = (props: AppContainerProps) => { ]); const [editorError, setEditorError] = useState(null); - const { - isEditorDialogOpen, - openEditorDialog, - handleEditorSelect, - exitEditorDialog, - } = useEditorSettings(settings, setEditorError, historyManager.addItem); + const { isEditorDialogOpen, openEditorDialog, handleEditorSelect, exitEditorDialog } = + useEditorSettings(settings, setEditorError, historyManager.addItem); - const { isSettingsDialogOpen, openSettingsDialog, closeSettingsDialog } = - useSettingsCommand(); + const { isSettingsDialogOpen, openSettingsDialog, closeSettingsDialog } = useSettingsCommand(); - const { - isModelDialogOpen, - isFastModelMode, - openModelDialog, - closeModelDialog, - } = useModelCommand(); - const { activeArenaDialog, openArenaDialog, closeArenaDialog } = - useArenaCommand(); + const { isModelDialogOpen, isFastModelMode, openModelDialog, closeModelDialog } = + useModelCommand(); + const { activeArenaDialog, openArenaDialog, closeArenaDialog } = useArenaCommand(); - const { - isResumeDialogOpen, - openResumeDialog, - closeResumeDialog, - handleResume, - } = useResumeCommand({ - config, - historyManager, - startNewSession, - remount: refreshStatic, - }); + const { isResumeDialogOpen, openResumeDialog, closeResumeDialog, handleResume } = + useResumeCommand({ + config, + historyManager, + startNewSession, + remount: refreshStatic, + }); const { toggleVimEnabled } = useVimMode(); - const { - isSubagentCreateDialogOpen, - openSubagentCreateDialog, - closeSubagentCreateDialog, - } = useSubagentCreateDialog(); - const { - isAgentsManagerDialogOpen, - openAgentsManagerDialog, - closeAgentsManagerDialog, - } = useAgentsManagerDialog(); + const { isSubagentCreateDialogOpen, openSubagentCreateDialog, closeSubagentCreateDialog } = + useSubagentCreateDialog(); + const { isAgentsManagerDialogOpen, openAgentsManagerDialog, closeAgentsManagerDialog } = + useAgentsManagerDialog(); const { isExtensionsManagerDialogOpen, openExtensionsManagerDialog, closeExtensionsManagerDialog, } = useExtensionsManagerDialog(); const { isMcpDialogOpen, openMcpDialog, closeMcpDialog } = useMcpDialog(); - const { isHooksDialogOpen, openHooksDialog, closeHooksDialog } = - useHooksDialog(); + const { isHooksDialogOpen, openHooksDialog, closeHooksDialog } = useHooksDialog(); const slashCommandActions = useMemo( () => ({ @@ -667,7 +591,7 @@ export const AppContainer = (props: AppContainerProps) => { historyManager.addItem( { type: MessageType.INFO, - text: 'Refreshing hierarchical memory (AIRISCODE.md or other context files)...', + text: "Refreshing hierarchical memory (AIRISCODE.md or other context files)...", }, Date.now(), ); @@ -680,7 +604,7 @@ export const AppContainer = (props: AppContainerProps) => { config.getFileService(), config.getExtensionContextFilePaths(), config.isTrustedFolder(), - settings.merged.context?.importFormat || 'tree', // Use setting or default to 'tree' + settings.merged.context?.importFormat || "tree", // Use setting or default to 'tree' ); config.setUserMemory(memoryContent); @@ -693,16 +617,13 @@ export const AppContainer = (props: AppContainerProps) => { text: `Memory refreshed successfully. ${ memoryContent.length > 0 ? `Loaded ${memoryContent.length} characters from ${fileCount} file(s).` - : 'No memory content found.' + : "No memory content found." }`, }, Date.now(), ); debugLogger.debug( - `[DEBUG] Refreshed memory content in config: ${memoryContent.substring( - 0, - 200, - )}...`, + `[DEBUG] Refreshed memory content in config: ${memoryContent.substring(0, 200)}...`, ); } catch (error) { const errorMessage = getErrorMessage(error); @@ -713,7 +634,7 @@ export const AppContainer = (props: AppContainerProps) => { }, Date.now(), ); - debugLogger.error('Error refreshing memory:', error); + debugLogger.error("Error refreshing memory:", error); } }, [config, historyManager, settings.merged]); @@ -779,20 +700,15 @@ export const AppContainer = (props: AppContainerProps) => { addItem: historyManager.addItem, onApprovalModeChange: handleApprovalModeChange, shouldBlockTab: () => hasSuggestionsVisible, - disabled: agentViewState.activeView !== 'main', + disabled: agentViewState.activeView !== "main", }); - const { - messageQueue, - addMessage, - clearQueue, - getQueuedMessagesText, - drainQueue, - } = useMessageQueue({ - isConfigInitialized, - streamingState, - submitQuery, - }); + const { messageQueue, addMessage, clearQueue, getQueuedMessagesText, drainQueue } = + useMessageQueue({ + isConfigInitialized, + streamingState, + submitQuery, + }); // Bridge message queue to mid-turn drain via ref. // drainQueue reads from the synchronous queueRef inside useMessageQueue, @@ -803,17 +719,14 @@ export const AppContainer = (props: AppContainerProps) => { const handleFinalSubmit = useCallback( (submittedValue: string) => { // Route to active in-process agent if viewing a sub-agent tab. - if (agentViewState.activeView !== 'main') { + if (agentViewState.activeView !== "main") { const agent = agentViewState.agents.get(agentViewState.activeView); if (agent) { agent.interactiveAgent.enqueueMessage(submittedValue.trim()); return; } } - if ( - streamingState === StreamingState.Responding && - isBtwCommand(submittedValue) - ) { + if (streamingState === StreamingState.Responding && isBtwCommand(submittedValue)) { void submitQuery(submittedValue); return; } @@ -821,9 +734,9 @@ export const AppContainer = (props: AppContainerProps) => { // Check if speculation has results for this submission const spec = speculationRef.current; if ( - spec.status !== 'idle' && + spec.status !== "idle" && spec.suggestion === submittedValue && - spec.status === 'completed' + spec.status === "completed" ) { // Accept completed speculation: inject messages and apply files acceptSpeculation(spec, geminiClient) @@ -831,9 +744,8 @@ export const AppContainer = (props: AppContainerProps) => { logSpeculation( config, new SpeculationEvent({ - outcome: 'accepted', - turns_used: spec.messages.filter((m) => m.role === 'model') - .length, + outcome: "accepted", + turns_used: spec.messages.filter((m) => m.role === "model").length, files_written: result.filesApplied.length, tool_use_count: spec.toolUseCount, duration_ms: Date.now() - spec.startTime, @@ -848,61 +760,51 @@ export const AppContainer = (props: AppContainerProps) => { // Render each speculated message as the appropriate HistoryItem for (let mi = 0; mi < result.messages.length; mi++) { const msg = result.messages[mi]; - if (msg.role === 'user' && msg.parts) { + if (msg.role === "user" && msg.parts) { // Check if this is a tool result (functionResponse) or user text - const hasText = msg.parts.some( - (p) => p.text && !p.functionResponse, - ); + const hasText = msg.parts.some((p) => p.text && !p.functionResponse); if (hasText) { const text = msg.parts - .map((p) => p.text ?? '') + .map((p) => p.text ?? "") .filter(Boolean) - .join(''); + .join(""); if (text) { - historyManager.addItem( - { type: 'user' as const, text }, - now, - ); + historyManager.addItem({ type: "user" as const, text }, now); } } // functionResponse parts are rendered as part of the tool_group below - } else if (msg.role === 'model' && msg.parts) { + } else if (msg.role === "model" && msg.parts) { // Extract text and tool calls separately const textParts = msg.parts .filter((p) => p.text && !p.functionCall) .map((p) => p.text!) - .join(''); + .join(""); const toolCalls = msg.parts.filter((p) => p.functionCall); if (textParts) { - historyManager.addItem( - { type: 'gemini' as const, text: textParts }, - now, - ); + historyManager.addItem({ type: "gemini" as const, text: textParts }, now); } if (toolCalls.length > 0) { // Find matching tool results from the next message const nextMsg = result.messages[mi + 1]; - const toolResults = - nextMsg?.parts?.filter((p) => p.functionResponse) ?? []; + const toolResults = nextMsg?.parts?.filter((p) => p.functionResponse) ?? []; const tools = toolCalls.map((tc, i) => { - const name = tc.functionCall?.name ?? 'unknown'; + const name = tc.functionCall?.name ?? "unknown"; const args = tc.functionCall?.args ?? {}; const resp = toolResults[i]?.functionResponse?.response; const resultText = - typeof resp === 'object' && resp - ? ((resp as Record)['output'] ?? - JSON.stringify(resp)) - : String(resp ?? ''); + typeof resp === "object" && resp + ? ((resp as Record)["output"] ?? JSON.stringify(resp)) + : String(resp ?? ""); return { callId: `spec-${name}-${i}`, name, description: Object.entries(args) .map(([k, v]) => `${k}: ${String(v).slice(0, 80)}`) - .join(', ') || name, + .join(", ") || name, resultDisplay: String(resultText).slice(0, 500), status: ToolCallStatus.Success, confirmationDetails: undefined, @@ -910,7 +812,7 @@ export const AppContainer = (props: AppContainerProps) => { }); const toolGroupItem: HistoryItemWithoutId = { - type: 'tool_group' as const, + type: "tool_group" as const, tools, }; historyManager.addItem(toolGroupItem, now); @@ -931,27 +833,19 @@ export const AppContainer = (props: AppContainerProps) => { } // Abort any running speculation since we're submitting something different - if (spec.status === 'running') { + if (spec.status === "running") { abortSpeculation(spec).catch(() => {}); speculationRef.current = IDLE_SPECULATION; } addMessage(submittedValue); }, - [ - addMessage, - agentViewState, - streamingState, - submitQuery, - config, - geminiClient, - historyManager, - ], + [addMessage, agentViewState, streamingState, submitQuery, config, geminiClient, historyManager], ); const handleArenaModelsSelected = useCallback( (models: string[]) => { - const value = models.join(','); + const value = models.join(","); buffer.setText(`/arena start --models ${value} `); closeArenaDialog(); }, @@ -973,17 +867,14 @@ export const AppContainer = (props: AppContainerProps) => { ); cancelHandlerRef.current = useCallback(() => { - const pendingHistoryItems = [ - ...pendingSlashCommandHistoryItems, - ...pendingGeminiHistoryItems, - ]; + const pendingHistoryItems = [...pendingSlashCommandHistoryItems, ...pendingGeminiHistoryItems]; if (isToolExecuting(pendingHistoryItems)) { - buffer.setText(''); // Just clear the prompt + buffer.setText(""); // Just clear the prompt return; } const lastUserMessage = userMessages.at(-1); - let textToSet = lastUserMessage || ''; + let textToSet = lastUserMessage || ""; const queuedText = getQueuedMessagesText(); if (queuedText) { @@ -1022,8 +913,7 @@ export const AppContainer = (props: AppContainerProps) => { const isInputActive = !initError && !isProcessing && - (streamingState === StreamingState.Idle || - streamingState === StreamingState.Responding); + (streamingState === StreamingState.Idle || streamingState === StreamingState.Responding); const [controlsHeight, setControlsHeight] = useState(0); @@ -1051,10 +941,7 @@ export const AppContainer = (props: AppContainerProps) => { config.setShellExecutionConfig({ terminalWidth: Math.floor(terminalWidth * SHELL_WIDTH_FRACTION), - terminalHeight: Math.max( - Math.floor(availableTerminalHeight - SHELL_HEIGHT_PADDING), - 1, - ), + terminalHeight: Math.max(Math.floor(availableTerminalHeight - SHELL_HEIGHT_PADDING), 1), pager: settings.merged.tools?.shell?.pager, showColor: settings.merged.tools?.shell?.showColor, }); @@ -1095,7 +982,7 @@ export const AppContainer = (props: AppContainerProps) => { !isThemeDialogOpen && !isEditorDialogOpen && !showWelcomeBackDialog && - welcomeBackChoice !== 'restart' && + welcomeBackChoice !== "restart" && geminiClient?.isInitialized?.() ) { handleFinalSubmit(initialPrompt); @@ -1115,15 +1002,14 @@ export const AppContainer = (props: AppContainerProps) => { ]); // Generate prompt suggestions when streaming completes - const followupSuggestionsEnabled = - settings.merged.ui?.enableFollowupSuggestions === true; + const followupSuggestionsEnabled = settings.merged.ui?.enableFollowupSuggestions === true; useEffect(() => { // Clear suggestion when feature is disabled at runtime if (!followupSuggestionsEnabled) { suggestionAbortRef.current?.abort(); setPromptSuggestion(null); - if (speculationRef.current.status === 'running') { + if (speculationRef.current.status === "running") { abortSpeculation(speculationRef.current).catch(() => {}); speculationRef.current = IDLE_SPECULATION; } @@ -1136,7 +1022,7 @@ export const AppContainer = (props: AppContainerProps) => { ) { suggestionAbortRef.current?.abort(); setPromptSuggestion(null); - if (speculationRef.current.status !== 'idle') { + if (speculationRef.current.status !== "idle") { abortSpeculation(speculationRef.current).catch(() => {}); speculationRef.current = IDLE_SPECULATION; } @@ -1152,9 +1038,8 @@ export const AppContainer = (props: AppContainerProps) => { streamingState === StreamingState.Idle && // Check both committed history and pending items for errors // (API errors go to pendingGeminiHistoryItems, not historyManager.history) - historyManager.history[historyManager.history.length - 1]?.type !== - 'error' && - !pendingGeminiHistoryItems.some((item) => item.type === 'error') && + historyManager.history[historyManager.history.length - 1]?.type !== "error" && + !pendingGeminiHistoryItems.some((item) => item.type === "error") && !shellConfirmationRequest && !confirmationRequest && !loopDetectionConfirmationRequest && @@ -1167,8 +1052,7 @@ export const AppContainer = (props: AppContainerProps) => { // Use curated history to avoid invalid/empty entries causing API errors const fullHistory = geminiClient.getChat().getHistory(true); - const conversationHistory = - fullHistory.length > 40 ? fullHistory.slice(-40) : fullHistory; + const conversationHistory = fullHistory.length > 40 ? fullHistory.slice(-40) : fullHistory; generatePromptSuggestion(config, conversationHistory, ac.signal, { enableCacheSharing: settings.merged.ui?.enableCacheSharing === true, model: settings.merged.fastModel || undefined, @@ -1194,7 +1078,7 @@ export const AppContainer = (props: AppContainerProps) => { logPromptSuggestion( config, new PromptSuggestionEvent({ - outcome: 'suppressed', + outcome: "suppressed", reason: result.filterReason, }), ); @@ -1214,7 +1098,7 @@ export const AppContainer = (props: AppContainerProps) => { return () => { suggestionAbortRef.current?.abort(); // Cleanup speculation on unmount (#21) - if (speculationRef.current.status !== 'idle') { + if (speculationRef.current.status !== "idle") { abortSpeculation(speculationRef.current).catch(() => {}); speculationRef.current = IDLE_SPECULATION; } @@ -1234,7 +1118,7 @@ export const AppContainer = (props: AppContainerProps) => { // user-initiated dismiss via typing/paste). InputPrompt calls onPromptSuggestionDismiss // on user input, which clears promptSuggestion, triggering this effect to abort speculation. useEffect(() => { - if (!promptSuggestion && speculationRef.current.status !== 'idle') { + if (!promptSuggestion && speculationRef.current.status !== "idle") { abortSpeculation(speculationRef.current).catch(() => {}); speculationRef.current = IDLE_SPECULATION; } @@ -1252,10 +1136,7 @@ export const AppContainer = (props: AppContainerProps) => { getIde(); }, []); const shouldShowIdePrompt = Boolean( - currentIDE && - !config.getIdeMode() && - !settings.merged.ide?.hasSeenNudge && - !idePromptAnswered, + currentIDE && !config.getIdeMode() && !settings.merged.ide?.hasSeenNudge && !idePromptAnswered, ); // Command migration nudge @@ -1265,15 +1146,10 @@ export const AppContainer = (props: AppContainerProps) => { setShowMigrationNudge: setShowCommandMigrationNudge, } = useCommandMigration(settings, config.storage); - const [showToolDescriptions, setShowToolDescriptions] = - useState(false); + const [showToolDescriptions, setShowToolDescriptions] = useState(false); - const [verboseMode, setVerboseMode] = useState( - settings.merged.ui?.verboseMode ?? true, - ); - const [frozenSnapshot, setFrozenSnapshot] = useState< - HistoryItemWithoutId[] | null - >(null); + const [verboseMode, setVerboseMode] = useState(settings.merged.ui?.verboseMode ?? true); + const [frozenSnapshot, setFrozenSnapshot] = useState(null); const [ctrlCPressedOnce, setCtrlCPressedOnce] = useState(false); const ctrlCTimerRef = useRef(null); @@ -1283,9 +1159,7 @@ export const AppContainer = (props: AppContainerProps) => { const escapeTimerRef = useRef(null); const dialogsVisibleRef = useRef(false); const [constrainHeight, setConstrainHeight] = useState(true); - const [ideContextState, setIdeContextState] = useState< - IdeContext | undefined - >(); + const [ideContextState, setIdeContextState] = useState(); const [showEscapePrompt, setShowEscapePrompt] = useState(false); const [showIdeRestartPrompt, setShowIdeRestartPrompt] = useState(false); @@ -1301,12 +1175,12 @@ export const AppContainer = (props: AppContainerProps) => { } }, [streamingState]); - const { isFolderTrustDialogOpen, handleFolderTrustSelect, isRestarting } = - useFolderTrust(settings, setIsTrustedFolder); - const { - needsRestart: ideNeedsRestart, - restartReason: ideTrustRestartReason, - } = useIdeTrustListener(); + const { isFolderTrustDialogOpen, handleFolderTrustSelect, isRestarting } = useFolderTrust( + settings, + setIsTrustedFolder, + ); + const { needsRestart: ideNeedsRestart, restartReason: ideTrustRestartReason } = + useIdeTrustListener(); const isInitialMount = useRef(true); useEffect(() => { @@ -1343,16 +1217,16 @@ export const AppContainer = (props: AppContainerProps) => { const handleIdePromptComplete = useCallback( (result: IdeIntegrationNudgeResult) => { - if (result.userSelection === 'yes') { + if (result.userSelection === "yes") { // Check whether the extension has been pre-installed if (result.isExtensionPreInstalled) { - handleSlashCommand('/ide enable'); + handleSlashCommand("/ide enable"); } else { - handleSlashCommand('/ide install'); + handleSlashCommand("/ide install"); } - settings.setValue(SettingScope.User, 'ide.hasSeenNudge', true); - } else if (result.userSelection === 'dismiss') { - settings.setValue(SettingScope.User, 'ide.hasSeenNudge', true); + settings.setValue(SettingScope.User, "ide.hasSeenNudge", true); + } else if (result.userSelection === "dismiss") { + settings.setValue(SettingScope.User, "ide.hasSeenNudge", true); } setIdePromptAnswered(true); }, @@ -1363,7 +1237,7 @@ export const AppContainer = (props: AppContainerProps) => { async (result: CommandMigrationNudgeResult) => { setShowCommandMigrationNudge(false); - if (result.userSelection === 'yes') { + if (result.userSelection === "yes") { // Perform migration for both workspace and user levels try { const results = []; @@ -1375,11 +1249,8 @@ export const AppContainer = (props: AppContainerProps) => { createBackup: true, deleteOriginal: false, }); - if ( - workspaceResult.convertedFiles.length > 0 || - workspaceResult.failedFiles.length > 0 - ) { - results.push({ level: 'workspace', result: workspaceResult }); + if (workspaceResult.convertedFiles.length > 0 || workspaceResult.failedFiles.length > 0) { + results.push({ level: "workspace", result: workspaceResult }); } // Migrate user commands @@ -1389,23 +1260,17 @@ export const AppContainer = (props: AppContainerProps) => { createBackup: true, deleteOriginal: false, }); - if ( - userResult.convertedFiles.length > 0 || - userResult.failedFiles.length > 0 - ) { - results.push({ level: 'user', result: userResult }); + if (userResult.convertedFiles.length > 0 || userResult.failedFiles.length > 0) { + results.push({ level: "user", result: userResult }); } // Report results for (const { level, result: migrationResult } of results) { - if ( - migrationResult.success && - migrationResult.convertedFiles.length > 0 - ) { + if (migrationResult.success && migrationResult.convertedFiles.length > 0) { historyManager.addItem( { type: MessageType.INFO, - text: `[${level}] Successfully migrated ${migrationResult.convertedFiles.length} command file${migrationResult.convertedFiles.length > 1 ? 's' : ''} to Markdown format. Original files backed up as .toml.backup`, + text: `[${level}] Successfully migrated ${migrationResult.convertedFiles.length} command file${migrationResult.convertedFiles.length > 1 ? "s" : ""} to Markdown format. Original files backed up as .toml.backup`, }, Date.now(), ); @@ -1415,7 +1280,7 @@ export const AppContainer = (props: AppContainerProps) => { historyManager.addItem( { type: MessageType.ERROR, - text: `[${level}] Failed to migrate ${migrationResult.failedFiles.length} file${migrationResult.failedFiles.length > 1 ? 's' : ''}:\n${migrationResult.failedFiles.map((f) => ` • ${f.file}: ${f.error}`).join('\n')}`, + text: `[${level}] Failed to migrate ${migrationResult.failedFiles.length} file${migrationResult.failedFiles.length > 1 ? "s" : ""}:\n${migrationResult.failedFiles.map((f) => ` • ${f.file}: ${f.error}`).join("\n")}`, }, Date.now(), ); @@ -1426,7 +1291,7 @@ export const AppContainer = (props: AppContainerProps) => { historyManager.addItem( { type: MessageType.INFO, - text: 'No TOML files found to migrate.', + text: "No TOML files found to migrate.", }, Date.now(), ); @@ -1445,16 +1310,16 @@ export const AppContainer = (props: AppContainerProps) => { [historyManager, setShowCommandMigrationNudge, config.storage], ); - const currentCandidatesTokens = Object.values( - sessionStats.metrics?.models ?? {}, - ).reduce((acc, model) => acc + (model.tokens?.candidates ?? 0), 0); + const currentCandidatesTokens = Object.values(sessionStats.metrics?.models ?? {}).reduce( + (acc, model) => acc + (model.tokens?.candidates ?? 0), + 0, + ); - const { elapsedTime, currentLoadingPhrase, taskStartTokens } = - useLoadingIndicator( - streamingState, - settings.merged.ui?.customWittyPhrases, - currentCandidatesTokens, - ); + const { elapsedTime, currentLoadingPhrase, taskStartTokens } = useLoadingIndicator( + streamingState, + settings.merged.ui?.customWittyPhrases, + currentCandidatesTokens, + ); useAttentionNotifications({ isFocused, @@ -1496,7 +1361,7 @@ export const AppContainer = (props: AppContainerProps) => { clearTimeout(timerRef.current); } // Exit directly - handleSlashCommand('/quit'); + handleSlashCommand("/quit"); return; } @@ -1535,7 +1400,7 @@ export const AppContainer = (props: AppContainerProps) => { // 5. Clear input buffer (if has content) if (buffer.text.length > 0) { - buffer.setText(''); + buffer.setText(""); return; // Input cleared, end processing } @@ -1561,7 +1426,7 @@ export const AppContainer = (props: AppContainerProps) => { (key: Key) => { // Debug log keystrokes if enabled if (settings.merged.general?.debugKeystrokeLogging) { - debugLogger.debug('[DEBUG] Keystroke:', JSON.stringify(key)); + debugLogger.debug("[DEBUG] Keystroke:", JSON.stringify(key)); } if (keyMatchers[Command.QUIT](key)) { @@ -1609,7 +1474,7 @@ export const AppContainer = (props: AppContainerProps) => { if (buffer.text.length > 0) { if (escapePressedOnce) { // Second press: clear input, keep the flag to allow immediate cancel - buffer.setText(''); + buffer.setText(""); return; } // First press: set flag and show prompt @@ -1649,7 +1514,7 @@ export const AppContainer = (props: AppContainerProps) => { !dialogsVisibleRef.current && buffer.text.length === 0 ) { - if (key.name === 'return' || key.sequence === ' ') { + if (key.name === "return" || key.sequence === " ") { setBtwItem(null); return; } @@ -1670,18 +1535,15 @@ export const AppContainer = (props: AppContainerProps) => { const mcpServers = config.getMcpServers(); if (Object.keys(mcpServers || {}).length > 0) { - handleSlashCommand(newValue ? '/mcp desc' : '/mcp nodesc'); + handleSlashCommand(newValue ? "/mcp desc" : "/mcp nodesc"); } } else if ( keyMatchers[Command.TOGGLE_IDE_CONTEXT_DETAIL](key) && config.getIdeMode() && ideContextState ) { - handleSlashCommand('/ide status'); - } else if ( - keyMatchers[Command.SHOW_MORE_LINES](key) && - !enteringConstrainHeightMode - ) { + handleSlashCommand("/ide status"); + } else if (keyMatchers[Command.SHOW_MORE_LINES](key) && !enteringConstrainHeightMode) { setConstrainHeight(false); } else if (keyMatchers[Command.TOGGLE_SHELL_INPUT_FOCUS](key)) { if (activePtyId || embeddedShellFocused) { @@ -1690,7 +1552,7 @@ export const AppContainer = (props: AppContainerProps) => { } else if (keyMatchers[Command.TOGGLE_VERBOSE_MODE](key)) { const newValue = !verboseMode; setVerboseMode(newValue); - void settings.setValue(SettingScope.User, 'ui.verboseMode', newValue); + void settings.setValue(SettingScope.User, "ui.verboseMode", newValue); refreshStatic(); // Only freeze during the actual responding phase. WaitingForConfirmation // must keep focus so the user can approve/cancel tool confirmation UI. @@ -1745,24 +1607,18 @@ export const AppContainer = (props: AppContainerProps) => { // Update terminal title with AIRIS Code status and thoughts useEffect(() => { // Respect both showStatusInTitle and hideWindowTitle settings - if ( - !settings.merged.ui?.showStatusInTitle || - settings.merged.ui?.hideWindowTitle - ) - return; + if (!settings.merged.ui?.showStatusInTitle || settings.merged.ui?.hideWindowTitle) return; let title; if (streamingState === StreamingState.Idle) { title = originalTitleRef.current; } else { - const statusText = thought?.subject - ?.replace(/[\r\n]+/g, ' ') - .substring(0, 80); + const statusText = thought?.subject?.replace(/[\r\n]+/g, " ").substring(0, 80); title = statusText || originalTitleRef.current; } // Pad the title to a fixed width to prevent taskbar icon resizing. - const paddedTitle = title.padEnd(80, ' '); + const paddedTitle = title.padEnd(80, " "); // Only update the title if it's different from the last value we set if (lastTitleRef.current !== paddedTitle) { @@ -1778,7 +1634,7 @@ export const AppContainer = (props: AppContainerProps) => { stdout, ]); - const nightly = props.version.includes('nightly'); + const nightly = props.version.includes("nightly"); const dialogsVisible = showWelcomeBackDialog || diff --git a/apps/airiscode-cli/src/ui/CommandFormatMigrationNudge.tsx b/apps/airiscode-cli/src/ui/CommandFormatMigrationNudge.tsx index 5e1070e86..07ae913ae 100644 --- a/apps/airiscode-cli/src/ui/CommandFormatMigrationNudge.tsx +++ b/apps/airiscode-cli/src/ui/CommandFormatMigrationNudge.tsx @@ -4,15 +4,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { Box, Text } from 'ink'; -import type { RadioSelectItem } from './components/shared/RadioButtonSelect.js'; -import { RadioButtonSelect } from './components/shared/RadioButtonSelect.js'; -import { useKeypress } from './hooks/useKeypress.js'; -import { theme } from './semantic-colors.js'; -import { t } from '../i18n/index.js'; +import { Box, Text } from "ink"; +import { t } from "../i18n/index.js"; +import type { RadioSelectItem } from "./components/shared/RadioButtonSelect.js"; +import { RadioButtonSelect } from "./components/shared/RadioButtonSelect.js"; +import { useKeypress } from "./hooks/useKeypress.js"; +import { theme } from "./semantic-colors.js"; export type CommandMigrationNudgeResult = { - userSelection: 'yes' | 'no'; + userSelection: "yes" | "no"; }; interface CommandFormatMigrationNudgeProps { @@ -26,9 +26,9 @@ export function CommandFormatMigrationNudge({ }: CommandFormatMigrationNudgeProps) { useKeypress( (key) => { - if (key.name === 'escape') { + if (key.name === "escape") { onComplete({ - userSelection: 'no', + userSelection: "no", }); } }, @@ -37,26 +37,26 @@ export function CommandFormatMigrationNudge({ const OPTIONS: Array> = [ { - label: t('Yes'), + label: t("Yes"), value: { - userSelection: 'yes', + userSelection: "yes", }, - key: 'Yes', + key: "Yes", }, { - label: t('No (esc)'), + label: t("No (esc)"), value: { - userSelection: 'no', + userSelection: "no", }, - key: 'No (esc)', + key: "No (esc)", }, ]; const count = tomlFiles.length; const fileList = count <= 3 - ? tomlFiles.map((f) => ` • ${f}`).join('\n') - : ` • ${tomlFiles.slice(0, 2).join('\n • ')}\n • ${t('... and {{count}} more', { count: String(count - 2) })}`; + ? tomlFiles.map((f) => ` • ${f}`).join("\n") + : ` • ${tomlFiles.slice(0, 2).join("\n • ")}\n • ${t("... and {{count}} more", { count: String(count - 2) })}`; return ( - {'⚠️ '} - {t('Command Format Migration')} + {"⚠️ "} + {t("Command Format Migration")} {count > 1 - ? t('Found {{count}} TOML command files:', { count: String(count) }) - : t('Found {{count}} TOML command file:', { count: String(count) })} + ? t("Found {{count}} TOML command files:", { count: String(count) }) + : t("Found {{count}} TOML command file:", { count: String(count) })} {fileList} - {''} + {""} - {t( - 'The TOML format is deprecated. Would you like to migrate them to Markdown format?', - )} + {t("The TOML format is deprecated. Would you like to migrate them to Markdown format?")} - {t('(Backups will be created and original files will be preserved)')} + {t("(Backups will be created and original files will be preserved)")} diff --git a/apps/airiscode-cli/src/ui/FeedbackDialog.tsx b/apps/airiscode-cli/src/ui/FeedbackDialog.tsx index 6482e1592..19c7c9a49 100644 --- a/apps/airiscode-cli/src/ui/FeedbackDialog.tsx +++ b/apps/airiscode-cli/src/ui/FeedbackDialog.tsx @@ -1,9 +1,9 @@ -import { Box, Text } from 'ink'; -import type React from 'react'; -import { t } from '../i18n/index.js'; -import { useUIActions } from './contexts/UIActionsContext.js'; -import { useUIState } from './contexts/UIStateContext.js'; -import { useKeypress } from './hooks/useKeypress.js'; +import { Box, Text } from "ink"; +import type React from "react"; +import { t } from "../i18n/index.js"; +import { useUIActions } from "./contexts/UIActionsContext.js"; +import { useUIState } from "./contexts/UIStateContext.js"; +import { useKeypress } from "./hooks/useKeypress.js"; export const FEEDBACK_OPTIONS = { GOOD: 1, @@ -13,13 +13,13 @@ export const FEEDBACK_OPTIONS = { } as const; const FEEDBACK_OPTION_KEYS = { - [FEEDBACK_OPTIONS.GOOD]: '1', - [FEEDBACK_OPTIONS.BAD]: '2', - [FEEDBACK_OPTIONS.FINE]: '3', - [FEEDBACK_OPTIONS.DISMISS]: '0', + [FEEDBACK_OPTIONS.GOOD]: "1", + [FEEDBACK_OPTIONS.BAD]: "2", + [FEEDBACK_OPTIONS.FINE]: "3", + [FEEDBACK_OPTIONS.DISMISS]: "0", } as const; -export const FEEDBACK_DIALOG_KEYS = ['1', '2', '3', '0'] as const; +export const FEEDBACK_DIALOG_KEYS = ["1", "2", "3", "0"] as const; export const FeedbackDialog: React.FC = () => { const uiState = useUIState(); @@ -48,26 +48,20 @@ export const FeedbackDialog: React.FC = () => { - {t('How is Qwen doing this session? (optional)')} + {t("How is Qwen doing this session? (optional)")} - - {FEEDBACK_OPTION_KEYS[FEEDBACK_OPTIONS.GOOD]}:{' '} - - {t('Good')} + {FEEDBACK_OPTION_KEYS[FEEDBACK_OPTIONS.GOOD]}: + {t("Good")} {FEEDBACK_OPTION_KEYS[FEEDBACK_OPTIONS.BAD]}: - {t('Bad')} + {t("Bad")} - - {FEEDBACK_OPTION_KEYS[FEEDBACK_OPTIONS.FINE]}:{' '} - - {t('Fine')} + {FEEDBACK_OPTION_KEYS[FEEDBACK_OPTIONS.FINE]}: + {t("Fine")} - - {FEEDBACK_OPTION_KEYS[FEEDBACK_OPTIONS.DISMISS]}:{' '} - - {t('Dismiss')} + {FEEDBACK_OPTION_KEYS[FEEDBACK_OPTIONS.DISMISS]}: + {t("Dismiss")} diff --git a/apps/airiscode-cli/src/ui/IdeIntegrationNudge.tsx b/apps/airiscode-cli/src/ui/IdeIntegrationNudge.tsx index 2d758fbea..1416a37f9 100644 --- a/apps/airiscode-cli/src/ui/IdeIntegrationNudge.tsx +++ b/apps/airiscode-cli/src/ui/IdeIntegrationNudge.tsx @@ -4,15 +4,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { IdeInfo } from '@airiscode/core'; -import { Box, Text } from 'ink'; -import type { RadioSelectItem } from './components/shared/RadioButtonSelect.js'; -import { RadioButtonSelect } from './components/shared/RadioButtonSelect.js'; -import { useKeypress } from './hooks/useKeypress.js'; -import { theme } from './semantic-colors.js'; +import type { IdeInfo } from "@airiscode/core"; +import { Box, Text } from "ink"; +import type { RadioSelectItem } from "./components/shared/RadioButtonSelect.js"; +import { RadioButtonSelect } from "./components/shared/RadioButtonSelect.js"; +import { useKeypress } from "./hooks/useKeypress.js"; +import { theme } from "./semantic-colors.js"; export type IdeIntegrationNudgeResult = { - userSelection: 'yes' | 'no' | 'dismiss'; + userSelection: "yes" | "no" | "dismiss"; isExtensionPreInstalled: boolean; }; @@ -21,15 +21,12 @@ interface IdeIntegrationNudgeProps { onComplete: (result: IdeIntegrationNudgeResult) => void; } -export function IdeIntegrationNudge({ - ide, - onComplete, -}: IdeIntegrationNudgeProps) { +export function IdeIntegrationNudge({ ide, onComplete }: IdeIntegrationNudgeProps) { useKeypress( (key) => { - if (key.name === 'escape') { + if (key.name === "escape") { onComplete({ - userSelection: 'no', + userSelection: "no", isExtensionPreInstalled: false, }); } @@ -38,33 +35,32 @@ export function IdeIntegrationNudge({ ); const { displayName: ideName } = ide; - const isInSandbox = !!process.env['SANDBOX']; + const isInSandbox = !!process.env["SANDBOX"]; // Assume extension is already installed if the env variables are set. const isExtensionPreInstalled = - !!process.env['AIRISCODE_IDE_SERVER_PORT'] && - !!process.env['AIRISCODE_IDE_WORKSPACE_PATH']; + !!process.env["AIRISCODE_IDE_SERVER_PORT"] && !!process.env["AIRISCODE_IDE_WORKSPACE_PATH"]; const OPTIONS: Array> = [ { - label: 'Yes', + label: "Yes", value: { - userSelection: 'yes', + userSelection: "yes", isExtensionPreInstalled, }, - key: 'Yes', + key: "Yes", }, { - label: 'No (esc)', + label: "No (esc)", value: { - userSelection: 'no', + userSelection: "no", isExtensionPreInstalled, }, - key: 'No (esc)', + key: "No (esc)", }, { label: "No, don't ask again", value: { - userSelection: 'dismiss', + userSelection: "dismiss", isExtensionPreInstalled, }, key: "No, don't ask again", @@ -75,10 +71,10 @@ export function IdeIntegrationNudge({ ? `Note: In sandbox environments, IDE integration requires manual setup on the host system. If you select Yes, you'll receive instructions on how to set this up.` : isExtensionPreInstalled ? `If you select Yes, the CLI will connect to your ${ - ideName ?? 'editor' + ideName ?? "editor" } and have access to your open files and display diffs directly.` : `If you select Yes, we'll install an extension that allows the CLI to access your open files and display diffs directly in ${ - ideName ?? 'your editor' + ideName ?? "your editor" }.`; return ( @@ -92,8 +88,8 @@ export function IdeIntegrationNudge({ > - {'> '} - {`Do you want to connect ${ideName ?? 'your editor'} to AIRIS Code?`} + {"> "} + {`Do you want to connect ${ideName ?? "your editor"} to AIRIS Code?`} {installText} diff --git a/apps/airiscode-cli/src/ui/auth/AuthDialog.tsx b/apps/airiscode-cli/src/ui/auth/AuthDialog.tsx index b8fe44574..94528c799 100644 --- a/apps/airiscode-cli/src/ui/auth/AuthDialog.tsx +++ b/apps/airiscode-cli/src/ui/auth/AuthDialog.tsx @@ -4,71 +4,60 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type React from 'react'; -import { useState } from 'react'; -import { AuthType } from '@airiscode/core'; -import { Box, Text } from 'ink'; -import Link from 'ink-link'; -import { theme } from '../semantic-colors.js'; -import { useKeypress } from '../hooks/useKeypress.js'; -import { DescriptiveRadioButtonSelect } from '../components/shared/DescriptiveRadioButtonSelect.js'; -import { ApiKeyInput } from '../components/ApiKeyInput.js'; -import { TextInput } from '../components/shared/TextInput.js'; -import { useUIState } from '../contexts/UIStateContext.js'; -import { useUIActions } from '../contexts/UIActionsContext.js'; -import { useConfig } from '../contexts/ConfigContext.js'; -import { t } from '../../i18n/index.js'; -import { - CodingPlanRegion, - isCodingPlanConfig, -} from '../../constants/codingPlan.js'; +import { AuthType } from "@airiscode/core"; +import { Box, Text } from "ink"; +import Link from "ink-link"; +import type React from "react"; +import { useState } from "react"; import { ALIBABA_STANDARD_API_KEY_ENDPOINTS, type AlibabaStandardRegion, -} from '../../constants/alibabaStandardApiKey.js'; +} from "../../constants/alibabaStandardApiKey.js"; +import { CodingPlanRegion, isCodingPlanConfig } from "../../constants/codingPlan.js"; +import { t } from "../../i18n/index.js"; +import { ApiKeyInput } from "../components/ApiKeyInput.js"; +import { DescriptiveRadioButtonSelect } from "../components/shared/DescriptiveRadioButtonSelect.js"; +import { TextInput } from "../components/shared/TextInput.js"; +import { useConfig } from "../contexts/ConfigContext.js"; +import { useUIActions } from "../contexts/UIActionsContext.js"; +import { useUIState } from "../contexts/UIStateContext.js"; +import { useKeypress } from "../hooks/useKeypress.js"; +import { theme } from "../semantic-colors.js"; const MODEL_PROVIDERS_DOCUMENTATION_URL = - 'https://qwenlm.github.io/airiscode-docs/en/users/configuration/model-providers/'; - -function parseDefaultAuthType( - defaultAuthType: string | undefined, -): AuthType | null { - if ( - defaultAuthType && - Object.values(AuthType).includes(defaultAuthType as AuthType) - ) { + "https://qwenlm.github.io/airiscode-docs/en/users/configuration/model-providers/"; + +function parseDefaultAuthType(defaultAuthType: string | undefined): AuthType | null { + if (defaultAuthType && Object.values(AuthType).includes(defaultAuthType as AuthType)) { return defaultAuthType as AuthType; } return null; } // Main menu option type -type MainOption = 'CODING_PLAN' | 'API_KEY'; -type ApiKeyOption = 'ALIBABA_STANDARD_API_KEY' | 'CUSTOM_API_KEY'; +type MainOption = "CODING_PLAN" | "API_KEY"; +type ApiKeyOption = "ALIBABA_STANDARD_API_KEY" | "CUSTOM_API_KEY"; // View level for navigation type ViewLevel = - | 'main' - | 'region-select' - | 'api-key-input' - | 'api-key-type-select' - | 'alibaba-standard-region-select' - | 'alibaba-standard-api-key-input' - | 'alibaba-standard-model-id-input' - | 'custom-info'; - -const ALIBABA_STANDARD_MODEL_IDS_PLACEHOLDER = 'qwen3.5-plus,glm-5,kimi-k2.5'; -const ALIBABA_STANDARD_API_DOCUMENTATION_URLS: Record< - AlibabaStandardRegion, - string -> = { - 'cn-beijing': 'https://bailian.console.aliyun.com/cn-beijing?tab=api#/api', - 'sg-singapore': - 'https://modelstudio.console.alibabacloud.com/ap-southeast-1?tab=api#/api/?type=model&url=2712195', - 'us-virginia': - 'https://modelstudio.console.alibabacloud.com/us-east-1?tab=api#/api/?type=model&url=2712195', - 'cn-hongkong': - 'https://modelstudio.console.alibabacloud.com/cn-hongkong?tab=api#/api/?type=model&url=2712195', + | "main" + | "region-select" + | "api-key-input" + | "api-key-type-select" + | "alibaba-standard-region-select" + | "alibaba-standard-api-key-input" + | "alibaba-standard-model-id-input" + | "custom-info"; + +const ALIBABA_STANDARD_MODEL_IDS_PLACEHOLDER = "qwen3.5-plus,glm-5,kimi-k2.5"; +const ALIBABA_STANDARD_API_DOCUMENTATION_URLS: Record = { + "cn-beijing": "https://bailian.console.aliyun.com/cn-beijing?tab=api#/api", + "sg-singapore": + "https://modelstudio.console.alibabacloud.com/ap-southeast-1?tab=api#/api/?type=model&url=2712195", + "us-virginia": + "https://modelstudio.console.alibabacloud.com/us-east-1?tab=api#/api/?type=model&url=2712195", + "cn-hongkong": + "https://modelstudio.console.alibabacloud.com/cn-hongkong?tab=api#/api/?type=model&url=2712195", }; export function AuthDialog(): React.JSX.Element { @@ -82,55 +71,48 @@ export function AuthDialog(): React.JSX.Element { const config = useConfig(); const [errorMessage, setErrorMessage] = useState(null); - const [viewLevel, setViewLevel] = useState('main'); + const [viewLevel, setViewLevel] = useState("main"); const [regionIndex, setRegionIndex] = useState(0); - const [region, setRegion] = useState( - CodingPlanRegion.CHINA, - ); - const [alibabaStandardRegionIndex, setAlibabaStandardRegionIndex] = - useState(0); + const [region, setRegion] = useState(CodingPlanRegion.CHINA); + const [alibabaStandardRegionIndex, setAlibabaStandardRegionIndex] = useState(0); const [apiKeyTypeIndex, setApiKeyTypeIndex] = useState(0); const [alibabaStandardRegion, setAlibabaStandardRegion] = - useState('cn-beijing'); - const [alibabaStandardApiKey, setAlibabaStandardApiKey] = useState(''); - const [alibabaStandardApiKeyError, setAlibabaStandardApiKeyError] = useState< - string | null - >(null); - const [alibabaStandardModelId, setAlibabaStandardModelId] = useState(''); - const [alibabaStandardModelIdError, setAlibabaStandardModelIdError] = - useState(null); + useState("cn-beijing"); + const [alibabaStandardApiKey, setAlibabaStandardApiKey] = useState(""); + const [alibabaStandardApiKeyError, setAlibabaStandardApiKeyError] = useState(null); + const [alibabaStandardModelId, setAlibabaStandardModelId] = useState(""); + const [alibabaStandardModelIdError, setAlibabaStandardModelIdError] = useState( + null, + ); // Main authentication entries (flat layout) const mainItems = [ { - key: 'CODING_PLAN', - title: t('Alibaba Cloud Coding Plan'), - label: t('Alibaba Cloud Coding Plan'), + key: "CODING_PLAN", + title: t("Alibaba Cloud Coding Plan"), + label: t("Alibaba Cloud Coding Plan"), description: t( - 'Paid \u00B7 Up to 6,000 requests/5 hrs \u00B7 All Alibaba Cloud Coding Plan Models', + "Paid \u00B7 Up to 6,000 requests/5 hrs \u00B7 All Alibaba Cloud Coding Plan Models", ), - value: 'CODING_PLAN' as MainOption, + value: "CODING_PLAN" as MainOption, }, { - key: 'API_KEY', - title: t('API Key'), - label: t('API Key'), - description: t('Bring your own API key'), - value: 'API_KEY' as MainOption, + key: "API_KEY", + title: t("API Key"), + label: t("API Key"), + description: t("Bring your own API key"), + value: "API_KEY" as MainOption, }, ]; // Region selection entries (shown after selecting Alibaba Cloud Coding Plan) const regionItems = [ { - key: 'china', - title: '阿里云百炼 (aliyun.com)', - label: '阿里云百炼 (aliyun.com)', + key: "china", + title: "阿里云百炼 (aliyun.com)", + label: "阿里云百炼 (aliyun.com)", description: ( - + https://help.aliyun.com/zh/model-studio/coding-plan @@ -139,14 +121,11 @@ export function AuthDialog(): React.JSX.Element { value: CodingPlanRegion.CHINA, }, { - key: 'global', - title: 'Alibaba Cloud (alibabacloud.com)', - label: 'Alibaba Cloud (alibabacloud.com)', + key: "global", + title: "Alibaba Cloud (alibabacloud.com)", + label: "Alibaba Cloud (alibabacloud.com)", description: ( - + https://www.alibabacloud.com/help/en/model-studio/coding-plan @@ -158,67 +137,65 @@ export function AuthDialog(): React.JSX.Element { const alibabaStandardRegionItems = [ { - key: 'cn-beijing', - title: t('China (Beijing)'), - label: t('China (Beijing)'), + key: "cn-beijing", + title: t("China (Beijing)"), + label: t("China (Beijing)"), description: ( - Endpoint: {ALIBABA_STANDARD_API_KEY_ENDPOINTS['cn-beijing']} + Endpoint: {ALIBABA_STANDARD_API_KEY_ENDPOINTS["cn-beijing"]} ), - value: 'cn-beijing' as AlibabaStandardRegion, + value: "cn-beijing" as AlibabaStandardRegion, }, { - key: 'sg-singapore', - title: t('Singapore'), - label: t('Singapore'), + key: "sg-singapore", + title: t("Singapore"), + label: t("Singapore"), description: ( - Endpoint: {ALIBABA_STANDARD_API_KEY_ENDPOINTS['sg-singapore']} + Endpoint: {ALIBABA_STANDARD_API_KEY_ENDPOINTS["sg-singapore"]} ), - value: 'sg-singapore' as AlibabaStandardRegion, + value: "sg-singapore" as AlibabaStandardRegion, }, { - key: 'us-virginia', - title: t('US (Virginia)'), - label: t('US (Virginia)'), + key: "us-virginia", + title: t("US (Virginia)"), + label: t("US (Virginia)"), description: ( - Endpoint: {ALIBABA_STANDARD_API_KEY_ENDPOINTS['us-virginia']} + Endpoint: {ALIBABA_STANDARD_API_KEY_ENDPOINTS["us-virginia"]} ), - value: 'us-virginia' as AlibabaStandardRegion, + value: "us-virginia" as AlibabaStandardRegion, }, { - key: 'cn-hongkong', - title: t('China (Hong Kong)'), - label: t('China (Hong Kong)'), + key: "cn-hongkong", + title: t("China (Hong Kong)"), + label: t("China (Hong Kong)"), description: ( - Endpoint: {ALIBABA_STANDARD_API_KEY_ENDPOINTS['cn-hongkong']} + Endpoint: {ALIBABA_STANDARD_API_KEY_ENDPOINTS["cn-hongkong"]} ), - value: 'cn-hongkong' as AlibabaStandardRegion, + value: "cn-hongkong" as AlibabaStandardRegion, }, ]; const apiKeyTypeItems = [ { - key: 'ALIBABA_STANDARD_API_KEY', - title: t('Alibaba Cloud ModelStudio Standard API Key'), - label: t('Alibaba Cloud ModelStudio Standard API Key'), - description: t('Quick setup for Model Studio (China/International)'), - value: 'ALIBABA_STANDARD_API_KEY' as ApiKeyOption, + key: "ALIBABA_STANDARD_API_KEY", + title: t("Alibaba Cloud ModelStudio Standard API Key"), + label: t("Alibaba Cloud ModelStudio Standard API Key"), + description: t("Quick setup for Model Studio (China/International)"), + value: "ALIBABA_STANDARD_API_KEY" as ApiKeyOption, }, { - key: 'CUSTOM_API_KEY', - title: t('Custom API Key'), - label: t('Custom API Key'), - description: t( - 'For other OpenAI / Anthropic / Gemini-compatible providers', - ), - value: 'CUSTOM_API_KEY' as ApiKeyOption, + key: "CUSTOM_API_KEY", + title: t("Custom API Key"), + label: t("Custom API Key"), + description: t("For other OpenAI / Anthropic / Gemini-compatible providers"), + value: "CUSTOM_API_KEY" as ApiKeyOption, }, ]; @@ -228,15 +205,12 @@ export function AuthDialog(): React.JSX.Element { // - API_KEY for other OpenAI / Anthropic / Gemini-compatible configs const contentGenConfig = config.getContentGeneratorConfig(); const isCurrentlyCodingPlan = - isCodingPlanConfig( - contentGenConfig?.baseUrl, - contentGenConfig?.apiKeyEnvKey, - ) !== false; + isCodingPlanConfig(contentGenConfig?.baseUrl, contentGenConfig?.apiKeyEnvKey) !== false; const authTypeToMainOption = (authType: AuthType): MainOption => { if (authType === AuthType.USE_OPENAI && isCurrentlyCodingPlan) { - return 'CODING_PLAN'; + return "CODING_PLAN"; } - return 'API_KEY'; + return "API_KEY"; }; const initialAuthIndex = Math.max( @@ -254,15 +228,13 @@ export function AuthDialog(): React.JSX.Element { } // Priority 3: QWEN_DEFAULT_AUTH_TYPE env var - const defaultAuthType = parseDefaultAuthType( - process.env['QWEN_DEFAULT_AUTH_TYPE'], - ); + const defaultAuthType = parseDefaultAuthType(process.env["QWEN_DEFAULT_AUTH_TYPE"]); if (defaultAuthType) { return item.value === authTypeToMainOption(defaultAuthType); } // Priority 4: default to API_KEY - return item.value === 'API_KEY'; + return item.value === "API_KEY"; }), ); @@ -270,14 +242,14 @@ export function AuthDialog(): React.JSX.Element { setErrorMessage(null); onAuthError(null); - if (value === 'CODING_PLAN') { + if (value === "CODING_PLAN") { // Navigate to region selection - setViewLevel('region-select'); + setViewLevel("region-select"); return; } - if (value === 'API_KEY') { - setViewLevel('api-key-type-select'); + if (value === "API_KEY") { + setViewLevel("api-key-type-select"); return; } }; @@ -286,39 +258,37 @@ export function AuthDialog(): React.JSX.Element { setErrorMessage(null); onAuthError(null); - if (value === 'ALIBABA_STANDARD_API_KEY') { + if (value === "ALIBABA_STANDARD_API_KEY") { setAlibabaStandardModelIdError(null); setAlibabaStandardApiKeyError(null); - setViewLevel('alibaba-standard-region-select'); + setViewLevel("alibaba-standard-region-select"); return; } - setViewLevel('custom-info'); + setViewLevel("custom-info"); }; const handleRegionSelect = async (selectedRegion: CodingPlanRegion) => { setErrorMessage(null); onAuthError(null); setRegion(selectedRegion); - setViewLevel('api-key-input'); + setViewLevel("api-key-input"); }; - const handleAlibabaStandardRegionSelect = async ( - selectedRegion: AlibabaStandardRegion, - ) => { + const handleAlibabaStandardRegionSelect = async (selectedRegion: AlibabaStandardRegion) => { setErrorMessage(null); onAuthError(null); setAlibabaStandardApiKeyError(null); setAlibabaStandardModelIdError(null); setAlibabaStandardRegion(selectedRegion); - setViewLevel('alibaba-standard-api-key-input'); + setViewLevel("alibaba-standard-api-key-input"); }; const handleApiKeyInputSubmit = async (apiKey: string) => { setErrorMessage(null); if (!apiKey.trim()) { - setErrorMessage(t('API key cannot be empty.')); + setErrorMessage(t("API key cannot be empty.")); return; } @@ -329,7 +299,7 @@ export function AuthDialog(): React.JSX.Element { const handleAlibabaStandardApiKeySubmit = () => { const trimmedKey = alibabaStandardApiKey.trim(); if (!trimmedKey) { - setAlibabaStandardApiKeyError(t('API key cannot be empty.')); + setAlibabaStandardApiKeyError(t("API key cannot be empty.")); return; } @@ -337,69 +307,65 @@ export function AuthDialog(): React.JSX.Element { if (!alibabaStandardModelId.trim()) { setAlibabaStandardModelId(ALIBABA_STANDARD_MODEL_IDS_PLACEHOLDER); } - setViewLevel('alibaba-standard-model-id-input'); + setViewLevel("alibaba-standard-model-id-input"); }; const handleAlibabaStandardModelSubmit = () => { const trimmedApiKey = alibabaStandardApiKey.trim(); const trimmedModelIds = alibabaStandardModelId.trim(); if (!trimmedApiKey) { - setAlibabaStandardApiKeyError(t('API key cannot be empty.')); - setViewLevel('alibaba-standard-api-key-input'); + setAlibabaStandardApiKeyError(t("API key cannot be empty.")); + setViewLevel("alibaba-standard-api-key-input"); return; } if (!trimmedModelIds) { - setAlibabaStandardModelIdError(t('Model IDs cannot be empty.')); + setAlibabaStandardModelIdError(t("Model IDs cannot be empty.")); return; } setAlibabaStandardModelIdError(null); - void handleAlibabaStandardSubmit( - trimmedApiKey, - alibabaStandardRegion, - trimmedModelIds, - ); + void handleAlibabaStandardSubmit(trimmedApiKey, alibabaStandardRegion, trimmedModelIds); }; const handleGoBack = () => { setErrorMessage(null); onAuthError(null); - if (viewLevel === 'region-select') { - setViewLevel('main'); - } else if (viewLevel === 'api-key-input') { - setViewLevel('region-select'); - } else if (viewLevel === 'api-key-type-select') { - setViewLevel('main'); - } else if (viewLevel === 'custom-info') { - setViewLevel('api-key-type-select'); - } else if (viewLevel === 'alibaba-standard-region-select') { - setViewLevel('api-key-type-select'); - } else if (viewLevel === 'alibaba-standard-api-key-input') { - setViewLevel('alibaba-standard-region-select'); - } else if (viewLevel === 'alibaba-standard-model-id-input') { - setViewLevel('alibaba-standard-api-key-input'); + if (viewLevel === "region-select") { + setViewLevel("main"); + } else if (viewLevel === "api-key-input") { + setViewLevel("region-select"); + } else if (viewLevel === "api-key-type-select") { + setViewLevel("main"); + } else if (viewLevel === "custom-info") { + setViewLevel("api-key-type-select"); + } else if (viewLevel === "alibaba-standard-region-select") { + setViewLevel("api-key-type-select"); + } else if (viewLevel === "alibaba-standard-api-key-input") { + setViewLevel("alibaba-standard-region-select"); + } else if (viewLevel === "alibaba-standard-model-id-input") { + setViewLevel("alibaba-standard-api-key-input"); } }; useKeypress( (key) => { - if (key.name === 'escape') { + if (key.name === "escape") { // Handle Escape based on current view level - if (viewLevel === 'region-select') { + if (viewLevel === "region-select") { handleGoBack(); return; } - if (viewLevel === 'api-key-input' || viewLevel === 'custom-info') { + if (viewLevel === "api-key-input" || viewLevel === "custom-info") { handleGoBack(); return; } if ( - viewLevel === 'api-key-type-select' || - viewLevel === 'alibaba-standard-region-select' || - viewLevel === 'alibaba-standard-api-key-input' || - viewLevel === 'alibaba-standard-model-id-input' + viewLevel === "api-key-type-select" || + viewLevel === "alibaba-standard-region-select" || + viewLevel === "alibaba-standard-api-key-input" || + viewLevel === "alibaba-standard-model-id-input" ) { handleGoBack(); return; @@ -411,9 +377,7 @@ export function AuthDialog(): React.JSX.Element { } if (config.getAuthType() === undefined) { setErrorMessage( - t( - 'You must select an auth method to proceed. Press Ctrl+C again to exit.', - ), + t("You must select an auth method to proceed. Press Ctrl+C again to exit."), ); return; } @@ -442,7 +406,7 @@ export function AuthDialog(): React.JSX.Element { <> - {t('Choose based on where your account is registered')} + {t("Choose based on where your account is registered")} @@ -459,7 +423,7 @@ export function AuthDialog(): React.JSX.Element { - {t('Enter to select, ↑↓ to navigate, Esc to go back')} + {t("Enter to select, ↑↓ to navigate, Esc to go back")} @@ -468,11 +432,7 @@ export function AuthDialog(): React.JSX.Element { // Render API key input for coding-plan mode const renderApiKeyInputView = () => ( - + ); @@ -484,9 +444,7 @@ export function AuthDialog(): React.JSX.Element { initialIndex={apiKeyTypeIndex} onSelect={handleApiKeyTypeSelect} onHighlight={(value) => { - const index = apiKeyTypeItems.findIndex( - (item) => item.value === value, - ); + const index = apiKeyTypeItems.findIndex((item) => item.value === value); setApiKeyTypeIndex(index); }} itemGap={1} @@ -494,7 +452,7 @@ export function AuthDialog(): React.JSX.Element { - {t('Enter to select, ↑↓ to navigate, Esc to go back')} + {t("Enter to select, ↑↓ to navigate, Esc to go back")} @@ -508,9 +466,7 @@ export function AuthDialog(): React.JSX.Element { initialIndex={alibabaStandardRegionIndex} onSelect={handleAlibabaStandardRegionSelect} onHighlight={(value) => { - const index = alibabaStandardRegionItems.findIndex( - (item) => item.value === value, - ); + const index = alibabaStandardRegionItems.findIndex((item) => item.value === value); setAlibabaStandardRegionIndex(index); }} itemGap={1} @@ -518,7 +474,7 @@ export function AuthDialog(): React.JSX.Element { - {t('Enter to select, ↑↓ to navigate, Esc to go back')} + {t("Enter to select, ↑↓ to navigate, Esc to go back")} @@ -532,13 +488,10 @@ export function AuthDialog(): React.JSX.Element { - {t('Documentation')}: + {t("Documentation")}: - + {ALIBABA_STANDARD_API_DOCUMENTATION_URLS[alibabaStandardRegion]} @@ -563,9 +516,7 @@ export function AuthDialog(): React.JSX.Element { )} - - {t('Enter to submit, Esc to go back')} - + {t("Enter to submit, Esc to go back")} ); @@ -575,7 +526,7 @@ export function AuthDialog(): React.JSX.Element { {t( - 'You can enter multiple model IDs, separated by commas. Examples: qwen3.5-plus,glm-5,kimi-k2.5', + "You can enter multiple model IDs, separated by commas. Examples: qwen3.5-plus,glm-5,kimi-k2.5", )} @@ -598,9 +549,7 @@ export function AuthDialog(): React.JSX.Element { )} - - {t('Enter to submit, Esc to go back')} - + {t("Enter to submit, Esc to go back")} ); @@ -610,47 +559,43 @@ export function AuthDialog(): React.JSX.Element { <> - {t('You can configure your API key and models in settings.json')} + {t("You can configure your API key and models in settings.json")} - {t('Refer to the documentation for setup instructions')} + {t("Refer to the documentation for setup instructions")} - - {MODEL_PROVIDERS_DOCUMENTATION_URL} - + {MODEL_PROVIDERS_DOCUMENTATION_URL} - {t('Esc to go back')} + {t("Esc to go back")} ); const getViewTitle = () => { switch (viewLevel) { - case 'main': - return t('Select Authentication Method'); - case 'region-select': - return t('Select Region for Coding Plan'); - case 'api-key-input': - return t('Enter Coding Plan API Key'); - case 'api-key-type-select': - return t('Select API Key Type'); - case 'custom-info': - return t('Custom Configuration'); - case 'alibaba-standard-region-select': - return t( - 'Select Region for Alibaba Cloud ModelStudio Standard API Key', - ); - case 'alibaba-standard-api-key-input': - return t('Enter Alibaba Cloud ModelStudio Standard API Key'); - case 'alibaba-standard-model-id-input': - return t('Enter Model IDs'); + case "main": + return t("Select Authentication Method"); + case "region-select": + return t("Select Region for Coding Plan"); + case "api-key-input": + return t("Enter Coding Plan API Key"); + case "api-key-type-select": + return t("Select API Key Type"); + case "custom-info": + return t("Custom Configuration"); + case "alibaba-standard-region-select": + return t("Select Region for Alibaba Cloud ModelStudio Standard API Key"); + case "alibaba-standard-api-key-input": + return t("Enter Alibaba Cloud ModelStudio Standard API Key"); + case "alibaba-standard-model-id-input": + return t("Enter Model IDs"); default: - return t('Select Authentication Method'); + return t("Select Authentication Method"); } }; @@ -664,17 +609,14 @@ export function AuthDialog(): React.JSX.Element { > {getViewTitle()} - {viewLevel === 'main' && renderMainView()} - {viewLevel === 'region-select' && renderRegionSelectView()} - {viewLevel === 'api-key-input' && renderApiKeyInputView()} - {viewLevel === 'api-key-type-select' && renderApiKeyTypeSelectView()} - {viewLevel === 'alibaba-standard-region-select' && - renderAlibabaStandardRegionSelectView()} - {viewLevel === 'alibaba-standard-api-key-input' && - renderAlibabaStandardApiKeyInputView()} - {viewLevel === 'alibaba-standard-model-id-input' && - renderAlibabaStandardModelIdInputView()} - {viewLevel === 'custom-info' && renderCustomInfoView()} + {viewLevel === "main" && renderMainView()} + {viewLevel === "region-select" && renderRegionSelectView()} + {viewLevel === "api-key-input" && renderApiKeyInputView()} + {viewLevel === "api-key-type-select" && renderApiKeyTypeSelectView()} + {viewLevel === "alibaba-standard-region-select" && renderAlibabaStandardRegionSelectView()} + {viewLevel === "alibaba-standard-api-key-input" && renderAlibabaStandardApiKeyInputView()} + {viewLevel === "alibaba-standard-model-id-input" && renderAlibabaStandardModelIdInputView()} + {viewLevel === "custom-info" && renderCustomInfoView()} {(authError || errorMessage) && ( @@ -682,7 +624,7 @@ export function AuthDialog(): React.JSX.Element { )} - {viewLevel === 'main' && ( + {viewLevel === "main" && ( <> {/* @@ -690,12 +632,10 @@ export function AuthDialog(): React.JSX.Element { */} - {'\u2500'.repeat(80)} + {"\u2500".repeat(80)} - - {t('Terms of Services and Privacy Notice')}: - + {t("Terms of Services and Privacy Notice")}: void; } -export function AuthInProgress({ - onTimeout, -}: AuthInProgressProps): React.JSX.Element { +export function AuthInProgress({ onTimeout }: AuthInProgressProps): React.JSX.Element { const [timedOut, setTimedOut] = useState(false); useKeypress( (key) => { - if (key.name === 'escape' || (key.ctrl && key.name === 'c')) { + if (key.name === "escape" || (key.ctrl && key.name === "c")) { onTimeout(); } }, @@ -48,14 +46,11 @@ export function AuthInProgress({ width="100%" > {timedOut ? ( - - {t('Authentication timed out. Please try again.')} - + {t("Authentication timed out. Please try again.")} ) : ( - {' '} - {t('Waiting for auth... (Press ESC or CTRL+C to cancel)')} + {t("Waiting for auth... (Press ESC or CTRL+C to cancel)")} )} diff --git a/apps/airiscode-cli/src/ui/auth/useAuth.ts b/apps/airiscode-cli/src/ui/auth/useAuth.ts index ee42463c1..b2d77534b 100644 --- a/apps/airiscode-cli/src/ui/auth/useAuth.ts +++ b/apps/airiscode-cli/src/ui/auth/useAuth.ts @@ -9,45 +9,41 @@ import type { ContentGeneratorConfig, ModelProvidersConfig, ProviderModelConfig, -} from '@airiscode/core'; -import { - AuthEvent, - AuthType, - getErrorMessage, - logAuth, -} from '@airiscode/core'; -import { useCallback, useEffect, useState } from 'react'; -import type { LoadedSettings } from '../../config/settings.js'; -import { getPersistScopeForModelSelection } from '../../config/modelProvidersScope.js'; +} from "@airiscode/core"; +import { AuthEvent, AuthType, getErrorMessage, logAuth } from "@airiscode/core"; +import { useCallback, useEffect, useState } from "react"; +import { getPersistScopeForModelSelection } from "../../config/modelProvidersScope.js"; +import type { LoadedSettings } from "../../config/settings.js"; // OpenAICredentials type (previously imported from OpenAIKeyPrompt) export interface OpenAICredentials { apiKey: string; baseUrl?: string; model?: string; } -import { AuthState, MessageType } from '../types.js'; -import type { HistoryItem } from '../types.js'; -import { t } from '../../i18n/index.js'; -import { - getCodingPlanConfig, - isCodingPlanConfig, - CodingPlanRegion, - CODING_PLAN_ENV_KEY, -} from '../../constants/codingPlan.js'; -import { backupSettingsFile } from '../../utils/settingsUtils.js'; + import { ALIBABA_STANDARD_API_KEY_ENDPOINTS, - DASHSCOPE_STANDARD_API_KEY_ENV_KEY, type AlibabaStandardRegion, -} from '../../constants/alibabaStandardApiKey.js'; + DASHSCOPE_STANDARD_API_KEY_ENV_KEY, +} from "../../constants/alibabaStandardApiKey.js"; +import { + CODING_PLAN_ENV_KEY, + CodingPlanRegion, + getCodingPlanConfig, + isCodingPlanConfig, +} from "../../constants/codingPlan.js"; +import { t } from "../../i18n/index.js"; +import { backupSettingsFile } from "../../utils/settingsUtils.js"; +import type { HistoryItem } from "../types.js"; +import { AuthState, MessageType } from "../types.js"; // Qwen OAuth removed - keep type alias for downstream compatibility -export type QwenAuthState = 'idle' | 'authenticating' | 'authenticated' | 'error'; +export type QwenAuthState = "idle" | "authenticating" | "authenticated" | "error"; export const useAuthCommand = ( settings: LoadedSettings, config: Config, - addItem: (item: Omit, timestamp: number) => void, + addItem: (item: Omit, timestamp: number) => void, onAuthChange?: () => void, ) => { const unAuthenticated = config.getAuthType() === undefined; @@ -60,12 +56,10 @@ export const useAuthCommand = ( const [isAuthenticating, setIsAuthenticating] = useState(false); const [isAuthDialogOpen, setIsAuthDialogOpen] = useState(unAuthenticated); - const [pendingAuthType, setPendingAuthType] = useState( - undefined, - ); + const [pendingAuthType, setPendingAuthType] = useState(undefined); // Qwen OAuth removed - stub state value - const qwenAuthState: QwenAuthState = 'idle'; + const qwenAuthState: QwenAuthState = "idle"; const onAuthError = useCallback( (error: string | null) => { @@ -81,19 +75,14 @@ export const useAuthCommand = ( const handleAuthFailure = useCallback( (error: unknown) => { setIsAuthenticating(false); - const errorMessage = t('Failed to authenticate. Message: {{message}}', { + const errorMessage = t("Failed to authenticate. Message: {{message}}", { message: getErrorMessage(error), }); onAuthError(errorMessage); // Log authentication failure if (pendingAuthType) { - const authEvent = new AuthEvent( - pendingAuthType, - 'manual', - 'error', - errorMessage, - ); + const authEvent = new AuthEvent(pendingAuthType, "manual", "error", errorMessage); logAuth(config, authEvent); } }, @@ -106,39 +95,23 @@ export const useAuthCommand = ( const authTypeScope = getPersistScopeForModelSelection(settings); // Persist authType - settings.setValue( - authTypeScope, - 'security.auth.selectedType', - authType, - ); + settings.setValue(authTypeScope, "security.auth.selectedType", authType); // Persist model from ContentGenerator config (handles fallback cases) // This ensures that when syncAfterAuthRefresh falls back to default model, // it gets persisted to settings.json const contentGeneratorConfig = config.getContentGeneratorConfig(); if (contentGeneratorConfig?.model) { - settings.setValue( - authTypeScope, - 'model.name', - contentGeneratorConfig.model, - ); + settings.setValue(authTypeScope, "model.name", contentGeneratorConfig.model); } // Persist credentials when provided if (credentials) { if (credentials?.apiKey != null) { - settings.setValue( - authTypeScope, - 'security.auth.apiKey', - credentials.apiKey, - ); + settings.setValue(authTypeScope, "security.auth.apiKey", credentials.apiKey); } if (credentials?.baseUrl != null) { - settings.setValue( - authTypeScope, - 'security.auth.baseUrl', - credentials.baseUrl, - ); + settings.setValue(authTypeScope, "security.auth.baseUrl", credentials.baseUrl); } } } catch (error) { @@ -159,7 +132,7 @@ export const useAuthCommand = ( addItem( { type: MessageType.INFO, - text: t('Authenticated successfully with {{authType}} credentials.', { + text: t("Authenticated successfully with {{authType}} credentials.", { authType, }), }, @@ -167,7 +140,7 @@ export const useAuthCommand = ( ); // Log authentication success - const authEvent = new AuthEvent(authType, 'manual', 'success'); + const authEvent = new AuthEvent(authType, "manual", "success"); logAuth(config, authEvent); }, [settings, handleAuthFailure, config, addItem, onAuthChange], @@ -191,9 +164,7 @@ export const useAuthCommand = ( return false; } - const modelProviders = settings.merged.modelProviders as - | ModelProvidersConfig - | undefined; + const modelProviders = settings.merged.modelProviders as ModelProvidersConfig | undefined; if (!modelProviders) { return false; } @@ -201,9 +172,7 @@ export const useAuthCommand = ( if (!Array.isArray(providerModels)) { return false; } - return providerModels.some( - (providerModel) => providerModel.id === modelId, - ); + return providerModels.some((providerModel) => providerModel.id === modelId); }, [settings], ); @@ -240,8 +209,9 @@ export const useAuthCommand = ( // Pass settings.model.generationConfig to updateCredentials so it can be merged // after clearing provider-sourced config. This ensures settings.json generationConfig // fields (e.g., samplingParams, timeout) are preserved. - const settingsGenerationConfig = settings.merged.model - ?.generationConfig as Partial | undefined; + const settingsGenerationConfig = settings.merged.model?.generationConfig as + | Partial + | undefined; config.updateCredentials( { apiKey: credentials.apiKey, @@ -273,7 +243,7 @@ export const useAuthCommand = ( const cancelAuthentication = useCallback(() => { // Log authentication cancellation if (isAuthenticating && pendingAuthType) { - const authEvent = new AuthEvent(pendingAuthType, 'manual', 'cancelled'); + const authEvent = new AuthEvent(pendingAuthType, "manual", "cancelled"); logAuth(config, authEvent); } @@ -289,10 +259,7 @@ export const useAuthCommand = ( * @param region - The region to use (default: CHINA) */ const handleCodingPlanSubmit = useCallback( - async ( - apiKey: string, - region: CodingPlanRegion = CodingPlanRegion.CHINA, - ) => { + async (apiKey: string, region: CodingPlanRegion = CodingPlanRegion.CHINA) => { try { setIsAuthenticating(true); setAuthError(null); @@ -314,18 +281,16 @@ export const useAuthCommand = ( process.env[CODING_PLAN_ENV_KEY] = apiKey; // Generate model configs from template - const newConfigs: ProviderModelConfig[] = template.map( - (templateConfig) => ({ - ...templateConfig, - envKey: CODING_PLAN_ENV_KEY, - }), - ); + const newConfigs: ProviderModelConfig[] = template.map((templateConfig) => ({ + ...templateConfig, + envKey: CODING_PLAN_ENV_KEY, + })); // Get existing configs const existingConfigs = - ( - settings.merged.modelProviders as ModelProvidersConfig | undefined - )?.[AuthType.USE_OPENAI] || []; + (settings.merged.modelProviders as ModelProvidersConfig | undefined)?.[ + AuthType.USE_OPENAI + ] || []; // Filter out all existing Coding Plan configs (mutually exclusive) const nonCodingPlanConfigs = existingConfigs.filter( @@ -336,36 +301,26 @@ export const useAuthCommand = ( const updatedConfigs = [...newConfigs, ...nonCodingPlanConfigs]; // Persist to modelProviders - settings.setValue( - persistScope, - `modelProviders.${AuthType.USE_OPENAI}`, - updatedConfigs, - ); + settings.setValue(persistScope, `modelProviders.${AuthType.USE_OPENAI}`, updatedConfigs); // Also persist authType - settings.setValue( - persistScope, - 'security.auth.selectedType', - AuthType.USE_OPENAI, - ); + settings.setValue(persistScope, "security.auth.selectedType", AuthType.USE_OPENAI); // Persist coding plan region - settings.setValue(persistScope, 'codingPlan.region', region); + settings.setValue(persistScope, "codingPlan.region", region); // Persist coding plan version (single field for backward compatibility) - settings.setValue(persistScope, 'codingPlan.version', version); + settings.setValue(persistScope, "codingPlan.version", version); // If there are configs, use the first one as the model if (updatedConfigs.length > 0 && updatedConfigs[0]?.id) { - settings.setValue(persistScope, 'model.name', updatedConfigs[0].id); + settings.setValue(persistScope, "model.name", updatedConfigs[0].id); } // Hot-reload model providers configuration before refreshAuth // This ensures ModelsConfig has the latest configuration from settings.json const updatedModelProviders: ModelProvidersConfig = { - ...(settings.merged.modelProviders as - | ModelProvidersConfig - | undefined), + ...(settings.merged.modelProviders as ModelProvidersConfig | undefined), [AuthType.USE_OPENAI]: updatedConfigs, }; config.reloadModelProvidersConfig(updatedModelProviders); @@ -387,8 +342,8 @@ export const useAuthCommand = ( { type: MessageType.INFO, text: t( - 'Authenticated successfully with {{region}}. API key and model configs saved to settings.json.', - { region: t('Alibaba Cloud Coding Plan') }, + "Authenticated successfully with {{region}}. API key and model configs saved to settings.json.", + { region: t("Alibaba Cloud Coding Plan") }, ), }, Date.now(), @@ -398,19 +353,13 @@ export const useAuthCommand = ( addItem( { type: MessageType.INFO, - text: t( - 'Tip: Use /model to switch between available Coding Plan models.', - ), + text: t("Tip: Use /model to switch between available Coding Plan models."), }, Date.now(), ); // Log success - const authEvent = new AuthEvent( - AuthType.USE_OPENAI, - 'coding-plan', - 'success', - ); + const authEvent = new AuthEvent(AuthType.USE_OPENAI, "coding-plan", "success"); logAuth(config, authEvent); } catch (error) { handleAuthFailure(error); @@ -424,27 +373,21 @@ export const useAuthCommand = ( * Persists key to env.DASHSCOPE_API_KEY and creates a modelProviders.openai entry. */ const handleAlibabaStandardSubmit = useCallback( - async ( - apiKey: string, - region: AlibabaStandardRegion, - modelIdsInput: string, - ) => { + async (apiKey: string, region: AlibabaStandardRegion, modelIdsInput: string) => { try { setIsAuthenticating(true); setAuthError(null); const trimmedApiKey = apiKey.trim(); const modelIds = modelIdsInput - .split(',') + .split(",") .map((id) => id.trim()) - .filter( - (id, index, array) => id.length > 0 && array.indexOf(id) === index, - ); + .filter((id, index, array) => id.length > 0 && array.indexOf(id) === index); if (!trimmedApiKey) { - throw new Error(t('API key cannot be empty.')); + throw new Error(t("API key cannot be empty.")); } if (modelIds.length === 0) { - throw new Error(t('Model IDs cannot be empty.')); + throw new Error(t("Model IDs cannot be empty.")); } const baseUrl = ALIBABA_STANDARD_API_KEY_ENDPOINTS[region]; @@ -453,11 +396,7 @@ export const useAuthCommand = ( const settingsFile = settings.forScope(persistScope); backupSettingsFile(settingsFile.path); - settings.setValue( - persistScope, - `env.${DASHSCOPE_STANDARD_API_KEY_ENV_KEY}`, - trimmedApiKey, - ); + settings.setValue(persistScope, `env.${DASHSCOPE_STANDARD_API_KEY_ENV_KEY}`, trimmedApiKey); process.env[DASHSCOPE_STANDARD_API_KEY_ENV_KEY] = trimmedApiKey; const newConfigs: ProviderModelConfig[] = modelIds.map((modelId) => ({ @@ -468,39 +407,27 @@ export const useAuthCommand = ( })); const existingConfigs = - ( - settings.merged.modelProviders as ModelProvidersConfig | undefined - )?.[AuthType.USE_OPENAI] || []; + (settings.merged.modelProviders as ModelProvidersConfig | undefined)?.[ + AuthType.USE_OPENAI + ] || []; const nonAlibabaStandardConfigs = existingConfigs.filter( (existing) => !( existing.envKey === DASHSCOPE_STANDARD_API_KEY_ENV_KEY && - typeof existing.baseUrl === 'string' && - Object.values(ALIBABA_STANDARD_API_KEY_ENDPOINTS).includes( - existing.baseUrl, - ) + typeof existing.baseUrl === "string" && + Object.values(ALIBABA_STANDARD_API_KEY_ENDPOINTS).includes(existing.baseUrl) ), ); const updatedConfigs = [...newConfigs, ...nonAlibabaStandardConfigs]; - settings.setValue( - persistScope, - `modelProviders.${AuthType.USE_OPENAI}`, - updatedConfigs, - ); - settings.setValue( - persistScope, - 'security.auth.selectedType', - AuthType.USE_OPENAI, - ); - settings.setValue(persistScope, 'model.name', modelIds[0]); + settings.setValue(persistScope, `modelProviders.${AuthType.USE_OPENAI}`, updatedConfigs); + settings.setValue(persistScope, "security.auth.selectedType", AuthType.USE_OPENAI); + settings.setValue(persistScope, "model.name", modelIds[0]); const updatedModelProviders: ModelProvidersConfig = { - ...(settings.merged.modelProviders as - | ModelProvidersConfig - | undefined), + ...(settings.merged.modelProviders as ModelProvidersConfig | undefined), [AuthType.USE_OPENAI]: updatedConfigs, }; config.reloadModelProvidersConfig(updatedModelProviders); @@ -517,7 +444,7 @@ export const useAuthCommand = ( { type: MessageType.INFO, text: t( - 'Alibaba Cloud ModelStudio Standard API Key successfully entered. Settings updated with env.DASHSCOPE_API_KEY and {{modelCount}} model(s).', + "Alibaba Cloud ModelStudio Standard API Key successfully entered. Settings updated with env.DASHSCOPE_API_KEY and {{modelCount}} model(s).", { modelCount: String(modelIds.length) }, ), }, @@ -528,17 +455,13 @@ export const useAuthCommand = ( { type: MessageType.INFO, text: t( - 'You can use /model to see new ModelStudio Standard models and switch between them.', + "You can use /model to see new ModelStudio Standard models and switch between them.", ), }, Date.now(), ); - const authEvent = new AuthEvent( - AuthType.USE_OPENAI, - 'manual', - 'success', - ); + const authEvent = new AuthEvent(AuthType.USE_OPENAI, "manual", "success"); logAuth(config, authEvent); } catch (error) { handleAuthFailure(error); @@ -558,27 +481,20 @@ export const useAuthCommand = ( * or broken authentication cycles. */ useEffect(() => { - const defaultAuthType = process.env['QWEN_DEFAULT_AUTH_TYPE']; + const defaultAuthType = process.env["QWEN_DEFAULT_AUTH_TYPE"]; if ( defaultAuthType && - ![ - AuthType.USE_OPENAI, - AuthType.USE_ANTHROPIC, - AuthType.USE_OLLAMA, - ].includes(defaultAuthType as AuthType) + ![AuthType.USE_OPENAI, AuthType.USE_ANTHROPIC, AuthType.USE_OLLAMA].includes( + defaultAuthType as AuthType, + ) ) { onAuthError( - t( - 'Invalid QWEN_DEFAULT_AUTH_TYPE value: "{{value}}". Valid values are: {{validValues}}', - { - value: defaultAuthType, - validValues: [ - AuthType.USE_OPENAI, - AuthType.USE_ANTHROPIC, - AuthType.USE_OLLAMA, - ].join(', '), - }, - ), + t('Invalid QWEN_DEFAULT_AUTH_TYPE value: "{{value}}". Valid values are: {{validValues}}', { + value: defaultAuthType, + validValues: [AuthType.USE_OPENAI, AuthType.USE_ANTHROPIC, AuthType.USE_OLLAMA].join( + ", ", + ), + }), ); } }, [onAuthError]); diff --git a/apps/airiscode-cli/src/ui/colors.ts b/apps/airiscode-cli/src/ui/colors.ts index 0fb75a358..a0f55e73e 100644 --- a/apps/airiscode-cli/src/ui/colors.ts +++ b/apps/airiscode-cli/src/ui/colors.ts @@ -4,8 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { themeManager } from './themes/theme-manager.js'; -import type { ColorsTheme } from './themes/theme.js'; +import type { ColorsTheme } from "./themes/theme.js"; +import { themeManager } from "./themes/theme-manager.js"; export const Colors: ColorsTheme = { get type() { diff --git a/apps/airiscode-cli/src/ui/commands/aboutCommand.ts b/apps/airiscode-cli/src/ui/commands/aboutCommand.ts index 1570b9e3d..8410d9194 100644 --- a/apps/airiscode-cli/src/ui/commands/aboutCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/aboutCommand.ts @@ -4,23 +4,23 @@ * SPDX-License-Identifier: Apache-2.0 */ -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'; +import { t } from "../../i18n/index.js"; +import { getExtendedSystemInfo } from "../../utils/systemInfo.js"; +import { type HistoryItemAbout, MessageType } from "../types.js"; +import type { SlashCommand } from "./types.js"; +import { CommandKind } from "./types.js"; export const aboutCommand: SlashCommand = { - name: 'status', - altNames: ['about'], + name: "status", + altNames: ["about"], get description() { - return t('show version info'); + return t("show version info"); }, kind: CommandKind.BUILT_IN, action: async (context) => { const systemInfo = await getExtendedSystemInfo(context); - const aboutItem: Omit = { + const aboutItem: Omit = { type: MessageType.ABOUT, systemInfo, }; diff --git a/apps/airiscode-cli/src/ui/commands/agentsCommand.ts b/apps/airiscode-cli/src/ui/commands/agentsCommand.ts index 02fed007b..f27554846 100644 --- a/apps/airiscode-cli/src/ui/commands/agentsCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/agentsCommand.ts @@ -4,40 +4,36 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { - CommandKind, - type SlashCommand, - type OpenDialogActionReturn, -} from './types.js'; -import { t } from '../../i18n/index.js'; +import { t } from "../../i18n/index.js"; +import { CommandKind, type OpenDialogActionReturn, type SlashCommand } from "./types.js"; export const agentsCommand: SlashCommand = { - name: 'agents', + name: "agents", get description() { - return t('Manage subagents for specialized task delegation.'); + return t("Manage subagents for specialized task delegation."); }, kind: CommandKind.BUILT_IN, subCommands: [ { - name: 'manage', + name: "manage", get description() { - return t('Manage existing subagents (view, edit, delete).'); + return t("Manage existing subagents (view, edit, delete)."); }, kind: CommandKind.BUILT_IN, action: (): OpenDialogActionReturn => ({ - type: 'dialog', - dialog: 'subagent_list', + type: "dialog", + dialog: "subagent_list", }), }, { - name: 'create', + name: "create", get description() { - return t('Create a new subagent with guided setup.'); + return t("Create a new subagent with guided setup."); }, kind: CommandKind.BUILT_IN, action: (): OpenDialogActionReturn => ({ - type: 'dialog', - dialog: 'subagent_create', + type: "dialog", + dialog: "subagent_create", }), }, ], diff --git a/apps/airiscode-cli/src/ui/commands/approvalModeCommand.ts b/apps/airiscode-cli/src/ui/commands/approvalModeCommand.ts index a1caf09bc..3fe204e94 100644 --- a/apps/airiscode-cli/src/ui/commands/approvalModeCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/approvalModeCommand.ts @@ -4,16 +4,16 @@ * SPDX-License-Identifier: Apache-2.0 */ +import type { ApprovalMode } from "@airiscode/core"; +import { APPROVAL_MODES } from "@airiscode/core"; +import { t } from "../../i18n/index.js"; import type { - SlashCommand, CommandContext, - OpenDialogActionReturn, MessageActionReturn, -} from './types.js'; -import { CommandKind } from './types.js'; -import { t } from '../../i18n/index.js'; -import type { ApprovalMode } from '@airiscode/core'; -import { APPROVAL_MODES } from '@airiscode/core'; + OpenDialogActionReturn, + SlashCommand, +} from "./types.js"; +import { CommandKind } from "./types.js"; /** * Parses the argument string and returns the corresponding ApprovalMode if valid. @@ -29,9 +29,9 @@ function parseApprovalModeArg(arg: string): ApprovalMode | undefined { } export const approvalModeCommand: SlashCommand = { - name: 'approval-mode', + name: "approval-mode", get description() { - return t('View or change the approval mode for tool usage'); + return t("View or change the approval mode for tool usage"); }, kind: CommandKind.BUILT_IN, action: async ( @@ -43,19 +43,19 @@ export const approvalModeCommand: SlashCommand = { // If no argument provided, open the dialog if (!args.trim()) { return { - type: 'dialog', - dialog: 'approval-mode', + type: "dialog", + dialog: "approval-mode", }; } // If invalid argument, return error message with valid options if (!mode) { return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: t('Invalid approval mode "{{arg}}". Valid modes: {{modes}}', { arg: args.trim(), - modes: APPROVAL_MODES.join(', '), + modes: APPROVAL_MODES.join(", "), }), }; } @@ -67,16 +67,16 @@ export const approvalModeCommand: SlashCommand = { config.setApprovalMode(mode); } catch (e) { return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: (e as Error).message, }; } } return { - type: 'message', - messageType: 'info', + type: "message", + messageType: "info", content: t('Approval mode set to "{{mode}}"', { mode }), }; }, diff --git a/apps/airiscode-cli/src/ui/commands/arenaCommand.ts b/apps/airiscode-cli/src/ui/commands/arenaCommand.ts index 053e86685..9dce632c8 100644 --- a/apps/airiscode-cli/src/ui/commands/arenaCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/arenaCommand.ts @@ -4,39 +4,35 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { - SlashCommand, - CommandContext, - ConfirmActionReturn, - MessageActionReturn, - OpenDialogActionReturn, - SlashCommandActionReturn, -} from './types.js'; -import { CommandKind } from './types.js'; import { - ArenaManager, - ArenaEventType, - isTerminalStatus, - isSuccessStatus, - ArenaSessionStatus, - AuthType, - createDebugLogger, - stripStartupContext, - type Config, - type ArenaModelConfig, - type ArenaAgentErrorEvent, type ArenaAgentCompleteEvent, + type ArenaAgentErrorEvent, type ArenaAgentStartEvent, + ArenaEventType, + ArenaManager, + type ArenaModelConfig, type ArenaSessionCompleteEvent, type ArenaSessionErrorEvent, type ArenaSessionStartEvent, + ArenaSessionStatus, type ArenaSessionUpdateEvent, -} from '@airiscode/core'; -import { - MessageType, - type ArenaAgentCardData, - type HistoryItemWithoutId, -} from '../types.js'; + AuthType, + type Config, + createDebugLogger, + isSuccessStatus, + isTerminalStatus, + stripStartupContext, +} from "@airiscode/core"; +import { type ArenaAgentCardData, type HistoryItemWithoutId, MessageType } from "../types.js"; +import type { + CommandContext, + ConfirmActionReturn, + MessageActionReturn, + OpenDialogActionReturn, + SlashCommand, + SlashCommandActionReturn, +} from "./types.js"; +import { CommandKind } from "./types.js"; /** * Parsed model entry with optional auth type. @@ -67,10 +63,10 @@ function parseArenaArgs(args: string): { let task = args; if (modelsMatch) { - const modelStrings = modelsMatch[1]!.split(',').filter(Boolean); + const modelStrings = modelsMatch[1]!.split(",").filter(Boolean); models = modelStrings.map((str) => { // Check for authType:modelId format - const colonIndex = str.indexOf(':'); + const colonIndex = str.indexOf(":"); if (colonIndex > 0) { return { authType: str.substring(0, colonIndex), @@ -79,16 +75,16 @@ function parseArenaArgs(args: string): { } return { modelId: str }; }); - task = task.replace(/--models\s+\S+/, '').trim(); + task = task.replace(/--models\s+\S+/, "").trim(); } // Strip surrounding quotes from task - task = task.replace(/^["']|["']$/g, '').trim(); + task = task.replace(/^["']|["']$/g, "").trim(); return { models, task }; } -const debugLogger = createDebugLogger('ARENA_COMMAND'); +const debugLogger = createDebugLogger("ARENA_COMMAND"); interface ArenaExecutionInput { task: string; @@ -102,17 +98,17 @@ function buildArenaExecutionInput( ): ArenaExecutionInput | MessageActionReturn { if (!parsed.task) { return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: - 'Usage: /arena start --models model1,model2 \n' + - '\n' + - 'Options:\n' + - ' --models [authType:]model1,[authType:]model2\n' + - ' Models to compete (required, at least 2)\n' + - ' Format: authType:modelId or just modelId\n' + - '\n' + - 'Examples:\n' + + "Usage: /arena start --models model1,model2 \n" + + "\n" + + "Options:\n" + + " --models [authType:]model1,[authType:]model2\n" + + " Models to compete (required, at least 2)\n" + + " Format: authType:modelId or just modelId\n" + + "\n" + + "Examples:\n" + ' /arena start --models openai:gpt-4o,anthropic:claude-3 "implement sorting"\n' + ' /arena start --models airiscoder-plus,kimi-for-coding "fix the bug"', }; @@ -120,25 +116,23 @@ function buildArenaExecutionInput( if (parsed.models.length < 2) { return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: - 'Arena requires at least 2 models. Use --models model1,model2 to specify.\n' + - 'Format: [authType:]modelId (e.g., openai:gpt-4o or just gpt-4o)', + "Arena requires at least 2 models. Use --models model1,model2 to specify.\n" + + "Format: [authType:]modelId (e.g., openai:gpt-4o or just gpt-4o)", }; } // Get the current auth type as default for models without explicit auth type const contentGeneratorConfig = config.getContentGeneratorConfig(); - const defaultAuthType = - contentGeneratorConfig?.authType ?? AuthType.USE_OPENAI; + const defaultAuthType = contentGeneratorConfig?.authType ?? AuthType.USE_OPENAI; // Build ArenaModelConfig for each model, resolving display names from // the model registry when available. const modelsConfig = config.getModelsConfig(); const models: ArenaModelConfig[] = parsed.models.map((parsedModel) => { - const authType = - (parsedModel.authType as AuthType | undefined) ?? defaultAuthType; + const authType = (parsedModel.authType as AuthType | undefined) ?? defaultAuthType; const registryModels = modelsConfig.getAvailableModelsForAuthType(authType); const resolved = registryModels.find((m) => m.id === parsedModel.modelId); return { @@ -166,18 +160,18 @@ function recordArenaItem(config: Config, item: HistoryItemWithoutId): void { const chatRecorder = config.getChatRecordingService(); if (!chatRecorder) return; chatRecorder.recordSlashCommand({ - phase: 'result', - rawCommand: '/arena', + phase: "result", + rawCommand: "/arena", outputHistoryItems: [{ ...item } as Record], }); } catch { - debugLogger.error('Failed to record arena history item'); + debugLogger.error("Failed to record arena history item"); } } function executeArenaCommand( config: Config, - ui: CommandContext['ui'], + ui: CommandContext["ui"], input: ArenaExecutionInput, ): void { // Capture the main session's chat history so arena agents start with @@ -189,7 +183,7 @@ function executeArenaCommand( const fullHistory = config.getGeminiClient().getHistory(); chatHistory = stripStartupContext(fullHistory); } catch { - debugLogger.debug('Could not retrieve chat history for arena agents'); + debugLogger.debug("Could not retrieve chat history for arena agents"); } const manager = new ArenaManager(config); @@ -197,15 +191,12 @@ function executeArenaCommand( const detachListeners: Array<() => void> = []; const agentLabels = new Map(); - const addArenaMessage = ( - type: 'info' | 'warning' | 'error' | 'success', - text: string, - ) => { + const addArenaMessage = (type: "info" | "warning" | "error" | "success", text: string) => { ui.addItem({ type, text }, Date.now()); }; const addAndRecordArenaMessage = ( - type: 'info' | 'warning' | 'error' | 'success', + type: "info" | "warning" | "error" | "success", text: string, ) => { const item: HistoryItemWithoutId = { type, text }; @@ -216,7 +207,7 @@ function executeArenaCommand( const handleSessionStart = (event: ArenaSessionStartEvent) => { const modelList = event.models .map((model, index) => ` ${index + 1}. ${model.modelId}`) - .join('\n'); + .join("\n"); // SESSION_START fires synchronously before the first await in // ArenaManager.start(), so the slash command processor's finally // block already captures this item — no extra recording needed. @@ -228,13 +219,11 @@ function executeArenaCommand( const handleAgentStart = (event: ArenaAgentStartEvent) => { agentLabels.set(event.agentId, event.model.modelId); - debugLogger.debug( - `Arena agent started: ${event.model.modelId} (${event.agentId})`, - ); + debugLogger.debug(`Arena agent started: ${event.model.modelId} (${event.agentId})`); }; const handleSessionUpdate = (event: ArenaSessionUpdateEvent) => { - const attachHintPrefix = 'To view agent panes, run: '; + const attachHintPrefix = "To view agent panes, run: "; if (event.message.startsWith(attachHintPrefix)) { const command = event.message.slice(attachHintPrefix.length).trim(); addAndRecordArenaMessage( @@ -244,9 +233,9 @@ function executeArenaCommand( return; } - if (event.type === 'success') { + if (event.type === "success") { addAndRecordArenaMessage(MessageType.SUCCESS, event.message); - } else if (event.type === 'info') { + } else if (event.type === "info") { addAndRecordArenaMessage(MessageType.INFO, event.message); } else { addAndRecordArenaMessage(MessageType.WARNING, event.message); @@ -255,15 +244,10 @@ function executeArenaCommand( const handleAgentError = (event: ArenaAgentErrorEvent) => { const label = agentLabels.get(event.agentId) || event.agentId; - addAndRecordArenaMessage( - MessageType.ERROR, - `[${label}] failed: ${event.error}`, - ); + addAndRecordArenaMessage(MessageType.ERROR, `[${label}] failed: ${event.error}`); }; - const buildAgentCardData = ( - result: ArenaAgentCompleteEvent['result'], - ): ArenaAgentCardData => ({ + const buildAgentCardData = (result: ArenaAgentCompleteEvent["result"]): ArenaAgentCardData => ({ label: result.model.modelId, status: result.status, durationMs: result.stats.durationMs, @@ -285,7 +269,7 @@ function executeArenaCommand( const agent = buildAgentCardData(event.result); const item = { - type: 'arena_agent_complete', + type: "arena_agent_complete", agent, } as HistoryItemWithoutId; ui.addItem(item, Date.now()); @@ -298,7 +282,7 @@ function executeArenaCommand( const handleSessionComplete = (event: ArenaSessionCompleteEvent) => { const item = { - type: 'arena_session_complete', + type: "arena_session_complete", sessionStatus: event.result.status, task: event.result.task, totalDurationMs: event.result.totalDurationMs ?? 0, @@ -309,33 +293,19 @@ function executeArenaCommand( }; emitter.on(ArenaEventType.SESSION_START, handleSessionStart); - detachListeners.push(() => - emitter.off(ArenaEventType.SESSION_START, handleSessionStart), - ); + detachListeners.push(() => emitter.off(ArenaEventType.SESSION_START, handleSessionStart)); emitter.on(ArenaEventType.AGENT_START, handleAgentStart); - detachListeners.push(() => - emitter.off(ArenaEventType.AGENT_START, handleAgentStart), - ); + detachListeners.push(() => emitter.off(ArenaEventType.AGENT_START, handleAgentStart)); emitter.on(ArenaEventType.SESSION_UPDATE, handleSessionUpdate); - detachListeners.push(() => - emitter.off(ArenaEventType.SESSION_UPDATE, handleSessionUpdate), - ); + detachListeners.push(() => emitter.off(ArenaEventType.SESSION_UPDATE, handleSessionUpdate)); emitter.on(ArenaEventType.AGENT_ERROR, handleAgentError); - detachListeners.push(() => - emitter.off(ArenaEventType.AGENT_ERROR, handleAgentError), - ); + detachListeners.push(() => emitter.off(ArenaEventType.AGENT_ERROR, handleAgentError)); emitter.on(ArenaEventType.AGENT_COMPLETE, handleAgentComplete); - detachListeners.push(() => - emitter.off(ArenaEventType.AGENT_COMPLETE, handleAgentComplete), - ); + detachListeners.push(() => emitter.off(ArenaEventType.AGENT_COMPLETE, handleAgentComplete)); emitter.on(ArenaEventType.SESSION_ERROR, handleSessionError); - detachListeners.push(() => - emitter.off(ArenaEventType.SESSION_ERROR, handleSessionError), - ); + detachListeners.push(() => emitter.off(ArenaEventType.SESSION_ERROR, handleSessionError)); emitter.on(ArenaEventType.SESSION_COMPLETE, handleSessionComplete); - detachListeners.push(() => - emitter.off(ArenaEventType.SESSION_COMPLETE, handleSessionComplete), - ); + detachListeners.push(() => emitter.off(ArenaEventType.SESSION_COMPLETE, handleSessionComplete)); config.setArenaManager(manager); @@ -353,12 +323,12 @@ function executeArenaCommand( }) .then( () => { - debugLogger.debug('Arena agents settled'); + debugLogger.debug("Arena agents settled"); }, (error) => { const message = error instanceof Error ? error.message : String(error); addAndRecordArenaMessage(MessageType.ERROR, `${message}`); - debugLogger.error('Arena session failed:', error); + debugLogger.error("Arena session failed:", error); // Clear the stored manager so subsequent /arena start calls // are not blocked by the stale reference after a startup failure. @@ -381,26 +351,25 @@ function executeArenaCommand( } export const arenaCommand: SlashCommand = { - name: 'arena', - description: 'Manage Arena sessions', + name: "arena", + description: "Manage Arena sessions", kind: CommandKind.BUILT_IN, subCommands: [ { - name: 'start', - description: - 'Start an Arena session with multiple models competing on the same task', + name: "start", + description: "Start an Arena session with multiple models competing on the same task", kind: CommandKind.BUILT_IN, action: async ( context: CommandContext, args: string, ): Promise => { - const executionMode = context.executionMode ?? 'interactive'; - if (executionMode !== 'interactive') { + const executionMode = context.executionMode ?? "interactive"; + if (executionMode !== "interactive") { return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: - 'Arena is not supported in non-interactive mode. Use interactive mode to start an Arena session.', + "Arena is not supported in non-interactive mode. Use interactive mode to start an Arena session.", }; } @@ -409,9 +378,9 @@ export const arenaCommand: SlashCommand = { if (!config) { return { - type: 'message', - messageType: 'error', - content: 'Configuration not available.', + type: "message", + messageType: "error", + content: "Configuration not available.", }; } @@ -419,23 +388,23 @@ export const arenaCommand: SlashCommand = { const existingManager = config.getArenaManager(); if (existingManager) { return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: - 'An Arena session exists. Use /arena stop or /arena select to end it before starting a new one.', + "An Arena session exists. Use /arena stop or /arena select to end it before starting a new one.", }; } const parsed = parseArenaArgs(args); if (parsed.models.length === 0) { return { - type: 'dialog', - dialog: 'arena_start', + type: "dialog", + dialog: "arena_start", }; } const executionInput = buildArenaExecutionInput(parsed, config); - if ('type' in executionInput) { + if ("type" in executionInput) { return executionInput; } @@ -443,116 +412,106 @@ export const arenaCommand: SlashCommand = { }, }, { - name: 'stop', - description: 'Stop the current Arena session', + name: "stop", + description: "Stop the current Arena session", kind: CommandKind.BUILT_IN, - action: async ( - context: CommandContext, - ): Promise => { - const executionMode = context.executionMode ?? 'interactive'; - if (executionMode !== 'interactive') { + action: async (context: CommandContext): Promise => { + const executionMode = context.executionMode ?? "interactive"; + if (executionMode !== "interactive") { return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: - 'Arena is not supported in non-interactive mode. Use interactive mode to stop an Arena session.', + "Arena is not supported in non-interactive mode. Use interactive mode to stop an Arena session.", }; } const { config } = context.services; if (!config) { return { - type: 'message', - messageType: 'error', - content: 'Configuration not available.', + type: "message", + messageType: "error", + content: "Configuration not available.", }; } const manager = config.getArenaManager(); if (!manager) { return { - type: 'message', - messageType: 'error', - content: 'No running Arena session found.', + type: "message", + messageType: "error", + content: "No running Arena session found.", }; } return { - type: 'dialog', - dialog: 'arena_stop', + type: "dialog", + dialog: "arena_stop", }; }, }, { - name: 'status', - description: 'Show the current Arena session status', + name: "status", + description: "Show the current Arena session status", kind: CommandKind.BUILT_IN, - action: async ( - context: CommandContext, - ): Promise => { - const executionMode = context.executionMode ?? 'interactive'; - if (executionMode !== 'interactive') { + action: async (context: CommandContext): Promise => { + const executionMode = context.executionMode ?? "interactive"; + if (executionMode !== "interactive") { return { - type: 'message', - messageType: 'error', - content: 'Arena is not supported in non-interactive mode.', + type: "message", + messageType: "error", + content: "Arena is not supported in non-interactive mode.", }; } const { config } = context.services; if (!config) { return { - type: 'message', - messageType: 'error', - content: 'Configuration not available.', + type: "message", + messageType: "error", + content: "Configuration not available.", }; } const manager = config.getArenaManager(); if (!manager) { return { - type: 'message', - messageType: 'error', - content: 'No Arena session found. Start one with /arena start.', + type: "message", + messageType: "error", + content: "No Arena session found. Start one with /arena start.", }; } return { - type: 'dialog', - dialog: 'arena_status', + type: "dialog", + dialog: "arena_status", }; }, }, { - name: 'select', - altNames: ['choose'], - description: - 'Select a model result and merge its diff into the current workspace', + name: "select", + altNames: ["choose"], + description: "Select a model result and merge its diff into the current workspace", kind: CommandKind.BUILT_IN, action: async ( context: CommandContext, args: string, - ): Promise< - | void - | MessageActionReturn - | OpenDialogActionReturn - | ConfirmActionReturn - > => { - const executionMode = context.executionMode ?? 'interactive'; - if (executionMode !== 'interactive') { + ): Promise => { + const executionMode = context.executionMode ?? "interactive"; + if (executionMode !== "interactive") { return { - type: 'message', - messageType: 'error', - content: 'Arena is not supported in non-interactive mode.', + type: "message", + messageType: "error", + content: "Arena is not supported in non-interactive mode.", }; } const { config } = context.services; if (!config) { return { - type: 'message', - messageType: 'error', - content: 'Configuration not available.', + type: "message", + messageType: "error", + content: "Configuration not available.", }; } @@ -560,9 +519,9 @@ export const arenaCommand: SlashCommand = { if (!manager) { return { - type: 'message', - messageType: 'error', - content: 'No arena session found. Start one with /arena start.', + type: "message", + messageType: "error", + content: "No arena session found. Start one with /arena start.", }; } @@ -572,32 +531,32 @@ export const arenaCommand: SlashCommand = { sessionStatus === ArenaSessionStatus.INITIALIZING ) { return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: - 'Arena session is still running. Wait for it to complete or use /arena stop first.', + "Arena session is still running. Wait for it to complete or use /arena stop first.", }; } // Handle --discard flag before checking for successful agents, // so users can clean up worktrees even when all agents failed. const trimmedArgs = args.trim(); - if (trimmedArgs === '--discard') { + if (trimmedArgs === "--discard") { if (!context.overwriteConfirmed) { return { - type: 'confirm_action', - prompt: 'Discard all Arena results and clean up worktrees?', + type: "confirm_action", + prompt: "Discard all Arena results and clean up worktrees?", originalInvocation: { - raw: context.invocation?.raw || '/arena select --discard', + raw: context.invocation?.raw || "/arena select --discard", }, }; } await config.cleanupArenaRuntime(true); return { - type: 'message', - messageType: 'info', - content: 'Arena results discarded. All worktrees cleaned up.', + type: "message", + messageType: "info", + content: "Arena results discarded. All worktrees cleaned up.", }; } @@ -606,11 +565,11 @@ export const arenaCommand: SlashCommand = { if (!hasSuccessful) { return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: - 'No successful agent results to select from. All agents failed or were cancelled.\n' + - 'Use /arena stop to end the session.', + "No successful agent results to select from. All agents failed or were cancelled.\n" + + "Use /arena stop to end the session.", }; } @@ -624,8 +583,8 @@ export const arenaCommand: SlashCommand = { if (!matchingAgent) { return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: `No idle agent found matching "${trimmedArgs}".`, }; } @@ -634,24 +593,24 @@ export const arenaCommand: SlashCommand = { const result = await manager.applyAgentResult(matchingAgent.agentId); if (!result.success) { return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: `Failed to apply changes from ${label}: ${result.error}`, }; } await config.cleanupArenaRuntime(true); return { - type: 'message', - messageType: 'info', + type: "message", + messageType: "info", content: `Applied changes from ${label} to workspace. Arena session complete.`, }; } // No args → open the select dialog return { - type: 'dialog', - dialog: 'arena_select', + type: "dialog", + dialog: "arena_select", }; }, }, diff --git a/apps/airiscode-cli/src/ui/commands/authCommand.ts b/apps/airiscode-cli/src/ui/commands/authCommand.ts index 83ab454b0..353d0f490 100644 --- a/apps/airiscode-cli/src/ui/commands/authCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/authCommand.ts @@ -4,19 +4,19 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { OpenDialogActionReturn, SlashCommand } from './types.js'; -import { CommandKind } from './types.js'; -import { t } from '../../i18n/index.js'; +import { t } from "../../i18n/index.js"; +import type { OpenDialogActionReturn, SlashCommand } from "./types.js"; +import { CommandKind } from "./types.js"; export const authCommand: SlashCommand = { - name: 'auth', - altNames: ['login'], + name: "auth", + altNames: ["login"], get description() { - return t('Configure authentication information for login'); + return t("Configure authentication information for login"); }, kind: CommandKind.BUILT_IN, action: (_context, _args): OpenDialogActionReturn => ({ - type: 'dialog', - dialog: 'auth', + type: "dialog", + dialog: "auth", }), }; diff --git a/apps/airiscode-cli/src/ui/commands/btwCommand.ts b/apps/airiscode-cli/src/ui/commands/btwCommand.ts index 8aa323b99..79fdcb131 100644 --- a/apps/airiscode-cli/src/ui/commands/btwCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/btwCommand.ts @@ -4,26 +4,20 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { - CommandContext, - SlashCommand, - SlashCommandActionReturn, -} from './types.js'; -import { CommandKind } from './types.js'; -import { MessageType } from '../types.js'; -import type { HistoryItemBtw } from '../types.js'; -import { t } from '../../i18n/index.js'; -import type { GeminiClient } from '@airiscode/core'; -import type { Content } from '@airiscode/core'; +import type { Content, GeminiClient } from "@airiscode/core"; +import { t } from "../../i18n/index.js"; +import type { HistoryItemBtw } from "../types.js"; +import { MessageType } from "../types.js"; +import type { CommandContext, SlashCommand, SlashCommandActionReturn } from "./types.js"; +import { CommandKind } from "./types.js"; function makeBtwPromptId(sessionId: string): string { return `${sessionId}########btw-${Date.now()}`; } function formatBtwError(error: unknown): string { - return t('Failed to answer btw question: {{error}}', { - error: - error instanceof Error ? error.message : String(error || 'Unknown error'), + return t("Failed to answer btw question: {{error}}", { + error: error instanceof Error ? error.message : String(error || "Unknown error"), }); } @@ -39,7 +33,7 @@ function trimHistory(history: Content[]): Content[] { // Slice from the end, ensuring we start on a 'user' message so the // alternating user/model pattern is preserved. const sliced = history.slice(-MAX_BTW_HISTORY_MESSAGES); - if (sliced[0]?.role === 'model' && sliced.length > 1) { + if (sliced[0]?.role === "model" && sliced.length > 1) { return sliced.slice(1); } return sliced; @@ -67,7 +61,7 @@ async function askBtw( [ ...history, { - role: 'user', + role: "user", parts: [ { text: `[This is a side question - answer directly and concisely. @@ -101,17 +95,15 @@ ${question}`, return ( parts ?.map((part) => part.text) - .filter((text): text is string => typeof text === 'string') - .join('') || t('No response received.') + .filter((text): text is string => typeof text === "string") + .join("") || t("No response received.") ); } export const btwCommand: SlashCommand = { - name: 'btw', + name: "btw", get description() { - return t( - 'Ask a quick side question without affecting the main conversation', - ); + return t("Ask a quick side question without affecting the main conversation"); }, kind: CommandKind.BUILT_IN, action: async ( @@ -119,14 +111,14 @@ export const btwCommand: SlashCommand = { args: string, ): Promise => { const question = args.trim(); - const executionMode = context.executionMode ?? 'interactive'; + const executionMode = context.executionMode ?? "interactive"; const abortSignal = context.abortSignal ?? new AbortController().signal; if (!question) { return { - type: 'message', - messageType: 'error', - content: t('Please provide a question. Usage: /btw '), + type: "message", + messageType: "error", + content: t("Please provide a question. Usage: /btw "), }; } @@ -135,9 +127,9 @@ export const btwCommand: SlashCommand = { if (!config) { return { - type: 'message', - messageType: 'error', - content: t('Config not loaded.'), + type: "message", + messageType: "error", + content: t("Config not loaded."), }; } @@ -147,65 +139,53 @@ export const btwCommand: SlashCommand = { if (!model) { return { - type: 'message', - messageType: 'error', - content: t('No model configured.'), + type: "message", + messageType: "error", + content: t("No model configured."), }; } // ACP mode: return a stream_messages async generator - if (executionMode === 'acp') { + if (executionMode === "acp") { const btwPromptId = makeBtwPromptId(sessionId); const messages = async function* () { try { yield { - messageType: 'info' as const, - content: t('Thinking...'), + messageType: "info" as const, + content: t("Thinking..."), }; - const answer = await askBtw( - geminiClient, - model, - question, - abortSignal, - btwPromptId, - ); + const answer = await askBtw(geminiClient, model, question, abortSignal, btwPromptId); yield { - messageType: 'info' as const, + messageType: "info" as const, content: `btw> ${question}\n${answer}`, }; } catch (error) { yield { - messageType: 'error' as const, + messageType: "error" as const, content: formatBtwError(error), }; } }; - return { type: 'stream_messages', messages: messages() }; + return { type: "stream_messages", messages: messages() }; } // Non-interactive mode: return a simple message result - if (executionMode === 'non_interactive') { + if (executionMode === "non_interactive") { try { const btwPromptId = makeBtwPromptId(sessionId); - const answer = await askBtw( - geminiClient, - model, - question, - abortSignal, - btwPromptId, - ); + const answer = await askBtw(geminiClient, model, question, abortSignal, btwPromptId); return { - type: 'message', - messageType: 'info', + type: "message", + messageType: "info", content: `btw> ${question}\n${answer}`, }; } catch (error) { return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: formatBtwError(error), }; } @@ -225,7 +205,7 @@ export const btwCommand: SlashCommand = { type: MessageType.BTW, btw: { question, - answer: '', + answer: "", isPending: true, }, }; diff --git a/apps/airiscode-cli/src/ui/commands/bugCommand.ts b/apps/airiscode-cli/src/ui/commands/bugCommand.ts index 0b40c5639..38b4834ab 100644 --- a/apps/airiscode-cli/src/ui/commands/bugCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/bugCommand.ts @@ -4,35 +4,29 @@ * SPDX-License-Identifier: Apache-2.0 */ -import open from 'open'; -import { - type CommandContext, - type SlashCommand, - CommandKind, -} from './types.js'; -import { MessageType } from '../types.js'; -import { getExtendedSystemInfo } from '../../utils/systemInfo.js'; -import { getSystemInfoFields } from '../../utils/systemInfoFields.js'; -import { t } from '../../i18n/index.js'; +import open from "open"; +import { t } from "../../i18n/index.js"; +import { getExtendedSystemInfo } from "../../utils/systemInfo.js"; +import { getSystemInfoFields } from "../../utils/systemInfoFields.js"; +import { MessageType } from "../types.js"; +import { type CommandContext, CommandKind, type SlashCommand } from "./types.js"; export const bugCommand: SlashCommand = { - name: 'bug', + name: "bug", get description() { - return t('submit a bug report'); + return t("submit a bug report"); }, kind: CommandKind.BUILT_IN, action: async (context: CommandContext, args?: string): Promise => { - const bugDescription = (args || '').trim(); + const bugDescription = (args || "").trim(); const systemInfo = await getExtendedSystemInfo(context); const fields = getSystemInfoFields(systemInfo); - const info = fields - .map((field) => `${field.label}: ${field.value}`) - .join('\n'); + const info = fields.map((field) => `${field.label}: ${field.value}`).join("\n"); let bugReportUrl = - 'https://github.com/QwenLM/airiscode/issues/new?template=bug_report.yml&title={title}&info={info}'; + "https://github.com/QwenLM/airiscode/issues/new?template=bug_report.yml&title={title}&info={info}"; const bugCommandSettings = context.services.config?.getBugCommand(); if (bugCommandSettings?.urlTemplate) { @@ -40,8 +34,8 @@ export const bugCommand: SlashCommand = { } bugReportUrl = bugReportUrl - .replace('{title}', encodeURIComponent(bugDescription)) - .replace('{info}', encodeURIComponent(`\n${info}\n`)); + .replace("{title}", encodeURIComponent(bugDescription)) + .replace("{info}", encodeURIComponent(`\n${info}\n`)); context.ui.addItem( { @@ -54,8 +48,7 @@ export const bugCommand: SlashCommand = { try { await open(bugReportUrl); } catch (error) { - const errorMessage = - error instanceof Error ? error.message : String(error); + const errorMessage = error instanceof Error ? error.message : String(error); context.ui.addItem( { type: MessageType.ERROR, diff --git a/apps/airiscode-cli/src/ui/commands/clearCommand.ts b/apps/airiscode-cli/src/ui/commands/clearCommand.ts index 28af33226..421a1d22d 100644 --- a/apps/airiscode-cli/src/ui/commands/clearCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/clearCommand.ts @@ -4,23 +4,23 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { SlashCommand } from './types.js'; -import { CommandKind } from './types.js'; -import { t } from '../../i18n/index.js'; import { - uiTelemetryService, + type PermissionMode, SessionEndReason, SessionStartSource, - ToolNames, SkillTool, - type PermissionMode, -} from '@airiscode/core'; + ToolNames, + uiTelemetryService, +} from "@airiscode/core"; +import { t } from "../../i18n/index.js"; +import type { SlashCommand } from "./types.js"; +import { CommandKind } from "./types.js"; export const clearCommand: SlashCommand = { - name: 'clear', - altNames: ['reset', 'new'], + name: "clear", + altNames: ["reset", "new"], get description() { - return t('Clear conversation history and free up context'); + return t("Clear conversation history and free up context"); }, kind: CommandKind.BUILT_IN, action: async (context, _args) => { @@ -59,13 +59,13 @@ export const clearCommand: SlashCommand = { const geminiClient = config.getGeminiClient(); if (geminiClient) { context.ui.setDebugMessage( - t('Starting a new session, resetting chat, and clearing terminal.'), + t("Starting a new session, resetting chat, and clearing terminal."), ); // 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(t('Starting a new session and clearing.')); + context.ui.setDebugMessage(t("Starting a new session and clearing.")); } // Fire SessionStart event (non-blocking to avoid UI lag) @@ -73,14 +73,14 @@ export const clearCommand: SlashCommand = { .getHookSystem() ?.fireSessionStartEvent( SessionStartSource.Clear, - config.getModel() ?? '', + config.getModel() ?? "", String(config.getApprovalMode()) as PermissionMode, ) .catch((err) => { config.getDebugLogger().warn(`SessionStart hook failed: ${err}`); }); } else { - context.ui.setDebugMessage(t('Starting a new session and clearing.')); + context.ui.setDebugMessage(t("Starting a new session and clearing.")); context.ui.clear(); } }, diff --git a/apps/airiscode-cli/src/ui/commands/compressCommand.ts b/apps/airiscode-cli/src/ui/commands/compressCommand.ts index cdd07d45d..3fc90f9b5 100644 --- a/apps/airiscode-cli/src/ui/commands/compressCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/compressCommand.ts @@ -4,29 +4,29 @@ * SPDX-License-Identifier: Apache-2.0 */ -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'; +import { t } from "../../i18n/index.js"; +import type { HistoryItemCompression } from "../types.js"; +import { MessageType } from "../types.js"; +import type { SlashCommand } from "./types.js"; +import { CommandKind } from "./types.js"; export const compressCommand: SlashCommand = { - name: 'compress', - altNames: ['summarize'], + name: "compress", + altNames: ["summarize"], get description() { - return t('Compresses the context by replacing it with a summary.'); + return t("Compresses the context by replacing it with a summary."); }, kind: CommandKind.BUILT_IN, action: async (context) => { const { ui } = context; - const executionMode = context.executionMode ?? 'interactive'; + const executionMode = context.executionMode ?? "interactive"; const abortSignal = context.abortSignal; - if (executionMode === 'interactive' && ui.pendingItem) { + if (executionMode === "interactive" && ui.pendingItem) { ui.addItem( { type: MessageType.ERROR, - text: t('Already compressing, wait for previous request to complete'), + text: t("Already compressing, wait for previous request to complete"), }, Date.now(), ); @@ -47,9 +47,9 @@ export const compressCommand: SlashCommand = { const geminiClient = config?.getGeminiClient(); if (!config || !geminiClient) { return { - type: 'message', - messageType: 'error', - content: t('Config not loaded.'), + type: "message", + messageType: "error", + content: t("Config not loaded."), }; } @@ -58,40 +58,40 @@ export const compressCommand: SlashCommand = { return await geminiClient.tryCompressChat(promptId, true); }; - if (executionMode === 'acp') { + if (executionMode === "acp") { const messages = async function* () { try { yield { - messageType: 'info' as const, - content: 'Compressing context...', + messageType: "info" as const, + content: "Compressing context...", }; const compressed = await doCompress(); if (!compressed) { yield { - messageType: 'error' as const, - content: t('Failed to compress chat history.'), + messageType: "error" as const, + content: t("Failed to compress chat history."), }; return; } yield { - messageType: 'info' as const, + messageType: "info" as const, content: `Context compressed (${compressed.originalTokenCount} -> ${compressed.newTokenCount}).`, }; } catch (e) { yield { - messageType: 'error' as const, - content: t('Failed to compress chat history: {{error}}', { + messageType: "error" as const, + content: t("Failed to compress chat history: {{error}}", { error: e instanceof Error ? e.message : String(e), }), }; } }; - return { type: 'stream_messages', messages: messages() }; + return { type: "stream_messages", messages: messages() }; } try { - if (executionMode === 'interactive') { + if (executionMode === "interactive") { ui.setPendingItem(pendingMessage); } @@ -102,11 +102,11 @@ export const compressCommand: SlashCommand = { } if (!compressed) { - if (executionMode === 'interactive') { + if (executionMode === "interactive") { ui.addItem( { type: MessageType.ERROR, - text: t('Failed to compress chat history.'), + text: t("Failed to compress chat history."), }, Date.now(), ); @@ -114,13 +114,13 @@ export const compressCommand: SlashCommand = { } return { - type: 'message', - messageType: 'error', - content: t('Failed to compress chat history.'), + type: "message", + messageType: "error", + content: t("Failed to compress chat history."), }; } - if (executionMode === 'interactive') { + if (executionMode === "interactive") { ui.addItem( { type: MessageType.COMPRESSION, @@ -137,8 +137,8 @@ export const compressCommand: SlashCommand = { } return { - type: 'message', - messageType: 'info', + type: "message", + messageType: "info", content: `Context compressed (${compressed.originalTokenCount} -> ${compressed.newTokenCount}).`, }; } catch (e) { @@ -146,11 +146,11 @@ export const compressCommand: SlashCommand = { if (abortSignal?.aborted) { return; } - if (executionMode === 'interactive') { + if (executionMode === "interactive") { ui.addItem( { type: MessageType.ERROR, - text: t('Failed to compress chat history: {{error}}', { + text: t("Failed to compress chat history: {{error}}", { error: e instanceof Error ? e.message : String(e), }), }, @@ -160,14 +160,14 @@ export const compressCommand: SlashCommand = { } return { - type: 'message', - messageType: 'error', - content: t('Failed to compress chat history: {{error}}', { + type: "message", + messageType: "error", + content: t("Failed to compress chat history: {{error}}", { error: e instanceof Error ? e.message : String(e), }), }; } finally { - if (executionMode === 'interactive') { + if (executionMode === "interactive") { ui.setPendingItem(null); } } diff --git a/apps/airiscode-cli/src/ui/commands/contextCommand.ts b/apps/airiscode-cli/src/ui/commands/contextCommand.ts index b8185e54e..d01ea9313 100644 --- a/apps/airiscode-cli/src/ui/commands/contextCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/contextCommand.ts @@ -5,28 +5,24 @@ */ import { - type CommandContext, - type SlashCommand, - CommandKind, -} from './types.js'; + buildSkillLlmContent, + DEFAULT_TOKEN_LIMIT, + DiscoveredMCPTool, + getCoreSystemPrompt, + SkillTool, + ToolNames, + uiTelemetryService, +} from "@airiscode/core"; +import { t } from "../../i18n/index.js"; import { - MessageType, - type HistoryItemContextUsage, type ContextCategoryBreakdown, - type ContextToolDetail, type ContextMemoryDetail, type ContextSkillDetail, -} from '../types.js'; -import { - DiscoveredMCPTool, - uiTelemetryService, - getCoreSystemPrompt, - DEFAULT_TOKEN_LIMIT, - ToolNames, - SkillTool, - buildSkillLlmContent, -} from '@airiscode/core'; -import { t } from '../../i18n/index.js'; + type ContextToolDetail, + type HistoryItemContextUsage, + MessageType, +} from "../types.js"; +import { type CommandContext, CommandKind, type SlashCommand } from "./types.js"; /** * Default compression token threshold (triggers compression at 70% usage). @@ -63,8 +59,7 @@ function parseMemoryFiles(memoryContent: string): ContextMemoryDetail[] { const results: ContextMemoryDetail[] = []; // Use backreference (\1) to ensure start/end path markers match - const regex = - /--- Context from: (.+?) ---\n([\s\S]*?)--- End of Context from: \1 ---/g; + const regex = /--- Context from: (.+?) ---\n([\s\S]*?)--- End of Context from: \1 ---/g; let match: RegExpExecArray | null; while ((match = regex.exec(memoryContent)) !== null) { @@ -79,7 +74,7 @@ function parseMemoryFiles(memoryContent: string): ContextMemoryDetail[] { // If no structured markers found, treat as a single memory block if (results.length === 0 && memoryContent.trim().length > 0) { results.push({ - path: t('memory'), + path: t("memory"), tokens: estimateTokens(memoryContent), }); } @@ -88,23 +83,20 @@ function parseMemoryFiles(memoryContent: string): ContextMemoryDetail[] { } export const contextCommand: SlashCommand = { - name: 'context', + name: "context", get description() { - return t( - 'Show context window usage breakdown. Use "/context detail" for per-item breakdown.', - ); + return t('Show context window usage breakdown. Use "/context detail" for per-item breakdown.'); }, kind: CommandKind.BUILT_IN, action: async (context: CommandContext, args?: string) => { const showDetails = - args?.trim().toLowerCase() === 'detail' || - args?.trim().toLowerCase() === '-d'; + args?.trim().toLowerCase() === "detail" || args?.trim().toLowerCase() === "-d"; const { config } = context.services; if (!config) { context.ui.addItem( { type: MessageType.ERROR, - text: t('Config not loaded.'), + text: t("Config not loaded."), }, Date.now(), ); @@ -113,10 +105,9 @@ export const contextCommand: SlashCommand = { // --- Gather data --- - const modelName = config.getModel() || 'unknown'; + const modelName = config.getModel() || "unknown"; const contentGeneratorConfig = config.getContentGeneratorConfig(); - const contextWindowSize = - contentGeneratorConfig.contextWindowSize ?? DEFAULT_TOKEN_LIMIT; + const contextWindowSize = contentGeneratorConfig.contextWindowSize ?? DEFAULT_TOKEN_LIMIT; // Total prompt token count from API (most accurate) const apiTotalTokens = uiTelemetryService.getLastPromptTokenCount(); @@ -132,9 +123,7 @@ export const contextCommand: SlashCommand = { // 2. Tool declarations tokens (includes ALL tools: built-in, MCP, skill tool) const toolRegistry = config.getToolRegistry(); const allTools = toolRegistry ? toolRegistry.getAllTools() : []; - const toolDeclarations = toolRegistry - ? toolRegistry.getFunctionDeclarations() - : []; + const toolDeclarations = toolRegistry ? toolRegistry.getFunctionDeclarations() : []; const toolsJsonStr = JSON.stringify(toolDeclarations); const allToolsTokens = estimateTokens(toolsJsonStr); @@ -178,9 +167,7 @@ export const contextCommand: SlashCommand = { // Determine which skills have been loaded in this session const loadedSkillNames: ReadonlySet = - skillTool instanceof SkillTool - ? skillTool.getLoadedSkillNames() - : new Set(); + skillTool instanceof SkillTool ? skillTool.getLoadedSkillNames() : new Set(); // Per-skill breakdown: listing cost + body cost for loaded skills const skillManager = config.getSkillManager(); @@ -193,9 +180,7 @@ export const contextCommand: SlashCommand = { const isLoaded = loadedSkillNames.has(skill.name); let bodyTokens: number | undefined; if (isLoaded && skill.body) { - const baseDir = skill.filePath - ? skill.filePath.replace(/\/[^/]+$/, '') - : ''; + const baseDir = skill.filePath ? skill.filePath.replace(/\/[^/]+$/, "") : ""; bodyTokens = estimateTokens(buildSkillLlmContent(baseDir, skill.body)); loadedBodiesTokens += bodyTokens; } @@ -212,30 +197,21 @@ export const contextCommand: SlashCommand = { // 6. Autocompact buffer const compressionThreshold = - config.getChatCompression()?.contextPercentageThreshold ?? - DEFAULT_COMPRESSION_THRESHOLD; + config.getChatCompression()?.contextPercentageThreshold ?? DEFAULT_COMPRESSION_THRESHOLD; const autocompactBuffer = - compressionThreshold > 0 - ? Math.round((1 - compressionThreshold) * contextWindowSize) - : 0; + compressionThreshold > 0 ? Math.round((1 - compressionThreshold) * contextWindowSize) : 0; // 7. Calculate raw overhead // allToolsTokens includes the skill tool definition; loadedBodiesTokens // covers the on-demand skill bodies now attributed to Skills. const rawOverhead = - systemPromptTokens + - allToolsTokens + - memoryFilesTokens + - loadedBodiesTokens; + systemPromptTokens + allToolsTokens + memoryFilesTokens + loadedBodiesTokens; // 8. Determine total tokens and build breakdown const isEstimated = apiTotalTokens === 0; // Sum of MCP tool tokens for category-level display - const mcpToolsTotalTokens = mcpTools.reduce( - (sum, tool) => sum + tool.tokens, - 0, - ); + const mcpToolsTotalTokens = mcpTools.reduce((sum, tool) => sum + tool.tokens, 0); let totalTokens: number; let displaySystemPrompt: number; @@ -268,10 +244,7 @@ export const contextCommand: SlashCommand = { displayMemoryFiles = memoryFilesTokens; messagesTokens = 0; // Free space accounts for the estimated overhead - freeSpace = Math.max( - 0, - contextWindowSize - rawOverhead - autocompactBuffer, - ); + freeSpace = Math.max(0, contextWindowSize - rawOverhead - autocompactBuffer); detailBuiltinTools = builtinTools; detailMcpTools = mcpTools; detailMemoryFiles = memoryFiles; @@ -282,8 +255,7 @@ export const contextCommand: SlashCommand = { // When estimates overshoot API total, scale down proportionally // so the breakdown categories add up to totalTokens. - const overheadScale = - rawOverhead > totalTokens ? totalTokens / rawOverhead : 1; + const overheadScale = rawOverhead > totalTokens ? totalTokens / rawOverhead : 1; displaySystemPrompt = Math.round(systemPromptTokens * overheadScale); const scaledAllTools = Math.round(allToolsTokens * overheadScale); @@ -293,13 +265,8 @@ export const contextCommand: SlashCommand = { const scaledMcpTotal = Math.round(mcpToolsTotalTokens * overheadScale); displayMcpTools = scaledMcpTotal; // builtinTools = allTools minus skill-definition minus mcpTools - const scaledSkillDefinition = Math.round( - skillToolDefinitionTokens * overheadScale, - ); - displayBuiltinTools = Math.max( - 0, - scaledAllTools - scaledSkillDefinition - scaledMcpTotal, - ); + const scaledSkillDefinition = Math.round(skillToolDefinitionTokens * overheadScale); + displayBuiltinTools = Math.max(0, scaledAllTools - scaledSkillDefinition - scaledMcpTotal); const scaledOverhead = displaySystemPrompt + @@ -317,10 +284,7 @@ export const contextCommand: SlashCommand = { messagesTokens = Math.max(0, totalTokens - scaledOverhead); } - freeSpace = Math.max( - 0, - contextWindowSize - totalTokens - autocompactBuffer, - ); + freeSpace = Math.max(0, contextWindowSize - totalTokens - autocompactBuffer); // Scale detail items to match their parent categories const scaleDetail = (items: T[]): T[] => @@ -339,9 +303,7 @@ export const contextCommand: SlashCommand = { ? skills.map((item) => ({ ...item, tokens: Math.round(item.tokens * overheadScale), - bodyTokens: item.bodyTokens - ? Math.round(item.bodyTokens * overheadScale) - : undefined, + bodyTokens: item.bodyTokens ? Math.round(item.bodyTokens * overheadScale) : undefined, })) : skills; } diff --git a/apps/airiscode-cli/src/ui/commands/copyCommand.ts b/apps/airiscode-cli/src/ui/commands/copyCommand.ts index 421b0323b..18a70591f 100644 --- a/apps/airiscode-cli/src/ui/commands/copyCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/copyCommand.ts @@ -4,15 +4,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { copyToClipboard } from '../utils/commandUtils.js'; -import type { SlashCommand, SlashCommandActionReturn } from './types.js'; -import { CommandKind } from './types.js'; -import { t } from '../../i18n/index.js'; +import { t } from "../../i18n/index.js"; +import { copyToClipboard } from "../utils/commandUtils.js"; +import type { SlashCommand, SlashCommandActionReturn } from "./types.js"; +import { CommandKind } from "./types.js"; export const copyCommand: SlashCommand = { - name: 'copy', + name: "copy", get description() { - return t('Copy the last result or code snippet to clipboard'); + return t("Copy the last result or code snippet to clipboard"); }, kind: CommandKind.BUILT_IN, action: async (context, _args): Promise => { @@ -21,46 +21,46 @@ export const copyCommand: SlashCommand = { // Get the last message from the AI (model role) const lastAiMessage = history - ? history.filter((item) => item.role === 'model').pop() + ? history.filter((item) => item.role === "model").pop() : undefined; if (!lastAiMessage) { return { - type: 'message', - messageType: 'info', - content: 'No output in history', + type: "message", + messageType: "info", + content: "No output in history", }; } // Extract text from the parts const lastAiOutput = lastAiMessage.parts ?.filter((part) => part.text) .map((part) => part.text) - .join(''); + .join(""); if (lastAiOutput) { try { await copyToClipboard(lastAiOutput); return { - type: 'message', - messageType: 'info', - content: 'Last output copied to the clipboard', + type: "message", + messageType: "info", + content: "Last output copied to the clipboard", }; } catch (error) { const message = error instanceof Error ? error.message : String(error); context.services.config?.getDebugLogger().debug(message); return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: `Failed to copy to the clipboard. ${message}`, }; } } else { return { - type: 'message', - messageType: 'info', - content: 'Last AI output contains no text to copy.', + type: "message", + messageType: "info", + content: "Last AI output contains no text to copy.", }; } }, diff --git a/apps/airiscode-cli/src/ui/commands/directoryCommand.tsx b/apps/airiscode-cli/src/ui/commands/directoryCommand.tsx index 5046e0ae3..663876cdb 100644 --- a/apps/airiscode-cli/src/ui/commands/directoryCommand.tsx +++ b/apps/airiscode-cli/src/ui/commands/directoryCommand.tsx @@ -4,23 +4,23 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { SlashCommand, CommandContext } from './types.js'; -import { CommandKind } from './types.js'; -import { MessageType } from '../types.js'; -import * as fs from 'node:fs'; -import * as os from 'node:os'; -import * as path from 'node:path'; -import { loadServerHierarchicalMemory } from '@airiscode/core'; -import { t } from '../../i18n/index.js'; +import * as fs from "node:fs"; +import * as os from "node:os"; +import * as path from "node:path"; +import { loadServerHierarchicalMemory } from "@airiscode/core"; +import { t } from "../../i18n/index.js"; +import { MessageType } from "../types.js"; +import type { CommandContext, SlashCommand } from "./types.js"; +import { CommandKind } from "./types.js"; export function expandHomeDir(p: string): string { if (!p) { - return ''; + return ""; } let expandedPath = p; - if (p.toLowerCase().startsWith('%userprofile%')) { - expandedPath = os.homedir() + p.substring('%userprofile%'.length); - } else if (p === '~' || p.startsWith('~/')) { + if (p.toLowerCase().startsWith("%userprofile%")) { + expandedPath = os.homedir() + p.substring("%userprofile%".length); + } else if (p === "~" || p.startsWith("~/")) { expandedPath = os.homedir() + p.substring(1); } return path.normalize(expandedPath); @@ -31,32 +31,22 @@ export function expandHomeDir(p: string): string { * Supports comma-separated paths by completing only the last segment. */ export function getDirPathCompletions(partialArg: string): string[] { - const lastComma = partialArg.lastIndexOf(','); - const prefix = lastComma >= 0 ? partialArg.substring(0, lastComma + 1) : ''; - const partial = - lastComma >= 0 - ? partialArg.substring(lastComma + 1).trimStart() - : partialArg; + const lastComma = partialArg.lastIndexOf(","); + const prefix = lastComma >= 0 ? partialArg.substring(0, lastComma + 1) : ""; + const partial = lastComma >= 0 ? partialArg.substring(lastComma + 1).trimStart() : partialArg; const trimmed = partial.trim(); if (!trimmed) return []; - const expanded = trimmed.startsWith('~') - ? trimmed.replace(/^~/, os.homedir()) - : trimmed; - const endsWithSep = expanded.endsWith('/') || expanded.endsWith(path.sep); + const expanded = trimmed.startsWith("~") ? trimmed.replace(/^~/, os.homedir()) : trimmed; + const endsWithSep = expanded.endsWith("/") || expanded.endsWith(path.sep); const searchDir = endsWithSep ? expanded : path.dirname(expanded); - const namePrefix = endsWithSep ? '' : path.basename(expanded); + const namePrefix = endsWithSep ? "" : path.basename(expanded); try { return fs .readdirSync(searchDir, { withFileTypes: true }) - .filter( - (e) => - e.isDirectory() && - e.name.startsWith(namePrefix) && - !e.name.startsWith('.'), - ) + .filter((e) => e.isDirectory() && e.name.startsWith(namePrefix) && !e.name.startsWith(".")) .map((e) => prefix + path.join(searchDir, e.name)) .slice(0, 8); } catch { @@ -65,19 +55,17 @@ export function getDirPathCompletions(partialArg: string): string[] { } export const directoryCommand: SlashCommand = { - name: 'directory', - altNames: ['dir'], + name: "directory", + altNames: ["dir"], get description() { - return t('Manage workspace directories'); + return t("Manage workspace directories"); }, kind: CommandKind.BUILT_IN, subCommands: [ { - name: 'add', + name: "add", get description() { - return t( - 'Add directories to the workspace. Use comma to separate multiple paths', - ); + return t("Add directories to the workspace. Use comma to separate multiple paths"); }, kind: CommandKind.BUILT_IN, completion: async (_context: CommandContext, partialArg: string) => @@ -87,13 +75,13 @@ export const directoryCommand: SlashCommand = { ui: { addItem }, services: { config }, } = context; - const [...rest] = args.split(' '); + const [...rest] = args.split(" "); if (!config) { addItem( { type: MessageType.ERROR, - text: t('Configuration is not available.'), + text: t("Configuration is not available."), }, Date.now(), ); @@ -103,14 +91,14 @@ export const directoryCommand: SlashCommand = { const workspaceContext = config.getWorkspaceContext(); const pathsToAdd = rest - .join(' ') - .split(',') + .join(" ") + .split(",") .filter((p) => p); if (pathsToAdd.length === 0) { addItem( { type: MessageType.ERROR, - text: t('Please provide at least one path to add.'), + text: t("Please provide at least one path to add."), }, Date.now(), ); @@ -119,10 +107,10 @@ export const directoryCommand: SlashCommand = { if (config.isRestrictiveSandbox()) { return { - type: 'message' as const, - messageType: 'error' as const, + type: "message" as const, + messageType: "error" as const, content: t( - 'The /directory add command is not supported in restrictive sandbox profiles. Please use --include-directories when starting the session instead.', + "The /directory add command is not supported in restrictive sandbox profiles. Please use --include-directories when starting the session instead.", ), }; } @@ -147,19 +135,14 @@ export const directoryCommand: SlashCommand = { try { if (config.shouldLoadMemoryFromIncludeDirectories()) { - const { memoryContent, fileCount } = - await loadServerHierarchicalMemory( - config.getWorkingDir(), - [ - ...config.getWorkspaceContext().getDirectories(), - ...pathsToAdd, - ], - config.getFileService(), - config.getExtensionContextFilePaths(), - config.getFolderTrust(), - context.services.settings.merged.context?.importFormat || - 'tree', // Use setting or default to 'tree' - ); + const { memoryContent, fileCount } = await loadServerHierarchicalMemory( + config.getWorkingDir(), + [...config.getWorkspaceContext().getDirectories(), ...pathsToAdd], + config.getFileService(), + config.getExtensionContextFilePaths(), + config.getFolderTrust(), + context.services.settings.merged.context?.importFormat || "tree", // Use setting or default to 'tree' + ); config.setUserMemory(memoryContent); config.setGeminiMdFileCount(fileCount); context.ui.setGeminiMdFileCount(fileCount); @@ -168,9 +151,9 @@ export const directoryCommand: SlashCommand = { { type: MessageType.INFO, text: t( - 'Successfully added AIRISCODE.md files from the following directories if there are:\n- {{directories}}', + "Successfully added AIRISCODE.md files from the following directories if there are:\n- {{directories}}", { - directories: added.join('\n- '), + directories: added.join("\n- "), }, ), }, @@ -178,7 +161,7 @@ export const directoryCommand: SlashCommand = { ); } catch (error) { errors.push( - t('Error refreshing memory: {{error}}', { + t("Error refreshing memory: {{error}}", { error: (error as Error).message, }), ); @@ -192,8 +175,8 @@ export const directoryCommand: SlashCommand = { addItem( { type: MessageType.INFO, - text: t('Successfully added directories:\n- {{directories}}', { - directories: added.join('\n- '), + text: t("Successfully added directories:\n- {{directories}}", { + directories: added.join("\n- "), }), }, Date.now(), @@ -201,18 +184,15 @@ export const directoryCommand: SlashCommand = { } if (errors.length > 0) { - addItem( - { type: MessageType.ERROR, text: errors.join('\n') }, - Date.now(), - ); + addItem({ type: MessageType.ERROR, text: errors.join("\n") }, Date.now()); } return; }, }, { - name: 'show', + name: "show", get description() { - return t('Show all directories in the workspace'); + return t("Show all directories in the workspace"); }, kind: CommandKind.BUILT_IN, action: async (context: CommandContext) => { @@ -224,7 +204,7 @@ export const directoryCommand: SlashCommand = { addItem( { type: MessageType.ERROR, - text: t('Configuration is not available.'), + text: t("Configuration is not available."), }, Date.now(), ); @@ -232,11 +212,11 @@ export const directoryCommand: SlashCommand = { } const workspaceContext = config.getWorkspaceContext(); const directories = workspaceContext.getDirectories(); - const directoryList = directories.map((dir) => `- ${dir}`).join('\n'); + const directoryList = directories.map((dir) => `- ${dir}`).join("\n"); addItem( { type: MessageType.INFO, - text: t('Current workspace directories:\n{{directories}}', { + text: t("Current workspace directories:\n{{directories}}", { directories: directoryList, }), }, diff --git a/apps/airiscode-cli/src/ui/commands/docsCommand.ts b/apps/airiscode-cli/src/ui/commands/docsCommand.ts index e03f2d399..a94f89a82 100644 --- a/apps/airiscode-cli/src/ui/commands/docsCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/docsCommand.ts @@ -4,32 +4,28 @@ * SPDX-License-Identifier: Apache-2.0 */ -import open from 'open'; -import process from 'node:process'; -import { - type CommandContext, - type SlashCommand, - CommandKind, -} from './types.js'; -import { MessageType } from '../types.js'; -import { t, getCurrentLanguage } from '../../i18n/index.js'; +import process from "node:process"; +import open from "open"; +import { getCurrentLanguage, t } from "../../i18n/index.js"; +import { MessageType } from "../types.js"; +import { type CommandContext, CommandKind, type SlashCommand } from "./types.js"; export const docsCommand: SlashCommand = { - name: 'docs', + name: "docs", get description() { - return t('open full AIRIS Code documentation in your browser'); + return t("open full AIRIS Code documentation in your browser"); }, kind: CommandKind.BUILT_IN, action: async (context: CommandContext): Promise => { - const langPath = getCurrentLanguage()?.startsWith('zh') ? 'zh' : 'en'; + const langPath = getCurrentLanguage()?.startsWith("zh") ? "zh" : "en"; const docsUrl = `https://qwenlm.github.io/airiscode-docs/${langPath}`; - if (process.env['SANDBOX'] && process.env['SANDBOX'] !== 'sandbox-exec') { + if (process.env["SANDBOX"] && process.env["SANDBOX"] !== "sandbox-exec") { context.ui.addItem( { type: MessageType.INFO, text: t( - 'Please open the following URL in your browser to view the documentation:\n{{url}}', + "Please open the following URL in your browser to view the documentation:\n{{url}}", { url: docsUrl, }, @@ -41,7 +37,7 @@ export const docsCommand: SlashCommand = { context.ui.addItem( { type: MessageType.INFO, - text: t('Opening documentation in your browser: {{url}}', { + text: t("Opening documentation in your browser: {{url}}", { url: docsUrl, }), }, diff --git a/apps/airiscode-cli/src/ui/commands/editorCommand.ts b/apps/airiscode-cli/src/ui/commands/editorCommand.ts index f39cbdbca..59441b02a 100644 --- a/apps/airiscode-cli/src/ui/commands/editorCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/editorCommand.ts @@ -4,21 +4,17 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { - CommandKind, - type OpenDialogActionReturn, - type SlashCommand, -} from './types.js'; -import { t } from '../../i18n/index.js'; +import { t } from "../../i18n/index.js"; +import { CommandKind, type OpenDialogActionReturn, type SlashCommand } from "./types.js"; export const editorCommand: SlashCommand = { - name: 'editor', + name: "editor", get description() { - return t('set external editor preference'); + return t("set external editor preference"); }, kind: CommandKind.BUILT_IN, action: (): OpenDialogActionReturn => ({ - type: 'dialog', - dialog: 'editor', + type: "dialog", + dialog: "editor", }), }; diff --git a/apps/airiscode-cli/src/ui/commands/exportCommand.ts b/apps/airiscode-cli/src/ui/commands/exportCommand.ts index e77e722bd..2e664e93d 100644 --- a/apps/airiscode-cli/src/ui/commands/exportCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/exportCommand.ts @@ -4,49 +4,47 @@ * SPDX-License-Identifier: Apache-2.0 */ -import * as fs from 'node:fs/promises'; -import path from 'node:path'; -import { - type CommandContext, - type SlashCommand, - type MessageActionReturn, - CommandKind, -} from './types.js'; -import { SessionService } from '@airiscode/core'; +import * as fs from "node:fs/promises"; +import path from "node:path"; +import { SessionService } from "@airiscode/core"; +import { t } from "../../i18n/index.js"; import { collectSessionData, + generateExportFilename, normalizeSessionData, - toMarkdown, toHtml, toJson, toJsonl, - generateExportFilename, -} from '../utils/export/index.js'; -import { t } from '../../i18n/index.js'; + toMarkdown, +} from "../utils/export/index.js"; +import { + type CommandContext, + CommandKind, + type MessageActionReturn, + type SlashCommand, +} from "./types.js"; /** * Action for the 'md' subcommand - exports session to markdown. */ -async function exportMarkdownAction( - context: CommandContext, -): Promise { +async function exportMarkdownAction(context: CommandContext): Promise { const { services } = context; const { config } = services; if (!config) { return { - type: 'message', - messageType: 'error', - content: 'Configuration not available.', + type: "message", + messageType: "error", + content: "Configuration not available.", }; } const cwd = config.getWorkingDir() || config.getProjectRoot(); if (!cwd) { return { - type: 'message', - messageType: 'error', - content: 'Could not determine current working directory.', + type: "message", + messageType: "error", + content: "Could not determine current working directory.", }; } @@ -58,9 +56,9 @@ async function exportMarkdownAction( if (!sessionData) { return { - type: 'message', - messageType: 'error', - content: 'No active session found to export.', + type: "message", + messageType: "error", + content: "No active session found to export.", }; } @@ -68,30 +66,26 @@ async function exportMarkdownAction( // Collect and normalize export data (SSOT) const exportData = await collectSessionData(conversation, config); - const normalizedData = normalizeSessionData( - exportData, - conversation.messages, - config, - ); + const normalizedData = normalizeSessionData(exportData, conversation.messages, config); // Generate markdown from SSOT const markdown = toMarkdown(normalizedData); - const filename = generateExportFilename('md'); + const filename = generateExportFilename("md"); const filepath = path.join(cwd, filename); // Write to file - await fs.writeFile(filepath, markdown, 'utf-8'); + await fs.writeFile(filepath, markdown, "utf-8"); return { - type: 'message', - messageType: 'info', + type: "message", + messageType: "info", content: `Session exported to markdown: ${filename}`, }; } catch (error) { return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: `Failed to export session: ${error instanceof Error ? error.message : String(error)}`, }; } @@ -100,26 +94,24 @@ async function exportMarkdownAction( /** * Action for the 'html' subcommand - exports session to HTML. */ -async function exportHtmlAction( - context: CommandContext, -): Promise { +async function exportHtmlAction(context: CommandContext): Promise { const { services } = context; const { config } = services; if (!config) { return { - type: 'message', - messageType: 'error', - content: 'Configuration not available.', + type: "message", + messageType: "error", + content: "Configuration not available.", }; } const cwd = config.getWorkingDir() || config.getProjectRoot(); if (!cwd) { return { - type: 'message', - messageType: 'error', - content: 'Could not determine current working directory.', + type: "message", + messageType: "error", + content: "Could not determine current working directory.", }; } @@ -131,9 +123,9 @@ async function exportHtmlAction( if (!sessionData) { return { - type: 'message', - messageType: 'error', - content: 'No active session found to export.', + type: "message", + messageType: "error", + content: "No active session found to export.", }; } @@ -141,30 +133,26 @@ async function exportHtmlAction( // Collect and normalize export data (SSOT) const exportData = await collectSessionData(conversation, config); - const normalizedData = normalizeSessionData( - exportData, - conversation.messages, - config, - ); + const normalizedData = normalizeSessionData(exportData, conversation.messages, config); // Generate HTML from SSOT const html = toHtml(normalizedData); - const filename = generateExportFilename('html'); + const filename = generateExportFilename("html"); const filepath = path.join(cwd, filename); // Write to file - await fs.writeFile(filepath, html, 'utf-8'); + await fs.writeFile(filepath, html, "utf-8"); return { - type: 'message', - messageType: 'info', + type: "message", + messageType: "info", content: `Session exported to HTML: ${filename}`, }; } catch (error) { return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: `Failed to export session: ${error instanceof Error ? error.message : String(error)}`, }; } @@ -173,26 +161,24 @@ async function exportHtmlAction( /** * Action for the 'json' subcommand - exports session to JSON. */ -async function exportJsonAction( - context: CommandContext, -): Promise { +async function exportJsonAction(context: CommandContext): Promise { const { services } = context; const { config } = services; if (!config) { return { - type: 'message', - messageType: 'error', - content: 'Configuration not available.', + type: "message", + messageType: "error", + content: "Configuration not available.", }; } const cwd = config.getWorkingDir() || config.getProjectRoot(); if (!cwd) { return { - type: 'message', - messageType: 'error', - content: 'Could not determine current working directory.', + type: "message", + messageType: "error", + content: "Could not determine current working directory.", }; } @@ -204,9 +190,9 @@ async function exportJsonAction( if (!sessionData) { return { - type: 'message', - messageType: 'error', - content: 'No active session found to export.', + type: "message", + messageType: "error", + content: "No active session found to export.", }; } @@ -214,30 +200,26 @@ async function exportJsonAction( // Collect and normalize export data (SSOT) const exportData = await collectSessionData(conversation, config); - const normalizedData = normalizeSessionData( - exportData, - conversation.messages, - config, - ); + const normalizedData = normalizeSessionData(exportData, conversation.messages, config); // Generate JSON from SSOT const json = toJson(normalizedData); - const filename = generateExportFilename('json'); + const filename = generateExportFilename("json"); const filepath = path.join(cwd, filename); // Write to file - await fs.writeFile(filepath, json, 'utf-8'); + await fs.writeFile(filepath, json, "utf-8"); return { - type: 'message', - messageType: 'info', + type: "message", + messageType: "info", content: `Session exported to JSON: ${filename}`, }; } catch (error) { return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: `Failed to export session: ${error instanceof Error ? error.message : String(error)}`, }; } @@ -246,26 +228,24 @@ async function exportJsonAction( /** * Action for the 'jsonl' subcommand - exports session to JSONL. */ -async function exportJsonlAction( - context: CommandContext, -): Promise { +async function exportJsonlAction(context: CommandContext): Promise { const { services } = context; const { config } = services; if (!config) { return { - type: 'message', - messageType: 'error', - content: 'Configuration not available.', + type: "message", + messageType: "error", + content: "Configuration not available.", }; } const cwd = config.getWorkingDir() || config.getProjectRoot(); if (!cwd) { return { - type: 'message', - messageType: 'error', - content: 'Could not determine current working directory.', + type: "message", + messageType: "error", + content: "Could not determine current working directory.", }; } @@ -277,9 +257,9 @@ async function exportJsonlAction( if (!sessionData) { return { - type: 'message', - messageType: 'error', - content: 'No active session found to export.', + type: "message", + messageType: "error", + content: "No active session found to export.", }; } @@ -287,30 +267,26 @@ async function exportJsonlAction( // Collect and normalize export data (SSOT) const exportData = await collectSessionData(conversation, config); - const normalizedData = normalizeSessionData( - exportData, - conversation.messages, - config, - ); + const normalizedData = normalizeSessionData(exportData, conversation.messages, config); // Generate JSONL from SSOT const jsonl = toJsonl(normalizedData); - const filename = generateExportFilename('jsonl'); + const filename = generateExportFilename("jsonl"); const filepath = path.join(cwd, filename); // Write to file - await fs.writeFile(filepath, jsonl, 'utf-8'); + await fs.writeFile(filepath, jsonl, "utf-8"); return { - type: 'message', - messageType: 'info', + type: "message", + messageType: "info", content: `Session exported to JSONL: ${filename}`, }; } catch (error) { return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: `Failed to export session: ${error instanceof Error ? error.message : String(error)}`, }; } @@ -320,40 +296,40 @@ async function exportJsonlAction( * Main export command with subcommands. */ export const exportCommand: SlashCommand = { - name: 'export', + name: "export", get description() { - return t('Export current session message history to a file'); + return t("Export current session message history to a file"); }, kind: CommandKind.BUILT_IN, subCommands: [ { - name: 'html', + name: "html", get description() { - return t('Export session to HTML format'); + return t("Export session to HTML format"); }, kind: CommandKind.BUILT_IN, action: exportHtmlAction, }, { - name: 'md', + name: "md", get description() { - return t('Export session to markdown format'); + return t("Export session to markdown format"); }, kind: CommandKind.BUILT_IN, action: exportMarkdownAction, }, { - name: 'json', + name: "json", get description() { - return t('Export session to JSON format'); + return t("Export session to JSON format"); }, kind: CommandKind.BUILT_IN, action: exportJsonAction, }, { - name: 'jsonl', + name: "jsonl", get description() { - return t('Export session to JSONL format (one message per line)'); + return t("Export session to JSONL format (one message per line)"); }, kind: CommandKind.BUILT_IN, action: exportJsonlAction, diff --git a/apps/airiscode-cli/src/ui/commands/extensionsCommand.ts b/apps/airiscode-cli/src/ui/commands/extensionsCommand.ts index 42f40d080..bb17779e2 100644 --- a/apps/airiscode-cli/src/ui/commands/extensionsCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/extensionsCommand.ts @@ -4,64 +4,51 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { getErrorMessage } from '../../utils/errors.js'; -import { MessageType } from '../types.js'; -import { - type CommandContext, - type SlashCommand, - CommandKind, -} from './types.js'; -import { t } from '../../i18n/index.js'; -import { - ExtensionManager, - parseInstallSource, - createDebugLogger, -} from '@airiscode/core'; -import open from 'open'; - -const debugLogger = createDebugLogger('EXTENSIONS_COMMAND'); +import { createDebugLogger, ExtensionManager, parseInstallSource } from "@airiscode/core"; +import open from "open"; +import { t } from "../../i18n/index.js"; +import { getErrorMessage } from "../../utils/errors.js"; +import { MessageType } from "../types.js"; +import { type CommandContext, CommandKind, type SlashCommand } from "./types.js"; + +const debugLogger = createDebugLogger("EXTENSIONS_COMMAND"); const EXTENSION_EXPLORE_URL = { - Gemini: 'https://geminicli.com/extensions/', - ClaudeCode: 'https://claudemarketplaces.com/', + Gemini: "https://geminicli.com/extensions/", + ClaudeCode: "https://claudemarketplaces.com/", } as const; type ExtensionExploreSource = keyof typeof EXTENSION_EXPLORE_URL; async function exploreAction(context: CommandContext, args: string) { const source = args.trim(); - const extensionsUrl = source - ? EXTENSION_EXPLORE_URL[source as ExtensionExploreSource] - : ''; + const extensionsUrl = source ? EXTENSION_EXPLORE_URL[source as ExtensionExploreSource] : ""; if (!extensionsUrl) { context.ui.addItem( { type: MessageType.ERROR, - text: t('Unknown extensions source: {{source}}.', { source }), + text: t("Unknown extensions source: {{source}}.", { source }), }, Date.now(), ); return; } // Only check for NODE_ENV for explicit test mode, not for unit test framework - if (process.env['NODE_ENV'] === 'test') { + if (process.env["NODE_ENV"] === "test") { context.ui.addItem( { type: MessageType.INFO, text: t( - 'Would open extensions page in your browser: {{url}} (skipped in test environment)', + "Would open extensions page in your browser: {{url}} (skipped in test environment)", { url: extensionsUrl }, ), }, Date.now(), ); - } else if ( - process.env['SANDBOX'] && - process.env['SANDBOX'] !== 'sandbox-exec' - ) { + } else if (process.env["SANDBOX"] && process.env["SANDBOX"] !== "sandbox-exec") { context.ui.addItem( { type: MessageType.INFO, - text: t('View available extensions at {{url}}', { url: extensionsUrl }), + text: t("View available extensions at {{url}}", { url: extensionsUrl }), }, Date.now(), ); @@ -69,7 +56,7 @@ async function exploreAction(context: CommandContext, args: string) { context.ui.addItem( { type: MessageType.INFO, - text: t('Opening extensions page in your browser: {{url}}', { + text: t("Opening extensions page in your browser: {{url}}", { url: extensionsUrl, }), }, @@ -81,10 +68,9 @@ async function exploreAction(context: CommandContext, args: string) { context.ui.addItem( { type: MessageType.ERROR, - text: t( - 'Failed to open browser. Check out the extensions gallery at {{url}}', - { url: extensionsUrl }, - ), + text: t("Failed to open browser. Check out the extensions gallery at {{url}}", { + url: extensionsUrl, + }), }, Date.now(), ); @@ -94,17 +80,15 @@ async function exploreAction(context: CommandContext, args: string) { async function listAction(_context: CommandContext, _args: string) { return { - type: 'dialog' as const, - dialog: 'extensions_manage' as const, + type: "dialog" as const, + dialog: "extensions_manage" as const, }; } async function installAction(context: CommandContext, args: string) { const extensionManager = context.services.config?.getExtensionManager(); if (!(extensionManager instanceof ExtensionManager)) { - debugLogger.error( - `Cannot ${context.invocation?.name} extensions in this environment`, - ); + debugLogger.error(`Cannot ${context.invocation?.name} extensions in this environment`); return; } @@ -113,7 +97,7 @@ async function installAction(context: CommandContext, args: string) { context.ui.addItem( { type: MessageType.ERROR, - text: t('Usage: /extensions install '), + text: t("Usage: /extensions install "), }, Date.now(), ); @@ -156,53 +140,33 @@ async function installAction(context: CommandContext, args: string) { } } -export async function completeExtensions( - context: CommandContext, - partialArg: string, -) { +export async function completeExtensions(context: CommandContext, partialArg: string) { let extensions = context.services.config?.getExtensions() ?? []; - if (context.invocation?.name === 'enable') { + if (context.invocation?.name === "enable") { extensions = extensions.filter((ext) => !ext.isActive); } - if ( - context.invocation?.name === 'disable' || - context.invocation?.name === 'restart' - ) { + if (context.invocation?.name === "disable" || context.invocation?.name === "restart") { extensions = extensions.filter((ext) => ext.isActive); } const extensionNames = extensions.map((ext) => ext.name); - const suggestions = extensionNames.filter((name) => - name.startsWith(partialArg), - ); + const suggestions = extensionNames.filter((name) => name.startsWith(partialArg)); - if ( - context.invocation?.name !== 'uninstall' && - context.invocation?.name !== 'detail' - ) { - if ('--all'.startsWith(partialArg) || 'all'.startsWith(partialArg)) { - suggestions.unshift('--all'); + if (context.invocation?.name !== "uninstall" && context.invocation?.name !== "detail") { + if ("--all".startsWith(partialArg) || "all".startsWith(partialArg)) { + suggestions.unshift("--all"); } } return suggestions; } -export async function completeExtensionsAndScopes( - context: CommandContext, - partialArg: string, -) { +export async function completeExtensionsAndScopes(context: CommandContext, partialArg: string) { const completions = await completeExtensions(context, partialArg); - return completions.flatMap((s) => [ - `${s} --scope user`, - `${s} --scope workspace`, - ]); + return completions.flatMap((s) => [`${s} --scope user`, `${s} --scope workspace`]); } -export async function completeExtensionsExplore( - context: CommandContext, - partialArg: string, -) { +export async function completeExtensionsExplore(context: CommandContext, partialArg: string) { const suggestions = Object.keys(EXTENSION_EXPLORE_URL).filter((name) => name.startsWith(partialArg), ); @@ -211,9 +175,9 @@ export async function completeExtensionsExplore( } const exploreExtensionsCommand: SlashCommand = { - name: 'explore', + name: "explore", get description() { - return t('Open extensions page in your browser'); + return t("Open extensions page in your browser"); }, kind: CommandKind.BUILT_IN, action: exploreAction, @@ -221,34 +185,30 @@ const exploreExtensionsCommand: SlashCommand = { }; const manageExtensionsCommand: SlashCommand = { - name: 'manage', + name: "manage", get description() { - return t('Manage installed extensions'); + return t("Manage installed extensions"); }, kind: CommandKind.BUILT_IN, action: listAction, }; const installCommand: SlashCommand = { - name: 'install', + name: "install", get description() { - return t('Install an extension from a git repo or local path'); + return t("Install an extension from a git repo or local path"); }, kind: CommandKind.BUILT_IN, action: installAction, }; export const extensionsCommand: SlashCommand = { - name: 'extensions', + name: "extensions", get description() { - return t('Manage extensions'); + return t("Manage extensions"); }, kind: CommandKind.BUILT_IN, - subCommands: [ - manageExtensionsCommand, - installCommand, - exploreExtensionsCommand, - ], + subCommands: [manageExtensionsCommand, installCommand, exploreExtensionsCommand], action: async (context, args) => // Default to list if no subcommand is provided manageExtensionsCommand.action!(context, args), diff --git a/apps/airiscode-cli/src/ui/commands/helpCommand.ts b/apps/airiscode-cli/src/ui/commands/helpCommand.ts index 5bc3587c2..0ca0b9874 100644 --- a/apps/airiscode-cli/src/ui/commands/helpCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/helpCommand.ts @@ -4,20 +4,20 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { SlashCommand } from './types.js'; -import { CommandKind } from './types.js'; -import { MessageType, type HistoryItemHelp } from '../types.js'; -import { t } from '../../i18n/index.js'; +import { t } from "../../i18n/index.js"; +import { type HistoryItemHelp, MessageType } from "../types.js"; +import type { SlashCommand } from "./types.js"; +import { CommandKind } from "./types.js"; export const helpCommand: SlashCommand = { - name: 'help', - altNames: ['?'], + name: "help", + altNames: ["?"], kind: CommandKind.BUILT_IN, get description() { - return t('for help on AIRIS Code'); + return t("for help on AIRIS Code"); }, action: async (context) => { - const helpItem: Omit = { + const helpItem: Omit = { type: MessageType.HELP, timestamp: new Date(), }; diff --git a/apps/airiscode-cli/src/ui/commands/hooksCommand.ts b/apps/airiscode-cli/src/ui/commands/hooksCommand.ts index 56d3531b4..3f706f129 100644 --- a/apps/airiscode-cli/src/ui/commands/hooksCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/hooksCommand.ts @@ -4,29 +4,29 @@ * SPDX-License-Identifier: Apache-2.0 */ +import type { HookRegistryEntry } from "@airiscode/core"; +import { t } from "../../i18n/index.js"; import type { - SlashCommand, - SlashCommandActionReturn, CommandContext, MessageActionReturn, -} from './types.js'; -import { CommandKind } from './types.js'; -import { t } from '../../i18n/index.js'; -import type { HookRegistryEntry } from '@airiscode/core'; + SlashCommand, + SlashCommandActionReturn, +} from "./types.js"; +import { CommandKind } from "./types.js"; /** * Format hook source for display */ function formatHookSource(source: string): string { switch (source) { - case 'project': - return t('Project'); - case 'user': - return t('User'); - case 'system': - return t('System'); - case 'extensions': - return t('Extension'); + case "project": + return t("Project"); + case "user": + return t("User"); + case "system": + return t("System"); + case "extensions": + return t("Extension"); default: return source; } @@ -36,36 +36,31 @@ function formatHookSource(source: string): string { * Format hook status for display */ function formatHookStatus(enabled: boolean): string { - return enabled ? t('✓ Enabled') : t('✗ Disabled'); + return enabled ? t("✓ Enabled") : t("✗ Disabled"); } const listCommand: SlashCommand = { - name: 'list', + name: "list", get description() { - return t('List all configured hooks'); + return t("List all configured hooks"); }, kind: CommandKind.BUILT_IN, - action: async ( - context: CommandContext, - _args: string, - ): Promise => { + action: async (context: CommandContext, _args: string): Promise => { const { config } = context.services; if (!config) { return { - type: 'message', - messageType: 'error', - content: t('Config not loaded.'), + type: "message", + messageType: "error", + content: t("Config not loaded."), }; } const hookSystem = config.getHookSystem(); if (!hookSystem) { return { - type: 'message', - messageType: 'info', - content: t( - 'Hooks are not enabled. Enable hooks in settings to use this feature.', - ), + type: "message", + messageType: "info", + content: t("Hooks are not enabled. Enable hooks in settings to use this feature."), }; } @@ -74,11 +69,9 @@ const listCommand: SlashCommand = { if (allHooks.length === 0) { return { - type: 'message', - messageType: 'info', - content: t( - 'No hooks configured. Add hooks in your settings.json file.', - ), + type: "message", + messageType: "info", + content: t("No hooks configured. Add hooks in your settings.json file."), }; } @@ -97,44 +90,46 @@ const listCommand: SlashCommand = { for (const [eventName, hooks] of hooksByEvent) { output += `### ${eventName}\n`; for (const hook of hooks) { - const name = hook.config.name || ('command' in hook.config ? hook.config.command : 'inject') || 'unnamed'; + const name = + hook.config.name || + ("command" in hook.config ? hook.config.command : "inject") || + "unnamed"; const source = formatHookSource(hook.source); const status = formatHookStatus(hook.enabled); - const matcher = hook.matcher ? ` (matcher: ${hook.matcher})` : ''; + const matcher = hook.matcher ? ` (matcher: ${hook.matcher})` : ""; output += `- **${name}** [${source}] ${status}${matcher}\n`; } - output += '\n'; + output += "\n"; } return { - type: 'message', - messageType: 'info', + type: "message", + messageType: "info", content: output, }; }, }; export const hooksCommand: SlashCommand = { - name: 'hooks', + name: "hooks", get description() { - return t('Manage AIRIS Code hooks'); + return t("Manage AIRIS Code hooks"); }, kind: CommandKind.BUILT_IN, - action: async ( - context: CommandContext, - args: string, - ): Promise => { + action: async (context: CommandContext, args: string): Promise => { // In interactive mode, open the hooks dialog - const executionMode = context.executionMode ?? 'interactive'; - if (executionMode === 'interactive') { + const executionMode = context.executionMode ?? "interactive"; + if (executionMode === "interactive") { return { - type: 'dialog', - dialog: 'hooks', + type: "dialog", + dialog: "hooks", }; } // In non-interactive mode, list hooks - const result = await listCommand.action?.(context, args) as SlashCommandActionReturn | undefined; - return result ?? { type: 'message', messageType: 'info', content: '' }; + const result = (await listCommand.action?.(context, args)) as + | SlashCommandActionReturn + | undefined; + return result ?? { type: "message", messageType: "info", content: "" }; }, }; diff --git a/apps/airiscode-cli/src/ui/commands/ideCommand.ts b/apps/airiscode-cli/src/ui/commands/ideCommand.ts index 77132e15d..3b812d7a1 100644 --- a/apps/airiscode-cli/src/ui/commands/ideCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/ideCommand.ts @@ -4,44 +4,38 @@ * SPDX-License-Identifier: Apache-2.0 */ +import path from "node:path"; import { + AIRISCODE_COMPANION_EXTENSION_NAME, type Config, - IdeClient, type File, - logIdeConnection, - IdeConnectionEvent, - IdeConnectionType, -} from '@airiscode/core'; -import { - AIRISCODE_COMPANION_EXTENSION_NAME, getIdeInstaller, IDEConnectionStatus, + IdeClient, + IdeConnectionEvent, + IdeConnectionType, ideContextStore, -} from '@airiscode/core'; -import path from 'node:path'; -import type { - CommandContext, - SlashCommand, - SlashCommandActionReturn, -} from './types.js'; -import { CommandKind } from './types.js'; -import { SettingScope } from '../../config/settings.js'; -import { t } from '../../i18n/index.js'; + logIdeConnection, +} from "@airiscode/core"; +import { SettingScope } from "../../config/settings.js"; +import { t } from "../../i18n/index.js"; +import type { CommandContext, SlashCommand, SlashCommandActionReturn } from "./types.js"; +import { CommandKind } from "./types.js"; function getIdeStatusMessage(ideClient: IdeClient): { - messageType: 'info' | 'error'; + messageType: "info" | "error"; content: string; } { const connection = ideClient.getConnectionStatus(); switch (connection.status) { case IDEConnectionStatus.Connected: return { - messageType: 'info', + messageType: "info", content: `🟢 Connected to ${ideClient.getDetectedIdeDisplayName()}`, }; case IDEConnectionStatus.Connecting: return { - messageType: 'info', + messageType: "info", content: `🟡 Connecting...`, }; default: { @@ -50,7 +44,7 @@ function getIdeStatusMessage(ideClient: IdeClient): { content += `: ${connection.details}`; } return { - messageType: 'error', + messageType: "error", content, }; } @@ -69,13 +63,11 @@ function formatFileList(openFiles: File[]): string { const basename = path.basename(file.path); const isDuplicate = (basenameCounts.get(basename) || 0) > 1; const parentDir = path.basename(path.dirname(file.path)); - const displayName = isDuplicate - ? `${basename} (/${parentDir})` - : basename; + const displayName = isDuplicate ? `${basename} (/${parentDir})` : basename; - return ` - ${displayName}${file.isActive ? ' (active)' : ''}`; + return ` - ${displayName}${file.isActive ? " (active)" : ""}`; }) - .join('\n'); + .join("\n"); const infoMessage = ` (Note: The file list is limited to a number of recently accessed files within your workspace and only includes local files on disk)`; @@ -84,7 +76,7 @@ function formatFileList(openFiles: File[]): string { } async function getIdeStatusMessageWithFiles(ideClient: IdeClient): Promise<{ - messageType: 'info' | 'error'; + messageType: "info" | "error"; content: string; }> { const connection = ideClient.getConnectionStatus(); @@ -97,13 +89,13 @@ async function getIdeStatusMessageWithFiles(ideClient: IdeClient): Promise<{ content += formatFileList(openFiles); } return { - messageType: 'info', + messageType: "info", content, }; } case IDEConnectionStatus.Connecting: return { - messageType: 'info', + messageType: "info", content: `🟡 Connecting...`, }; default: { @@ -112,17 +104,14 @@ async function getIdeStatusMessageWithFiles(ideClient: IdeClient): Promise<{ content += `: ${connection.details}`; } return { - messageType: 'error', + messageType: "error", content, }; } } } -async function setIdeModeAndSyncConnection( - config: Config, - value: boolean, -): Promise { +async function setIdeModeAndSyncConnection(config: Config, value: boolean): Promise { config.setIdeMode(value); const ideClient = await IdeClient.getInstance(); if (value) { @@ -138,42 +127,41 @@ export const ideCommand = async (): Promise => { const currentIDE = ideClient.getCurrentIde(); if (!currentIDE) { return { - name: 'ide', + name: "ide", get description() { - return t('manage IDE integration'); + return t("manage IDE integration"); }, kind: CommandKind.BUILT_IN, action: (): SlashCommandActionReturn => ({ - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: t( - 'IDE integration is not supported in your current environment. To use this feature, run AIRIS Code in one of these supported IDEs: VS Code or VS Code forks.', + "IDE integration is not supported in your current environment. To use this feature, run AIRIS Code in one of these supported IDEs: VS Code or VS Code forks.", ), }) as const, }; } const ideSlashCommand: SlashCommand = { - name: 'ide', + name: "ide", get description() { - return t('manage IDE integration'); + return t("manage IDE integration"); }, kind: CommandKind.BUILT_IN, subCommands: [], }; const statusCommand: SlashCommand = { - name: 'status', + name: "status", get description() { - return t('check status of IDE integration'); + return t("check status of IDE integration"); }, kind: CommandKind.BUILT_IN, action: async (): Promise => { - const { messageType, content } = - await getIdeStatusMessageWithFiles(ideClient); + const { messageType, content } = await getIdeStatusMessageWithFiles(ideClient); return { - type: 'message', + type: "message", messageType, content, } as const; @@ -181,21 +169,21 @@ export const ideCommand = async (): Promise => { }; const installCommand: SlashCommand = { - name: 'install', + name: "install", get description() { - const ideName = ideClient.getDetectedIdeDisplayName() ?? 'IDE'; - return t('install required IDE companion for {{ideName}}', { + 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); - const isSandBox = !!process.env['SANDBOX']; + const isSandBox = !!process.env["SANDBOX"]; if (isSandBox) { context.ui.addItem( { - type: 'info', + type: "info", text: `IDE integration needs to be installed on the host. If you have already installed it, you can directly connect the ide`, }, Date.now(), @@ -206,7 +194,7 @@ export const ideCommand = async (): Promise => { const ideName = ideClient.getDetectedIdeDisplayName(); context.ui.addItem( { - type: 'error', + type: "error", text: `Automatic installation is not supported for ${ideName}. Please install the '${AIRISCODE_COMPANION_EXTENSION_NAME}' extension manually from the marketplace.`, }, Date.now(), @@ -216,7 +204,7 @@ export const ideCommand = async (): Promise => { context.ui.addItem( { - type: 'info', + type: "info", text: `Installing IDE companion...`, }, Date.now(), @@ -225,31 +213,24 @@ export const ideCommand = async (): Promise => { const result = await installer.install(); context.ui.addItem( { - type: result.success ? 'info' : 'error', + type: result.success ? "info" : "error", text: result.message, }, Date.now(), ); if (result.success) { - context.services.settings.setValue( - SettingScope.User, - 'ide.enabled', - true, - ); + context.services.settings.setValue(SettingScope.User, "ide.enabled", true); // Poll for up to 5 seconds for the extension to activate. for (let i = 0; i < 10; i++) { await setIdeModeAndSyncConnection(context.services.config!, true); - if ( - ideClient.getConnectionStatus().status === - IDEConnectionStatus.Connected - ) { + if (ideClient.getConnectionStatus().status === IDEConnectionStatus.Connected) { break; } await new Promise((resolve) => setTimeout(resolve, 500)); } const { messageType, content } = getIdeStatusMessage(ideClient); - if (messageType === 'error') { + if (messageType === "error") { context.ui.addItem( { type: messageType, @@ -271,17 +252,13 @@ export const ideCommand = async (): Promise => { }; const enableCommand: SlashCommand = { - name: 'enable', + name: "enable", get description() { - return t('enable IDE integration'); + return t("enable IDE integration"); }, kind: CommandKind.BUILT_IN, action: async (context: CommandContext) => { - context.services.settings.setValue( - SettingScope.User, - 'ide.enabled', - true, - ); + context.services.settings.setValue(SettingScope.User, "ide.enabled", true); await setIdeModeAndSyncConnection(context.services.config!, true); const { messageType, content } = getIdeStatusMessage(ideClient); context.ui.addItem( @@ -295,17 +272,13 @@ export const ideCommand = async (): Promise => { }; const disableCommand: SlashCommand = { - name: 'disable', + name: "disable", get description() { - return t('disable IDE integration'); + return t("disable IDE integration"); }, kind: CommandKind.BUILT_IN, action: async (context: CommandContext) => { - context.services.settings.setValue( - SettingScope.User, - 'ide.enabled', - false, - ); + context.services.settings.setValue(SettingScope.User, "ide.enabled", false); await setIdeModeAndSyncConnection(context.services.config!, false); const { messageType, content } = getIdeStatusMessage(ideClient); context.ui.addItem( @@ -324,11 +297,7 @@ export const ideCommand = async (): Promise => { if (isConnected) { ideSlashCommand.subCommands = [statusCommand, disableCommand]; } else { - ideSlashCommand.subCommands = [ - enableCommand, - statusCommand, - installCommand, - ]; + ideSlashCommand.subCommands = [enableCommand, statusCommand, installCommand]; } return ideSlashCommand; diff --git a/apps/airiscode-cli/src/ui/commands/initCommand.ts b/apps/airiscode-cli/src/ui/commands/initCommand.ts index 4daa1a8c8..4272e1631 100644 --- a/apps/airiscode-cli/src/ui/commands/initCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/initCommand.ts @@ -4,34 +4,27 @@ * SPDX-License-Identifier: Apache-2.0 */ -import * as fs from 'node:fs'; -import * as path from 'node:path'; -import type { - CommandContext, - SlashCommand, - SlashCommandActionReturn, -} from './types.js'; -import { getCurrentGeminiMdFilename } from '@airiscode/core'; -import { CommandKind } from './types.js'; -import { Text } from 'ink'; -import React from 'react'; -import { t } from '../../i18n/index.js'; +import * as fs from "node:fs"; +import * as path from "node:path"; +import { getCurrentGeminiMdFilename } from "@airiscode/core"; +import { Text } from "ink"; +import React from "react"; +import { t } from "../../i18n/index.js"; +import type { CommandContext, SlashCommand, SlashCommandActionReturn } from "./types.js"; +import { CommandKind } from "./types.js"; export const initCommand: SlashCommand = { - name: 'init', + name: "init", get description() { - return t('Analyzes the project and creates a tailored AIRISCODE.md file.'); + return t("Analyzes the project and creates a tailored AIRISCODE.md file."); }, kind: CommandKind.BUILT_IN, - action: async ( - context: CommandContext, - _args: string, - ): Promise => { + action: async (context: CommandContext, _args: string): Promise => { if (!context.services.config) { return { - type: 'message', - messageType: 'error', - content: t('Configuration not available.'), + type: "message", + messageType: "error", + content: t("Configuration not available."), }; } const targetDir = context.services.config.getTargetDir(); @@ -42,12 +35,12 @@ export const initCommand: SlashCommand = { if (fs.existsSync(contextFilePath)) { // If file exists but is empty (or whitespace), continue to initialize try { - const existing = fs.readFileSync(contextFilePath, 'utf8'); + const existing = fs.readFileSync(contextFilePath, "utf8"); if (existing && existing.trim().length > 0) { // File exists and has content - ask for confirmation to overwrite if (!context.overwriteConfirmed) { return { - type: 'confirm_action', + type: "confirm_action", // TODO: Move to .tsx file to use JSX syntax instead of React.createElement // For now, using React.createElement to maintain .ts compatibility for PR review prompt: React.createElement( @@ -56,7 +49,7 @@ export const initCommand: SlashCommand = { `A ${contextFileName} file already exists in this directory. Do you want to regenerate it?`, ), originalInvocation: { - raw: context.invocation?.raw || '/init', + raw: context.invocation?.raw || "/init", }, }; } @@ -69,31 +62,31 @@ export const initCommand: SlashCommand = { // Ensure an empty context file exists before prompting the model to populate it try { - fs.writeFileSync(contextFilePath, '', 'utf8'); + fs.writeFileSync(contextFilePath, "", "utf8"); context.ui.addItem( { - type: 'info', + type: "info", text: `Empty ${contextFileName} created. Now analyzing the project to populate it.`, }, Date.now(), ); } catch (err) { return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: `Failed to create ${contextFileName}: ${err instanceof Error ? err.message : String(err)}`, }; } } catch (error) { return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: `Unexpected error preparing ${contextFileName}: ${error instanceof Error ? error.message : String(error)}`, }; } return { - type: 'submit_prompt', + type: "submit_prompt", content: ` You are AIRIS Code, an interactive CLI agent. Analyze the current directory and generate a comprehensive ${contextFileName} file to be used as instructional context for future interactions. diff --git a/apps/airiscode-cli/src/ui/commands/insightCommand.ts b/apps/airiscode-cli/src/ui/commands/insightCommand.ts index fdfed82ba..58d1d8031 100644 --- a/apps/airiscode-cli/src/ui/commands/insightCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/insightCommand.ts @@ -4,43 +4,35 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { CommandContext, SlashCommand } from './types.js'; -import { CommandKind } from './types.js'; -import { MessageType } from '../types.js'; -import type { HistoryItemInsightProgress } from '../types.js'; -import { t } from '../../i18n/index.js'; -import { join } from 'path'; -import { StaticInsightGenerator } from '../../services/insight/generators/StaticInsightGenerator.js'; -import { createDebugLogger, Storage } from '@airiscode/core'; -import open from 'open'; - -const logger = createDebugLogger('DataProcessor'); +import { createDebugLogger, Storage } from "@airiscode/core"; +import open from "open"; +import { join } from "path"; +import { t } from "../../i18n/index.js"; +import { StaticInsightGenerator } from "../../services/insight/generators/StaticInsightGenerator.js"; +import type { HistoryItemInsightProgress } from "../types.js"; +import { MessageType } from "../types.js"; +import type { CommandContext, SlashCommand } from "./types.js"; +import { CommandKind } from "./types.js"; + +const logger = createDebugLogger("DataProcessor"); export const insightCommand: SlashCommand = { - name: 'insight', + name: "insight", get description() { - return t( - 'generate personalized programming insights from your chat history', - ); + return t("generate personalized programming insights from your chat history"); }, kind: CommandKind.BUILT_IN, action: async (context: CommandContext) => { try { - context.ui.setDebugMessage(t('Generating insights...')); + context.ui.setDebugMessage(t("Generating insights...")); - const projectsDir = join(Storage.getRuntimeBaseDir(), 'projects'); + const projectsDir = join(Storage.getRuntimeBaseDir(), "projects"); if (!context.services.config) { - throw new Error('Config service is not available'); + throw new Error("Config service is not available"); } - const insightGenerator = new StaticInsightGenerator( - context.services.config, - ); + const insightGenerator = new StaticInsightGenerator(context.services.config); - const updateProgress = ( - stage: string, - progress: number, - detail?: string, - ) => { + const updateProgress = (stage: string, progress: number, detail?: string) => { const progressItem: HistoryItemInsightProgress = { type: MessageType.INSIGHT_PROGRESS, progress: { @@ -55,19 +47,16 @@ export const insightCommand: SlashCommand = { context.ui.addItem( { type: MessageType.INFO, - text: t('This may take a couple minutes. Sit tight!'), + text: t("This may take a couple minutes. Sit tight!"), }, Date.now(), ); // Initial progress - updateProgress(t('Starting insight generation...'), 0); + updateProgress(t("Starting insight generation..."), 0); // Generate the static insight HTML file - const outputPath = await insightGenerator.generateStaticInsight( - projectsDir, - updateProgress, - ); + const outputPath = await insightGenerator.generateStaticInsight(projectsDir, updateProgress); // Clear pending item context.ui.setPendingItem(null); @@ -75,7 +64,7 @@ export const insightCommand: SlashCommand = { context.ui.addItem( { type: MessageType.INFO, - text: t('Insight report generated successfully!'), + text: t("Insight report generated successfully!"), }, Date.now(), ); @@ -87,30 +76,27 @@ export const insightCommand: SlashCommand = { context.ui.addItem( { type: MessageType.INFO, - text: t('Opening insights in your browser: {{path}}', { + text: t("Opening insights in your browser: {{path}}", { path: outputPath, }), }, Date.now(), ); } catch (browserError) { - logger.error('Failed to open browser automatically:', browserError); + logger.error("Failed to open browser automatically:", browserError); context.ui.addItem( { type: MessageType.INFO, - text: t( - 'Insights generated at: {{path}}. Please open this file in your browser.', - { - path: outputPath, - }, - ), + text: t("Insights generated at: {{path}}. Please open this file in your browser.", { + path: outputPath, + }), }, Date.now(), ); } - context.ui.setDebugMessage(t('Insights ready.')); + context.ui.setDebugMessage(t("Insights ready.")); } catch (error) { // Clear pending item on error context.ui.setPendingItem(null); @@ -118,14 +104,14 @@ export const insightCommand: SlashCommand = { context.ui.addItem( { type: MessageType.ERROR, - text: t('Failed to generate insights: {{error}}', { + text: t("Failed to generate insights: {{error}}", { error: (error as Error).message, }), }, Date.now(), ); - logger.error('Insight generation error:', error); + logger.error("Insight generation error:", error); } }, }; diff --git a/apps/airiscode-cli/src/ui/commands/languageCommand.ts b/apps/airiscode-cli/src/ui/commands/languageCommand.ts index e21f046aa..f939bf519 100644 --- a/apps/airiscode-cli/src/ui/commands/languageCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/languageCommand.ts @@ -4,33 +4,30 @@ * 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 { createDebugLogger } from "@airiscode/core"; +import { SettingScope } from "../../config/settings.js"; import { - setLanguageAsync, getCurrentLanguage, type SupportedLanguage, + setLanguageAsync, t, -} from '../../i18n/index.js'; +} from "../../i18n/index.js"; +import { getSupportedLanguageIds, SUPPORTED_LANGUAGES } from "../../i18n/languages.js"; import { - SUPPORTED_LANGUAGES, - getSupportedLanguageIds, -} from '../../i18n/languages.js'; -import { - OUTPUT_LANGUAGE_AUTO, isAutoLanguage, + OUTPUT_LANGUAGE_AUTO, resolveOutputLanguage, updateOutputLanguageFile, -} from '../../utils/languageUtils.js'; -import { createDebugLogger } from '@airiscode/core'; +} from "../../utils/languageUtils.js"; +import type { + CommandContext, + MessageActionReturn, + SlashCommand, + SlashCommandActionReturn, +} from "./types.js"; +import { CommandKind } from "./types.js"; -const debugLogger = createDebugLogger('LANGUAGE_COMMAND'); +const debugLogger = createDebugLogger("LANGUAGE_COMMAND"); /** * Gets the current LLM output language setting and its resolved value. @@ -41,8 +38,7 @@ function getCurrentOutputLanguage(context?: CommandContext): { resolved: string; } { const settingValue = - context?.services?.settings?.merged?.general?.outputLanguage || - OUTPUT_LANGUAGE_AUTO; + context?.services?.settings?.merged?.general?.outputLanguage || OUTPUT_LANGUAGE_AUTO; const resolved = resolveOutputLanguage(settingValue); return { setting: settingValue, resolved }; } @@ -89,9 +85,9 @@ async function setUiLanguage( if (!services.config) { return { - type: 'message', - messageType: 'error', - content: t('Configuration not available.'), + type: "message", + messageType: "error", + content: t("Configuration not available."), }; } @@ -101,9 +97,9 @@ async function setUiLanguage( // Persist to settings if (services.settings?.setValue) { try { - services.settings.setValue(SettingScope.User, 'general.language', lang); + services.settings.setValue(SettingScope.User, "general.language", lang); } catch (error) { - debugLogger.warn('Failed to save language setting:', error); + debugLogger.warn("Failed to save language setting:", error); } } @@ -111,9 +107,9 @@ async function setUiLanguage( context.ui.reloadCommands(); return { - type: 'message', - messageType: 'info', - content: t('UI language changed to {{lang}}', { + type: "message", + messageType: "info", + content: t("UI language changed to {{lang}}", { lang: formatUiLanguageDisplay(lang), }), }; @@ -141,58 +137,50 @@ async function setOutputLanguage( try { context.services.settings.setValue( SettingScope.User, - 'general.outputLanguage', + "general.outputLanguage", settingValue, ); } catch (error) { - debugLogger.warn('Failed to save output language setting:', error); + debugLogger.warn("Failed to save output language setting:", error); } } // Format display message - const displayLang = isAuto - ? `${t('Auto (detect from system)')} → ${resolved}` - : resolved; + const displayLang = isAuto ? `${t("Auto (detect from system)")} → ${resolved}` : resolved; return { - type: 'message', - messageType: 'info', + type: "message", + messageType: "info", content: [ - t('LLM output language set to {{lang}}', { lang: displayLang }), - '', - t('Please restart the application for the changes to take effect.'), - ].join('\n'), + t("LLM output language set to {{lang}}", { lang: displayLang }), + "", + t("Please restart the application for the changes to take effect."), + ].join("\n"), }; } catch (error) { return { - type: 'message', - messageType: 'error', - content: t( - 'Failed to generate LLM output language rule file: {{error}}', - { - error: error instanceof Error ? error.message : String(error), - }, - ), + 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', + name: "language", get description() { - return t('View or change the language setting'); + return t("View or change the language setting"); }, kind: CommandKind.BUILT_IN, - action: async ( - context: CommandContext, - args: string, - ): Promise => { + action: async (context: CommandContext, args: string): Promise => { if (!context.services.config) { return { - type: 'message', - messageType: 'error', - content: t('Configuration not available.'), + type: "message", + messageType: "error", + content: t("Configuration not available."), }; } @@ -202,17 +190,12 @@ export const languageCommand: SlashCommand = { if (trimmedArgs) { const [firstArg, ...rest] = trimmedArgs.split(/\s+/); const subCommandName = firstArg.toLowerCase(); - const subArgs = rest.join(' '); + const subArgs = rest.join(" "); - if (subCommandName === 'ui' || subCommandName === 'output') { - const subCommand = languageCommand.subCommands?.find( - (s) => s.name === subCommandName, - ); + if (subCommandName === "ui" || subCommandName === "output") { + const subCommand = languageCommand.subCommands?.find((s) => s.name === subCommandName); if (subCommand?.action) { - return subCommand.action( - context, - subArgs, - ) as Promise; + return subCommand.action(context, subArgs) as Promise; } } @@ -224,87 +207,79 @@ export const languageCommand: SlashCommand = { // Unknown argument return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: [ - t('Invalid command. Available subcommands:'), - ` - /language ui [${getSupportedLanguageIds()}] - ${t('Set UI language')}`, - ` - /language output - ${t('Set LLM output language')}`, - ].join('\n'), + t("Invalid command. Available subcommands:"), + ` - /language ui [${getSupportedLanguageIds()}] - ${t("Set UI language")}`, + ` - /language output - ${t("Set LLM output language")}`, + ].join("\n"), }; } // No arguments: show current status const currentUiLang = getCurrentLanguage(); - const { setting: outputSetting, resolved: outputResolved } = - getCurrentOutputLanguage(context); + const { setting: outputSetting, resolved: outputResolved } = getCurrentOutputLanguage(context); // Format output language display: show "Auto → English" or just "English" const outputLangDisplay = isAutoLanguage(outputSetting) - ? `${t('Auto (detect from system)')} → ${outputResolved}` + ? `${t("Auto (detect from system)")} → ${outputResolved}` : outputResolved; return { - type: 'message', - messageType: 'info', + type: "message", + messageType: "info", content: [ - t('Current UI language: {{lang}}', { + t("Current UI language: {{lang}}", { lang: formatUiLanguageDisplay(currentUiLang as SupportedLanguage), }), - t('Current LLM output language: {{lang}}', { lang: outputLangDisplay }), - '', - t('Available subcommands:'), - ` /language ui [${getSupportedLanguageIds()}] - ${t('Set UI language')}`, - ` /language output - ${t('Set LLM output language')}`, - ].join('\n'), + t("Current LLM output language: {{lang}}", { lang: outputLangDisplay }), + "", + t("Available subcommands:"), + ` /language ui [${getSupportedLanguageIds()}] - ${t("Set UI language")}`, + ` /language output - ${t("Set LLM output language")}`, + ].join("\n"), }; }, subCommands: [ // /language ui subcommand { - name: 'ui', + name: "ui", get description() { - return t('Set UI language'); + return t("Set UI language"); }, kind: CommandKind.BUILT_IN, - action: async ( - context: CommandContext, - args: string, - ): Promise => { + action: async (context: CommandContext, args: string): Promise => { const trimmedArgs = args.trim(); if (!trimmedArgs) { return { - type: 'message', - messageType: 'info', + type: "message", + messageType: "info", content: [ - t('Set UI language'), - '', - t('Usage: /language ui [{{options}}]', { + t("Set UI language"), + "", + t("Usage: /language ui [{{options}}]", { options: getSupportedLanguageIds(), }), - '', - t('Available options:'), - ...SUPPORTED_LANGUAGES.map( - (o) => ` - ${o.id}: ${o.nativeName || o.fullName}`, - ), - '', - t( - 'To request additional UI language packs, please open an issue on GitHub.', - ), - ].join('\n'), + "", + t("Available options:"), + ...SUPPORTED_LANGUAGES.map((o) => ` - ${o.id}: ${o.nativeName || o.fullName}`), + "", + t("To request additional UI language packs, please open an issue on GitHub."), + ].join("\n"), }; } const targetLang = parseUiLanguageArg(trimmedArgs); if (!targetLang) { return { - type: 'message', - messageType: 'error', - content: t('Invalid language. Available: {{options}}', { - options: getSupportedLanguageIds(','), + type: "message", + messageType: "error", + content: t("Invalid language. Available: {{options}}", { + options: getSupportedLanguageIds(","), }), }; } @@ -317,7 +292,7 @@ export const languageCommand: SlashCommand = { (lang): SlashCommand => ({ name: lang.id, get description() { - return t('Set UI language to {{name}}', { + return t("Set UI language to {{name}}", { name: lang.nativeName || lang.fullName, }); }, @@ -325,11 +300,9 @@ export const languageCommand: SlashCommand = { action: async (context, args) => { if (args.trim()) { return { - type: 'message', - messageType: 'error', - content: t( - 'Language subcommands do not accept additional arguments.', - ), + type: "message", + messageType: "error", + content: t("Language subcommands do not accept additional arguments."), }; } return setUiLanguage(context, lang.code); @@ -340,30 +313,27 @@ export const languageCommand: SlashCommand = { // /language output subcommand { - name: 'output', + name: "output", get description() { - return t('Set LLM output language'); + return t("Set LLM output language"); }, kind: CommandKind.BUILT_IN, - action: async ( - context: CommandContext, - args: string, - ): Promise => { + action: async (context: CommandContext, args: string): Promise => { const trimmedArgs = args.trim(); if (!trimmedArgs) { return { - type: 'message', - messageType: 'info', + type: "message", + messageType: "info", content: [ - t('Set LLM output language'), - '', - t('Usage: /language output '), - ` ${t('Example: /language output 中文')}`, - ` ${t('Example: /language output English')}`, - ` ${t('Example: /language output 日本語')}`, - ].join('\n'), + t("Set LLM output language"), + "", + t("Usage: /language output "), + ` ${t("Example: /language output 中文")}`, + ` ${t("Example: /language output English")}`, + ` ${t("Example: /language output 日本語")}`, + ].join("\n"), }; } diff --git a/apps/airiscode-cli/src/ui/commands/mcpCommand.ts b/apps/airiscode-cli/src/ui/commands/mcpCommand.ts index 4ca165e35..0e4798b74 100644 --- a/apps/airiscode-cli/src/ui/commands/mcpCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/mcpCommand.ts @@ -4,18 +4,18 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { SlashCommand, OpenDialogActionReturn } from './types.js'; -import { CommandKind } from './types.js'; -import { t } from '../../i18n/index.js'; +import { t } from "../../i18n/index.js"; +import type { OpenDialogActionReturn, SlashCommand } from "./types.js"; +import { CommandKind } from "./types.js"; export const mcpCommand: SlashCommand = { - name: 'mcp', + name: "mcp", get description() { - return t('Open MCP management dialog'); + return t("Open MCP management dialog"); }, kind: CommandKind.BUILT_IN, action: async (): Promise => ({ - type: 'dialog', - dialog: 'mcp', + type: "dialog", + dialog: "mcp", }), }; diff --git a/apps/airiscode-cli/src/ui/commands/memoryCommand.ts b/apps/airiscode-cli/src/ui/commands/memoryCommand.ts index 8ec1cfcc5..cb0a7e172 100644 --- a/apps/airiscode-cli/src/ui/commands/memoryCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/memoryCommand.ts @@ -4,19 +4,19 @@ * SPDX-License-Identifier: Apache-2.0 */ +import fs from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; import { - getErrorMessage, + AIRISCODE_DIR, getAllGeminiMdFilenames, + getErrorMessage, loadServerHierarchicalMemory, - AIRISCODE_DIR, -} from '@airiscode/core'; -import path from 'node:path'; -import os from 'node:os'; -import fs from 'node: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'; +} from "@airiscode/core"; +import { t } from "../../i18n/index.js"; +import { MessageType } from "../types.js"; +import type { SlashCommand, SlashCommandActionReturn } from "./types.js"; +import { CommandKind } from "./types.js"; /** * Read all existing memory files from the configured filenames in a directory. @@ -29,7 +29,7 @@ async function findAllExistingMemoryFiles( for (const filename of getAllGeminiMdFilenames()) { const filePath = path.join(dir, filename); try { - const content = await fs.readFile(filePath, 'utf-8'); + const content = await fs.readFile(filePath, "utf-8"); if (content.trim().length > 0) { results.push({ filePath, content }); } @@ -41,26 +41,26 @@ async function findAllExistingMemoryFiles( } export const memoryCommand: SlashCommand = { - name: 'memory', + name: "memory", get description() { - return t('Commands for interacting with memory.'); + return t("Commands for interacting with memory."); }, kind: CommandKind.BUILT_IN, subCommands: [ { - name: 'show', + name: "show", get description() { - return t('Show the current memory contents.'); + return t("Show the current memory contents."); }, kind: CommandKind.BUILT_IN, action: async (context) => { - const memoryContent = context.services.config?.getUserMemory() || ''; + const memoryContent = context.services.config?.getUserMemory() || ""; const fileCount = context.services.config?.getGeminiMdFileCount() || 0; const messageContent = memoryContent.length > 0 - ? `${t('Current memory content from {{count}} file(s):', { count: String(fileCount) })}\n\n---\n${memoryContent}\n---` - : t('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( { @@ -72,25 +72,24 @@ export const memoryCommand: SlashCommand = { }, subCommands: [ { - name: '--project', + name: "--project", get description() { - return t('Show project-level memory contents.'); + return t("Show project-level memory contents."); }, kind: CommandKind.BUILT_IN, action: async (context) => { - const workingDir = - context.services.config?.getWorkingDir?.() ?? process.cwd(); + const workingDir = context.services.config?.getWorkingDir?.() ?? process.cwd(); const results = await findAllExistingMemoryFiles(workingDir); if (results.length > 0) { const combined = results .map((r) => - t( - 'Project memory content from {{path}}:\n\n---\n{{content}}\n---', - { path: r.filePath, content: r.content }, - ), + t("Project memory content from {{path}}:\n\n---\n{{content}}\n---", { + path: r.filePath, + content: r.content, + }), ) - .join('\n\n'); + .join("\n\n"); context.ui.addItem( { type: MessageType.INFO, @@ -102,9 +101,7 @@ export const memoryCommand: SlashCommand = { context.ui.addItem( { type: MessageType.INFO, - text: t( - 'Project memory file not found or is currently empty.', - ), + text: t("Project memory file not found or is currently empty."), }, Date.now(), ); @@ -112,9 +109,9 @@ export const memoryCommand: SlashCommand = { }, }, { - name: '--global', + name: "--global", get description() { - return t('Show global memory contents.'); + return t("Show global memory contents."); }, kind: CommandKind.BUILT_IN, action: async (context) => { @@ -124,11 +121,11 @@ export const memoryCommand: SlashCommand = { if (results.length > 0) { const combined = results .map((r) => - t('Global memory content:\n\n---\n{{content}}\n---', { + t("Global memory content:\n\n---\n{{content}}\n---", { content: r.content, }), ) - .join('\n\n'); + .join("\n\n"); context.ui.addItem( { type: MessageType.INFO, @@ -140,9 +137,7 @@ export const memoryCommand: SlashCommand = { context.ui.addItem( { type: MessageType.INFO, - text: t( - 'Global memory file not found or is currently empty.', - ), + text: t("Global memory file not found or is currently empty."), }, Date.now(), ); @@ -152,60 +147,54 @@ export const memoryCommand: SlashCommand = { ], }, { - name: 'add', + name: "add", get description() { return t( - 'Add content to the memory. Use --global for global memory or --project for project memory.', + "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() === '') { + if (!args || args.trim() === "") { return { - type: 'message', - messageType: 'error', - content: t( - 'Usage: /memory add [--global|--project] ', - ), + type: "message", + messageType: "error", + content: t("Usage: /memory add [--global|--project] "), }; } const trimmedArgs = args.trim(); - let scope: 'global' | 'project' | undefined; + let scope: "global" | "project" | undefined; let fact: string; // Check for scope flags - if (trimmedArgs.startsWith('--global ')) { - scope = 'global'; - fact = trimmedArgs.substring('--global '.length).trim(); - } else if (trimmedArgs.startsWith('--project ')) { - scope = 'project'; - fact = trimmedArgs.substring('--project '.length).trim(); - } else if (trimmedArgs === '--global' || trimmedArgs === '--project') { + if (trimmedArgs.startsWith("--global ")) { + scope = "global"; + fact = trimmedArgs.substring("--global ".length).trim(); + } else if (trimmedArgs.startsWith("--project ")) { + scope = "project"; + fact = trimmedArgs.substring("--project ".length).trim(); + } else if (trimmedArgs === "--global" || trimmedArgs === "--project") { // Flag provided but no text after it return { - type: 'message', - messageType: 'error', - content: t( - 'Usage: /memory add [--global|--project] ', - ), + type: "message", + messageType: "error", + content: t("Usage: /memory add [--global|--project] "), }; } else { // No scope specified, will be handled by the tool fact = trimmedArgs; } - if (!fact || fact.trim() === '') { + if (!fact || fact.trim() === "") { return { - type: 'message', - messageType: 'error', - content: t( - 'Usage: /memory add [--global|--project] ', - ), + type: "message", + messageType: "error", + content: t("Usage: /memory add [--global|--project] "), }; } - const scopeText = scope ? `(${scope})` : ''; + const scopeText = scope ? `(${scope})` : ""; context.ui.addItem( { type: MessageType.INFO, @@ -218,24 +207,24 @@ export const memoryCommand: SlashCommand = { ); return { - type: 'tool', - toolName: 'save_memory', + type: "tool", + toolName: "save_memory", toolArgs: scope ? { fact, scope } : { fact }, }; }, subCommands: [ { - name: '--project', + name: "--project", get description() { - return t('Add content to project-level memory.'); + return t("Add content to project-level memory."); }, kind: CommandKind.BUILT_IN, action: (context, args): SlashCommandActionReturn | void => { - if (!args || args.trim() === '') { + if (!args || args.trim() === "") { return { - type: 'message', - messageType: 'error', - content: t('Usage: /memory add --project '), + type: "message", + messageType: "error", + content: t("Usage: /memory add --project "), }; } @@ -250,24 +239,24 @@ export const memoryCommand: SlashCommand = { ); return { - type: 'tool', - toolName: 'save_memory', - toolArgs: { fact: args.trim(), scope: 'project' }, + type: "tool", + toolName: "save_memory", + toolArgs: { fact: args.trim(), scope: "project" }, }; }, }, { - name: '--global', + name: "--global", get description() { - return t('Add content to global memory.'); + return t("Add content to global memory."); }, kind: CommandKind.BUILT_IN, action: (context, args): SlashCommandActionReturn | void => { - if (!args || args.trim() === '') { + if (!args || args.trim() === "") { return { - type: 'message', - messageType: 'error', - content: t('Usage: /memory add --global '), + type: "message", + messageType: "error", + content: t("Usage: /memory add --global "), }; } @@ -282,25 +271,25 @@ export const memoryCommand: SlashCommand = { ); return { - type: 'tool', - toolName: 'save_memory', - toolArgs: { fact: args.trim(), scope: 'global' }, + type: "tool", + toolName: "save_memory", + toolArgs: { fact: args.trim(), scope: "global" }, }; }, }, ], }, { - name: 'refresh', + name: "refresh", get description() { - return t('Refresh the memory from the source.'); + return t("Refresh the memory from the source."); }, kind: CommandKind.BUILT_IN, action: async (context) => { context.ui.addItem( { type: MessageType.INFO, - text: t('Refreshing memory from source files...'), + text: t("Refreshing memory from source files..."), }, Date.now(), ); @@ -308,25 +297,23 @@ export const memoryCommand: SlashCommand = { try { const config = context.services.config; if (config) { - const { memoryContent, fileCount } = - await loadServerHierarchicalMemory( - config.getWorkingDir(), - config.shouldLoadMemoryFromIncludeDirectories() - ? config.getWorkspaceContext().getDirectories() - : [], - config.getFileService(), - config.getExtensionContextFilePaths(), - config.getFolderTrust(), - context.services.settings.merged.context?.importFormat || - 'tree', // Use setting or default to 'tree' - ); + const { memoryContent, fileCount } = await loadServerHierarchicalMemory( + config.getWorkingDir(), + config.shouldLoadMemoryFromIncludeDirectories() + ? config.getWorkspaceContext().getDirectories() + : [], + config.getFileService(), + config.getExtensionContextFilePaths(), + config.getFolderTrust(), + context.services.settings.merged.context?.importFormat || "tree", // Use setting or default to 'tree' + ); config.setUserMemory(memoryContent); config.setGeminiMdFileCount(fileCount); const successMessage = memoryContent.length > 0 ? `Memory refreshed successfully. Loaded ${memoryContent.length} characters from ${fileCount} file(s).` - : 'Memory refreshed successfully. No memory content found.'; + : "Memory refreshed successfully. No memory content found."; context.ui.addItem( { diff --git a/apps/airiscode-cli/src/ui/commands/modelCommand.ts b/apps/airiscode-cli/src/ui/commands/modelCommand.ts index 2ccbda323..8020e21bd 100644 --- a/apps/airiscode-cli/src/ui/commands/modelCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/modelCommand.ts @@ -4,28 +4,28 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { getPersistScopeForModelSelection } from "../../config/modelProvidersScope.js"; +import { t } from "../../i18n/index.js"; import type { - SlashCommand, CommandContext, - OpenDialogActionReturn, MessageActionReturn, -} from './types.js'; -import { CommandKind } from './types.js'; -import { t } from '../../i18n/index.js'; -import { getPersistScopeForModelSelection } from '../../config/modelProvidersScope.js'; + OpenDialogActionReturn, + SlashCommand, +} from "./types.js"; +import { CommandKind } from "./types.js"; export const modelCommand: SlashCommand = { - name: 'model', + name: "model", get description() { - return t('Switch the model for this session'); + return t("Switch the model for this session"); }, kind: CommandKind.BUILT_IN, completion: async (_context, partialArg) => { - if ('--fast'.startsWith(partialArg)) { + if ("--fast".startsWith(partialArg)) { return [ { - value: '--fast', - description: t('Set fast model for background tasks'), + value: "--fast", + description: t("Set fast model for background tasks"), }, ]; } @@ -39,64 +39,60 @@ export const modelCommand: SlashCommand = { if (!config) { return { - type: 'message', - messageType: 'error', - content: t('Configuration not available.'), + type: "message", + messageType: "error", + content: t("Configuration not available."), }; } // Handle --fast flag: /model --fast - const args = context.invocation?.args?.trim() ?? ''; - if (args.startsWith('--fast')) { - const modelName = args.replace('--fast', '').trim(); + const args = context.invocation?.args?.trim() ?? ""; + if (args.startsWith("--fast")) { + const modelName = args.replace("--fast", "").trim(); if (!modelName) { // Open model dialog in fast-model mode return { - type: 'dialog', - dialog: 'fast-model', + type: "dialog", + dialog: "fast-model", }; } // Set fast model if (!settings) { return { - type: 'message', - messageType: 'error', - content: t('Settings service not available.'), + type: "message", + messageType: "error", + content: t("Settings service not available."), }; } - settings.setValue( - getPersistScopeForModelSelection(settings), - 'fastModel', - modelName, - ); + settings.setValue(getPersistScopeForModelSelection(settings), "fastModel", modelName); return { - type: 'message', - messageType: 'info', - content: t('Fast Model') + ': ' + modelName, + type: "message", + messageType: "info", + content: t("Fast Model") + ": " + modelName, }; } const contentGeneratorConfig = config.getContentGeneratorConfig(); if (!contentGeneratorConfig) { return { - type: 'message', - messageType: 'error', - content: t('Content generator configuration not available.'), + type: "message", + messageType: "error", + content: t("Content generator configuration not available."), }; } const authType = contentGeneratorConfig.authType; if (!authType) { return { - type: 'message', - messageType: 'error', - content: t('Authentication type not available.'), + type: "message", + messageType: "error", + content: t("Authentication type not available."), }; } return { - type: 'dialog', - dialog: 'model', + type: "dialog", + dialog: "model", }; }, }; diff --git a/apps/airiscode-cli/src/ui/commands/permissionsCommand.ts b/apps/airiscode-cli/src/ui/commands/permissionsCommand.ts index 034fec843..140511a6b 100644 --- a/apps/airiscode-cli/src/ui/commands/permissionsCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/permissionsCommand.ts @@ -4,18 +4,18 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { OpenDialogActionReturn, SlashCommand } from './types.js'; -import { CommandKind } from './types.js'; -import { t } from '../../i18n/index.js'; +import { t } from "../../i18n/index.js"; +import type { OpenDialogActionReturn, SlashCommand } from "./types.js"; +import { CommandKind } from "./types.js"; export const permissionsCommand: SlashCommand = { - name: 'permissions', + name: "permissions", get description() { - return t('Manage permission rules'); + return t("Manage permission rules"); }, kind: CommandKind.BUILT_IN, action: (): OpenDialogActionReturn => ({ - type: 'dialog', - dialog: 'permissions', + type: "dialog", + dialog: "permissions", }), }; diff --git a/apps/airiscode-cli/src/ui/commands/planCommand.ts b/apps/airiscode-cli/src/ui/commands/planCommand.ts index 0b2098401..9f63db84e 100644 --- a/apps/airiscode-cli/src/ui/commands/planCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/planCommand.ts @@ -4,20 +4,20 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { ApprovalMode } from "@airiscode/core"; +import { t } from "../../i18n/index.js"; import { type CommandContext, CommandKind, - type SlashCommand, type MessageActionReturn, + type SlashCommand, type SubmitPromptActionReturn, -} from './types.js'; -import { t } from '../../i18n/index.js'; -import { ApprovalMode } from '@airiscode/core'; +} from "./types.js"; export const planCommand: SlashCommand = { - name: 'plan', + name: "plan", get description() { - return t('Switch to plan mode or exit plan mode'); + return t("Switch to plan mode or exit plan mode"); }, kind: CommandKind.BUILT_IN, action: async ( @@ -27,20 +27,20 @@ export const planCommand: SlashCommand = { const { config } = context.services; if (!config) { return { - type: 'message', - messageType: 'error', - content: t('Configuration is not available.'), + type: "message", + messageType: "error", + content: t("Configuration is not available."), }; } const trimmedArgs = args.trim(); const currentMode = config.getApprovalMode(); - if (trimmedArgs === 'exit') { + if (trimmedArgs === "exit") { if (currentMode !== ApprovalMode.PLAN) { return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: t('Not in plan mode. Use "/plan" to enter plan mode first.'), }; } @@ -48,15 +48,15 @@ export const planCommand: SlashCommand = { config.setApprovalMode(config.getPrePlanMode()); } catch (e) { return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: (e as Error).message, }; } return { - type: 'message', - messageType: 'info', - content: t('Exited plan mode. Previous approval mode restored.'), + type: "message", + messageType: "info", + content: t("Exited plan mode. Previous approval mode restored."), }; } @@ -65,39 +65,37 @@ export const planCommand: SlashCommand = { config.setApprovalMode(ApprovalMode.PLAN); } catch (e) { return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: (e as Error).message, }; } if (trimmedArgs) { return { - type: 'submit_prompt', + type: "submit_prompt", content: [{ text: trimmedArgs }], }; } return { - type: 'message', - messageType: 'info', - content: t( - 'Enabled plan mode. The agent will analyze and plan without executing tools.', - ), + type: "message", + messageType: "info", + content: t("Enabled plan mode. The agent will analyze and plan without executing tools."), }; } // Already in plan mode if (trimmedArgs) { return { - type: 'submit_prompt', + type: "submit_prompt", content: [{ text: trimmedArgs }], }; } return { - type: 'message', - messageType: 'info', + type: "message", + messageType: "info", content: t('Already in plan mode. Use "/plan exit" to exit plan mode.'), }; }, diff --git a/apps/airiscode-cli/src/ui/commands/quitCommand.ts b/apps/airiscode-cli/src/ui/commands/quitCommand.ts index 4e9da3a0c..3073a446c 100644 --- a/apps/airiscode-cli/src/ui/commands/quitCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/quitCommand.ts @@ -4,15 +4,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { formatDuration } from '../utils/formatters.js'; -import { CommandKind, type SlashCommand } from './types.js'; -import { t } from '../../i18n/index.js'; +import { t } from "../../i18n/index.js"; +import { formatDuration } from "../utils/formatters.js"; +import { CommandKind, type SlashCommand } from "./types.js"; export const quitCommand: SlashCommand = { - name: 'quit', - altNames: ['exit'], + name: "quit", + altNames: ["exit"], get description() { - return t('exit the cli'); + return t("exit the cli"); }, kind: CommandKind.BUILT_IN, action: (context) => { @@ -21,15 +21,15 @@ export const quitCommand: SlashCommand = { const wallDuration = now - sessionStartTime.getTime(); return { - type: 'quit', + type: "quit", messages: [ { - type: 'user', + type: "user", text: `/quit`, // Keep it consistent, even if /exit was used id: now - 1, }, { - type: 'quit', + type: "quit", duration: formatDuration(wallDuration), id: now, }, diff --git a/apps/airiscode-cli/src/ui/commands/restoreCommand.ts b/apps/airiscode-cli/src/ui/commands/restoreCommand.ts index e505cffa1..8cf93699f 100644 --- a/apps/airiscode-cli/src/ui/commands/restoreCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/restoreCommand.ts @@ -4,16 +4,16 @@ * SPDX-License-Identifier: Apache-2.0 */ -import * as fs from 'node:fs/promises'; -import path from 'node:path'; +import * as fs from "node:fs/promises"; +import path from "node:path"; +import type { Config } from "@airiscode/core"; +import { t } from "../../i18n/index.js"; import { type CommandContext, + CommandKind, type SlashCommand, type SlashCommandActionReturn, - CommandKind, -} from './types.js'; -import type { Config } from '@airiscode/core'; -import { t } from '../../i18n/index.js'; +} from "./types.js"; async function restoreAction( context: CommandContext, @@ -27,9 +27,9 @@ async function restoreAction( if (!checkpointDir) { return { - type: 'message', - messageType: 'error', - content: 'Could not determine the .qwen directory path.', + type: "message", + messageType: "error", + content: "Could not determine the .qwen directory path.", }; } @@ -37,53 +37,53 @@ async function restoreAction( // Ensure the directory exists before trying to read it. await fs.mkdir(checkpointDir, { recursive: true }); const files = await fs.readdir(checkpointDir); - const jsonFiles = files.filter((file) => file.endsWith('.json')); + const jsonFiles = files.filter((file) => file.endsWith(".json")); if (!args) { if (jsonFiles.length === 0) { return { - type: 'message', - messageType: 'info', - content: 'No restorable tool calls found.', + type: "message", + messageType: "info", + content: "No restorable tool calls found.", }; } const truncatedFiles = jsonFiles.map((file) => { - const components = file.split('.'); + const components = file.split("."); if (components.length <= 1) { return file; } components.pop(); - return components.join('.'); + return components.join("."); }); - const fileList = truncatedFiles.join('\n'); + const fileList = truncatedFiles.join("\n"); return { - type: 'message', - messageType: 'info', + type: "message", + messageType: "info", content: `Available tool calls to restore:\n\n${fileList}`, }; } - const selectedFile = args.endsWith('.json') ? args : `${args}.json`; + const selectedFile = args.endsWith(".json") ? args : `${args}.json`; if (!jsonFiles.includes(selectedFile)) { return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: `File not found: ${selectedFile}`, }; } const filePath = path.join(checkpointDir, selectedFile); - const data = await fs.readFile(filePath, 'utf-8'); + const data = await fs.readFile(filePath, "utf-8"); const toolCallData = JSON.parse(data); if (toolCallData.history) { if (!loadHistory) { // This should not happen return { - type: 'message', - messageType: 'error', - content: 'loadHistory function is not available.', + type: "message", + messageType: "error", + content: "loadHistory function is not available.", }; } loadHistory(toolCallData.history); @@ -97,31 +97,28 @@ async function restoreAction( await gitService?.restoreProjectFromSnapshot(toolCallData.commitHash); addItem( { - type: 'info', - text: 'Restored project to the state before the tool call.', + type: "info", + text: "Restored project to the state before the tool call.", }, Date.now(), ); } return { - type: 'tool', + type: "tool", toolName: toolCallData.toolCall.name, toolArgs: toolCallData.toolCall.args, }; } catch (error) { return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: `Could not read restorable tool calls. This is the error: ${error}`, }; } } -async function completion( - context: CommandContext, - _partialArg: string, -): Promise { +async function completion(context: CommandContext, _partialArg: string): Promise { const { services } = context; const { config } = services; const checkpointDir = config?.storage.getProjectTempCheckpointsDir(); @@ -130,9 +127,7 @@ async function completion( } try { const files = await fs.readdir(checkpointDir); - return files - .filter((file) => file.endsWith('.json')) - .map((file) => file.replace('.json', '')); + return files.filter((file) => file.endsWith(".json")).map((file) => file.replace(".json", "")); } catch (_err) { return []; } @@ -144,10 +139,10 @@ export const restoreCommand = (config: Config | null): SlashCommand | null => { } return { - name: 'restore', + name: "restore", get description() { return t( - 'Restore a tool call. This will reset the conversation and file history to the state it was in when the tool call was suggested', + "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, diff --git a/apps/airiscode-cli/src/ui/commands/resumeCommand.ts b/apps/airiscode-cli/src/ui/commands/resumeCommand.ts index ece068afc..40aacd8c0 100644 --- a/apps/airiscode-cli/src/ui/commands/resumeCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/resumeCommand.ts @@ -4,18 +4,18 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { SlashCommand, SlashCommandActionReturn } from './types.js'; -import { CommandKind } from './types.js'; -import { t } from '../../i18n/index.js'; +import { t } from "../../i18n/index.js"; +import type { SlashCommand, SlashCommandActionReturn } from "./types.js"; +import { CommandKind } from "./types.js"; export const resumeCommand: SlashCommand = { - name: 'resume', + name: "resume", kind: CommandKind.BUILT_IN, get description() { - return t('Resume a previous session'); + return t("Resume a previous session"); }, action: async (): Promise => ({ - type: 'dialog', - dialog: 'resume', + type: "dialog", + dialog: "resume", }), }; diff --git a/apps/airiscode-cli/src/ui/commands/settingsCommand.ts b/apps/airiscode-cli/src/ui/commands/settingsCommand.ts index 762977086..7e5760782 100644 --- a/apps/airiscode-cli/src/ui/commands/settingsCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/settingsCommand.ts @@ -4,18 +4,18 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { OpenDialogActionReturn, SlashCommand } from './types.js'; -import { CommandKind } from './types.js'; -import { t } from '../../i18n/index.js'; +import { t } from "../../i18n/index.js"; +import type { OpenDialogActionReturn, SlashCommand } from "./types.js"; +import { CommandKind } from "./types.js"; export const settingsCommand: SlashCommand = { - name: 'settings', + name: "settings", get description() { - return t('View and edit AIRIS Code settings'); + return t("View and edit AIRIS Code settings"); }, kind: CommandKind.BUILT_IN, action: (_context, _args): OpenDialogActionReturn => ({ - type: 'dialog', - dialog: 'settings', + type: "dialog", + dialog: "settings", }), }; diff --git a/apps/airiscode-cli/src/ui/commands/setupGithubCommand.ts b/apps/airiscode-cli/src/ui/commands/setupGithubCommand.ts index 3bc46c6a8..95e9a476d 100644 --- a/apps/airiscode-cli/src/ui/commands/setupGithubCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/setupGithubCommand.ts @@ -4,33 +4,31 @@ * SPDX-License-Identifier: Apache-2.0 */ -import path from 'node:path'; -import * as fs from 'node:fs'; -import { Writable } from 'node:stream'; -import { ProxyAgent } from 'undici'; - -import type { CommandContext } from '../../ui/commands/types.js'; +import * as fs from "node:fs"; +import path from "node:path"; +import { Writable } from "node:stream"; +import { createDebugLogger } from "@airiscode/core"; +import { ProxyAgent } from "undici"; +import { t } from "../../i18n/index.js"; +import type { CommandContext } from "../../ui/commands/types.js"; +import { getUrlOpenCommand } from "../../ui/utils/commandUtils.js"; import { + getGitHubRepoInfo, getGitRepoRoot, getLatestGitHubRelease, isGitHubRepository, - getGitHubRepoInfo, -} from '../../utils/gitUtils.js'; +} from "../../utils/gitUtils.js"; +import type { SlashCommand, SlashCommandActionReturn } from "./types.js"; +import { CommandKind } from "./types.js"; -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'; -import { createDebugLogger } from '@airiscode/core'; - -const debugLogger = createDebugLogger('SETUP_GITHUB'); +const debugLogger = createDebugLogger("SETUP_GITHUB"); export const GITHUB_WORKFLOW_PATHS = [ - 'qwen-dispatch/qwen-dispatch.yml', - 'qwen-assistant/qwen-invoke.yml', - 'issue-triage/qwen-triage.yml', - 'issue-triage/qwen-scheduled-triage.yml', - 'pr-review/qwen-review.yml', + "qwen-dispatch/qwen-dispatch.yml", + "qwen-assistant/qwen-invoke.yml", + "issue-triage/qwen-triage.yml", + "issue-triage/qwen-scheduled-triage.yml", + "pr-review/qwen-review.yml", ]; // Generate OS-specific commands to open the GitHub pages needed for setup. @@ -55,15 +53,15 @@ function getOpenUrlsCommands(readmeUrl: string): string[] { // Add AIRIS Code specific entries to .gitignore file export async function updateGitignore(gitRepoRoot: string): Promise { - const gitignoreEntries = ['.airiscode/', 'gha-creds-*.json']; + const gitignoreEntries = [".airiscode/", "gha-creds-*.json"]; - const gitignorePath = path.join(gitRepoRoot, '.gitignore'); + const gitignorePath = path.join(gitRepoRoot, ".gitignore"); try { // Check if .gitignore exists and read its content - let existingContent = ''; + let existingContent = ""; let fileExists = true; try { - existingContent = await fs.promises.readFile(gitignorePath, 'utf8'); + existingContent = await fs.promises.readFile(gitignorePath, "utf8"); } catch (_error) { // File doesn't exist fileExists = false; @@ -71,51 +69,43 @@ export async function updateGitignore(gitRepoRoot: string): Promise { if (!fileExists) { // Create new .gitignore file with the entries - const contentToWrite = gitignoreEntries.join('\n') + '\n'; + const contentToWrite = gitignoreEntries.join("\n") + "\n"; await fs.promises.writeFile(gitignorePath, contentToWrite); } else { // Check which entries are missing const missingEntries = gitignoreEntries.filter( (entry) => - !existingContent - .split(/\r?\n/) - .some((line) => line.split('#')[0].trim() === entry), + !existingContent.split(/\r?\n/).some((line) => line.split("#")[0].trim() === entry), ); if (missingEntries.length > 0) { - const contentToAdd = '\n' + missingEntries.join('\n') + '\n'; + const contentToAdd = "\n" + missingEntries.join("\n") + "\n"; await fs.promises.appendFile(gitignorePath, contentToAdd); } } } catch (error) { - debugLogger.debug('Failed to update .gitignore:', error); + debugLogger.debug("Failed to update .gitignore:", error); // Continue without failing the whole command } } export const setupGithubCommand: SlashCommand = { - name: 'setup-github', + name: "setup-github", get description() { - return t('Set up GitHub Actions'); + return t("Set up GitHub Actions"); }, kind: CommandKind.BUILT_IN, - action: async ( - context: CommandContext, - ): Promise => { + action: async (context: CommandContext): Promise => { const abortController = new AbortController(); // If we have a context abort signal (from ESC cancellation), link it to our controller if (context.abortSignal) { - context.abortSignal.addEventListener( - 'abort', - () => abortController.abort(), - { once: true }, - ); + context.abortSignal.addEventListener("abort", () => abortController.abort(), { once: true }); } if (!isGitHubRepository()) { throw new Error( - 'Unable to determine the GitHub repository. /setup-github must be run from a git repository.', + "Unable to determine the GitHub repository. /setup-github must be run from a git repository.", ); } @@ -126,7 +116,7 @@ export const setupGithubCommand: SlashCommand = { } catch (_error) { debugLogger.debug(`Failed to get git repo root:`, _error); throw new Error( - 'Unable to determine the GitHub repository. /setup-github must be run from a git repository.', + "Unable to determine the GitHub repository. /setup-github must be run from a git repository.", ); } @@ -136,14 +126,11 @@ export const setupGithubCommand: SlashCommand = { const readmeUrl = `https://github.com/QwenLM/airiscode-action/blob/${releaseTag}/README.md#quick-start`; // Create the .github/workflows directory to download the files into - const githubWorkflowsDir = path.join(gitRepoRoot, '.github', 'workflows'); + const githubWorkflowsDir = path.join(gitRepoRoot, ".github", "workflows"); try { await fs.promises.mkdir(githubWorkflowsDir, { recursive: true }); } catch (_error) { - debugLogger.debug( - `Failed to create ${githubWorkflowsDir} directory:`, - _error, - ); + debugLogger.debug(`Failed to create ${githubWorkflowsDir} directory:`, _error); throw new Error( `Unable to create ${githubWorkflowsDir} directory. Do you have file permissions in the current directory?`, ); @@ -157,12 +144,9 @@ export const setupGithubCommand: SlashCommand = { (async () => { const endpoint = `https://raw.githubusercontent.com/QwenLM/airiscode-action/refs/tags/${releaseTag}/examples/workflows/${workflow}`; const response = await fetch(endpoint, { - method: 'GET', + method: "GET", dispatcher: proxy ? new ProxyAgent(proxy) : undefined, - signal: AbortSignal.any([ - AbortSignal.timeout(30_000), - abortController.signal, - ]), + signal: AbortSignal.any([AbortSignal.timeout(30_000), abortController.signal]), } as RequestInit); if (!response.ok) { @@ -177,14 +161,11 @@ export const setupGithubCommand: SlashCommand = { ); } - const destination = path.resolve( - githubWorkflowsDir, - path.basename(workflow), - ); + const destination = path.resolve(githubWorkflowsDir, path.basename(workflow)); const fileStream = fs.createWriteStream(destination, { mode: 0o644, // -rw-r--r--, user(rw), group(r), other(r) - flags: 'w', // write and overwrite + flags: "w", // write and overwrite flush: true, }); @@ -204,19 +185,18 @@ export const setupGithubCommand: SlashCommand = { // Print out a message const commands = []; - commands.push('set -eEuo pipefail'); + commands.push("set -eEuo pipefail"); commands.push( `echo "Successfully downloaded ${GITHUB_WORKFLOW_PATHS.length} workflows and updated .gitignore. Follow the steps in ${readmeUrl} (skipping the /setup-github step) to complete setup."`, ); commands.push(...getOpenUrlsCommands(readmeUrl)); - const command = `(${commands.join(' && ')})`; + const command = `(${commands.join(" && ")})`; return { - type: 'tool', - toolName: 'run_shell_command', + type: "tool", + toolName: "run_shell_command", toolArgs: { - description: - 'Setting up GitHub Actions to triage issues and review PRs with Qwen.', + description: "Setting up GitHub Actions to triage issues and review PRs with Qwen.", command, is_background: false, }, diff --git a/apps/airiscode-cli/src/ui/commands/skillsCommand.ts b/apps/airiscode-cli/src/ui/commands/skillsCommand.ts index 1eb5b55bc..aa1b4fb0b 100644 --- a/apps/airiscode-cli/src/ui/commands/skillsCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/skillsCommand.ts @@ -4,36 +4,36 @@ * SPDX-License-Identifier: Apache-2.0 */ +import type { SkillConfig } from "@airiscode/core"; +import { createDebugLogger } from "@airiscode/core"; +import { AsyncFzf } from "fzf"; +import { t } from "../../i18n/index.js"; +import { type HistoryItemSkillsList, MessageType } from "../types.js"; import { - CommandKind, type CommandCompletionItem, type CommandContext, + CommandKind, type SlashCommand, -} from './types.js'; -import { MessageType, type HistoryItemSkillsList } from '../types.js'; -import { t } from '../../i18n/index.js'; -import { AsyncFzf } from 'fzf'; -import type { SkillConfig } from '@airiscode/core'; -import { createDebugLogger } from '@airiscode/core'; +} from "./types.js"; -const debugLogger = createDebugLogger('SKILLS_COMMAND'); +const debugLogger = createDebugLogger("SKILLS_COMMAND"); export const skillsCommand: SlashCommand = { - name: 'skills', + name: "skills", get description() { - return t('List available skills.'); + return t("List available skills."); }, kind: CommandKind.BUILT_IN, action: async (context: CommandContext, args?: string) => { - const rawArgs = args?.trim() ?? ''; - const [skillName = ''] = rawArgs.split(/\s+/); + const rawArgs = args?.trim() ?? ""; + const [skillName = ""] = rawArgs.split(/\s+/); const skillManager = context.services.config?.getSkillManager(); if (!skillManager) { context.ui.addItem( { type: MessageType.ERROR, - text: t('Could not retrieve skill manager.'), + text: t("Could not retrieve skill manager."), }, Date.now(), ); @@ -45,7 +45,7 @@ export const skillsCommand: SlashCommand = { context.ui.addItem( { type: MessageType.INFO, - text: t('No skills are currently available.'), + text: t("No skills are currently available."), }, Date.now(), ); @@ -53,9 +53,7 @@ export const skillsCommand: SlashCommand = { } if (!skillName) { - const sortedSkills = [...skills].sort((left, right) => - left.name.localeCompare(right.name), - ); + const sortedSkills = [...skills].sort((left, right) => left.name.localeCompare(right.name)); const skillsListItem: HistoryItemSkillsList = { type: MessageType.SKILLS_LIST, skills: sortedSkills.map((skill) => ({ name: skill.name })), @@ -64,15 +62,13 @@ export const skillsCommand: SlashCommand = { return; } const normalizedName = skillName.toLowerCase(); - const hasSkill = skills.some( - (skill) => skill.name.toLowerCase() === normalizedName, - ); + const hasSkill = skills.some((skill) => skill.name.toLowerCase() === normalizedName); if (!hasSkill) { context.ui.addItem( { type: MessageType.ERROR, - text: t('Unknown skill: {{name}}', { name: skillName }), + text: t("Unknown skill: {{name}}", { name: skillName }), }, Date.now(), ); @@ -81,7 +77,7 @@ export const skillsCommand: SlashCommand = { const rawInput = context.invocation?.raw ?? `/skills ${rawArgs}`; return { - type: 'submit_prompt', + type: "submit_prompt", content: [{ text: rawInput }], }; }, @@ -105,10 +101,7 @@ export const skillsCommand: SlashCommand = { }, }; -async function getSkillMatches( - skills: SkillConfig[], - query: string, -): Promise { +async function getSkillMatches(skills: SkillConfig[], query: string): Promise { if (!query) { return skills; } @@ -118,18 +111,16 @@ async function getSkillMatches( try { const fzf = new AsyncFzf(names, { - fuzzy: 'v2', - casing: 'case-insensitive', + fuzzy: "v2", + casing: "case-insensitive", }); const results = (await fzf.find(query)) as Array<{ item: string }>; return results .map((result) => skillMap.get(result.item)) .filter((skill): skill is SkillConfig => !!skill); } catch (error) { - debugLogger.error('[skillsCommand] Fuzzy match failed:', error); + debugLogger.error("[skillsCommand] Fuzzy match failed:", error); const lowerQuery = query.toLowerCase(); - return skills.filter((skill) => - skill.name.toLowerCase().startsWith(lowerQuery), - ); + return skills.filter((skill) => skill.name.toLowerCase().startsWith(lowerQuery)); } } diff --git a/apps/airiscode-cli/src/ui/commands/statsCommand.ts b/apps/airiscode-cli/src/ui/commands/statsCommand.ts index cb4a3f512..29b037c2a 100644 --- a/apps/airiscode-cli/src/ui/commands/statsCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/statsCommand.ts @@ -4,21 +4,17 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { HistoryItemStats } from '../types.js'; -import { MessageType } from '../types.js'; -import { formatDuration } from '../utils/formatters.js'; -import { - type CommandContext, - type SlashCommand, - CommandKind, -} from './types.js'; -import { t } from '../../i18n/index.js'; +import { t } from "../../i18n/index.js"; +import type { HistoryItemStats } from "../types.js"; +import { MessageType } from "../types.js"; +import { formatDuration } from "../utils/formatters.js"; +import { type CommandContext, CommandKind, type SlashCommand } from "./types.js"; export const statsCommand: SlashCommand = { - name: 'stats', - altNames: ['usage'], + name: "stats", + altNames: ["usage"], get description() { - return t('check session stats. Usage: /stats [model|tools]'); + return t("check session stats. Usage: /stats [model|tools]"); }, kind: CommandKind.BUILT_IN, action: (context: CommandContext) => { @@ -28,7 +24,7 @@ export const statsCommand: SlashCommand = { context.ui.addItem( { type: MessageType.ERROR, - text: t('Session start time is unavailable, cannot calculate stats.'), + text: t("Session start time is unavailable, cannot calculate stats."), }, Date.now(), ); @@ -45,9 +41,9 @@ export const statsCommand: SlashCommand = { }, subCommands: [ { - name: 'model', + name: "model", get description() { - return t('Show model-specific usage statistics.'); + return t("Show model-specific usage statistics."); }, kind: CommandKind.BUILT_IN, action: (context: CommandContext) => { @@ -60,9 +56,9 @@ export const statsCommand: SlashCommand = { }, }, { - name: 'tools', + name: "tools", get description() { - return t('Show tool-specific usage statistics.'); + return t("Show tool-specific usage statistics."); }, kind: CommandKind.BUILT_IN, action: (context: CommandContext) => { diff --git a/apps/airiscode-cli/src/ui/commands/statuslineCommand.ts b/apps/airiscode-cli/src/ui/commands/statuslineCommand.ts index fa26d4ae7..6807e1251 100644 --- a/apps/airiscode-cli/src/ui/commands/statuslineCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/statuslineCommand.ts @@ -4,21 +4,20 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { SlashCommand, SubmitPromptActionReturn } from './types.js'; -import { CommandKind } from './types.js'; -import { t } from '../../i18n/index.js'; +import { t } from "../../i18n/index.js"; +import type { SlashCommand, SubmitPromptActionReturn } from "./types.js"; +import { CommandKind } from "./types.js"; export const statuslineCommand: SlashCommand = { - name: 'statusline', + name: "statusline", get description() { return t("Set up AIRIS Code's status line UI"); }, kind: CommandKind.BUILT_IN, action: (_context, args): SubmitPromptActionReturn => { - const prompt = - args.trim() || 'Configure my statusLine from my shell PS1 configuration'; + const prompt = args.trim() || "Configure my statusLine from my shell PS1 configuration"; return { - type: 'submit_prompt', + type: "submit_prompt", content: [ { text: `Use the Agent tool with subagent_type: "statusline-setup" and this prompt:\n\n${prompt}`, diff --git a/apps/airiscode-cli/src/ui/commands/summaryCommand.ts b/apps/airiscode-cli/src/ui/commands/summaryCommand.ts index b03ef79c5..fa68ef9b4 100644 --- a/apps/airiscode-cli/src/ui/commands/summaryCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/summaryCommand.ts @@ -4,65 +4,55 @@ * SPDX-License-Identifier: Apache-2.0 */ -import * as fsPromises from 'fs/promises'; -import path from 'path'; -import { - type SlashCommand, - CommandKind, - type SlashCommandActionReturn, -} from './types.js'; -import { getProjectSummaryPrompt } from '@airiscode/core'; -import type { HistoryItemSummary } from '../types.js'; -import { t } from '../../i18n/index.js'; +import { getProjectSummaryPrompt } from "@airiscode/core"; +import * as fsPromises from "fs/promises"; +import path from "path"; +import { t } from "../../i18n/index.js"; +import type { HistoryItemSummary } from "../types.js"; +import { CommandKind, type SlashCommand, type SlashCommandActionReturn } from "./types.js"; export const summaryCommand: SlashCommand = { - name: 'summary', + name: "summary", get description() { - return t( - 'Generate a project summary and save it to .airiscode/PROJECT_SUMMARY.md', - ); + return t("Generate a project summary and save it to .airiscode/PROJECT_SUMMARY.md"); }, kind: CommandKind.BUILT_IN, action: async (context): Promise => { const { config } = context.services; const { ui } = context; - const executionMode = context.executionMode ?? 'interactive'; + const executionMode = context.executionMode ?? "interactive"; const abortSignal = context.abortSignal; if (!config) { return { - type: 'message', - messageType: 'error', - content: t('Config not loaded.'), + type: "message", + messageType: "error", + content: t("Config not loaded."), }; } const geminiClient = config.getGeminiClient(); if (!geminiClient) { return { - type: 'message', - messageType: 'error', - content: t('No chat client available to generate summary.'), + type: "message", + messageType: "error", + content: t("No chat client available to generate summary."), }; } // Check if already generating summary (interactive UI only) - if (executionMode === 'interactive' && ui.pendingItem) { + if (executionMode === "interactive" && ui.pendingItem) { ui.addItem( { - type: 'error' as const, - text: t( - 'Already generating summary, wait for previous request to complete', - ), + type: "error" as const, + text: t("Already generating summary, wait for previous request to complete"), }, Date.now(), ); return { - type: 'message', - messageType: 'error', - content: t( - 'Already generating summary, wait for previous request to complete', - ), + type: "message", + messageType: "error", + content: t("Already generating summary, wait for previous request to complete"), }; } @@ -71,11 +61,9 @@ export const summaryCommand: SlashCommand = { return chat.getHistory(); }; - const validateChatHistory = ( - history: ReturnType, - ) => { + const validateChatHistory = (history: ReturnType) => { if (history.length <= 2) { - throw new Error(t('No conversation found to summarize.')); + throw new Error(t("No conversation found to summarize.")); } }; @@ -93,7 +81,7 @@ export const summaryCommand: SlashCommand = { [ ...conversationContext, { - role: 'user', + role: "user", parts: [ { text: getProjectSummaryPrompt(), @@ -112,14 +100,12 @@ export const summaryCommand: SlashCommand = { const markdownSummary = parts ?.map((part) => part.text) - .filter((text): text is string => typeof text === 'string') - .join('') || ''; + .filter((text): text is string => typeof text === "string") + .join("") || ""; if (!markdownSummary) { throw new Error( - t( - 'Failed to generate summary - no text content received from LLM response', - ), + t("Failed to generate summary - no text content received from LLM response"), ); } @@ -134,7 +120,7 @@ export const summaryCommand: SlashCommand = { }> => { // Ensure .qwen directory exists const projectRoot = config.getProjectRoot(); - const qwenDir = path.join(projectRoot, '.airiscode'); + const qwenDir = path.join(projectRoot, ".airiscode"); try { await fsPromises.mkdir(qwenDir, { recursive: true }); } catch (_err) { @@ -142,7 +128,7 @@ export const summaryCommand: SlashCommand = { } // Save the summary to PROJECT_SUMMARY.md - const summaryPath = path.join(qwenDir, 'PROJECT_SUMMARY.md'); + const summaryPath = path.join(qwenDir, "PROJECT_SUMMARY.md"); const summaryContent = `${markdownSummary} --- @@ -151,20 +137,20 @@ export const summaryCommand: SlashCommand = { **Update time**: ${new Date().toISOString()} `; - await fsPromises.writeFile(summaryPath, summaryContent, 'utf8'); + await fsPromises.writeFile(summaryPath, summaryContent, "utf8"); return { - filePathForDisplay: '.airiscode/PROJECT_SUMMARY.md', + filePathForDisplay: ".airiscode/PROJECT_SUMMARY.md", fullPath: summaryPath, }; }; - const emitInteractivePending = (stage: 'generating' | 'saving') => { - if (executionMode !== 'interactive') { + const emitInteractivePending = (stage: "generating" | "saving") => { + if (executionMode !== "interactive") { return; } const pendingMessage: HistoryItemSummary = { - type: 'summary', + type: "summary", summary: { isPending: true, stage, @@ -174,15 +160,15 @@ export const summaryCommand: SlashCommand = { }; const completeInteractive = (filePathForDisplay: string) => { - if (executionMode !== 'interactive') { + if (executionMode !== "interactive") { return; } ui.setPendingItem(null); const completedSummaryItem: HistoryItemSummary = { - type: 'summary', + type: "summary", summary: { isPending: false, - stage: 'completed', + stage: "completed", filePath: filePathForDisplay, }, }; @@ -190,12 +176,12 @@ export const summaryCommand: SlashCommand = { }; const formatErrorMessage = (error: unknown): string => - t('Failed to generate project context summary: {{error}}', { + t("Failed to generate project context summary: {{error}}", { error: error instanceof Error ? error.message : String(error), }); const failInteractive = (error: unknown) => { - if (executionMode !== 'interactive') { + if (executionMode !== "interactive") { return; } // If cancelled via ESC, don't show error — cancelSlashCommand already handled UI @@ -205,7 +191,7 @@ export const summaryCommand: SlashCommand = { ui.setPendingItem(null); ui.addItem( { - type: 'error' as const, + type: "error" as const, text: `❌ ${formatErrorMessage(error)}`, }, Date.now(), @@ -213,27 +199,27 @@ export const summaryCommand: SlashCommand = { }; const formatSuccessMessage = (filePathForDisplay: string): string => - t('Saved project summary to {{filePathForDisplay}}.', { + t("Saved project summary to {{filePathForDisplay}}.", { filePathForDisplay, }); const returnNoConversationMessage = (): SlashCommandActionReturn => { - const msg = t('No conversation found to summarize.'); - if (executionMode === 'acp') { + const msg = t("No conversation found to summarize."); + if (executionMode === "acp") { const messages = async function* () { yield { - messageType: 'info' as const, + messageType: "info" as const, content: msg, }; }; return { - type: 'stream_messages', + type: "stream_messages", messages: messages(), }; } return { - type: 'message', - messageType: 'info', + type: "message", + messageType: "info", content: msg, }; }; @@ -244,12 +230,12 @@ export const summaryCommand: SlashCommand = { markdownSummary: string; filePathForDisplay: string; }> => { - emitInteractivePending('generating'); + emitInteractivePending("generating"); const markdownSummary = await generateSummaryMarkdown(history); if (abortSignal?.aborted) { - throw new DOMException('Summary generation cancelled.', 'AbortError'); + throw new DOMException("Summary generation cancelled.", "AbortError"); } - emitInteractivePending('saving'); + emitInteractivePending("saving"); const { filePathForDisplay } = await saveSummaryToDisk(markdownSummary); completeInteractive(filePathForDisplay); return { markdownSummary, filePathForDisplay }; @@ -263,32 +249,31 @@ export const summaryCommand: SlashCommand = { return returnNoConversationMessage(); } - if (executionMode === 'acp') { + if (executionMode === "acp") { const messages = async function* () { try { yield { - messageType: 'info' as const, - content: t('Generating project summary...'), + messageType: "info" as const, + content: t("Generating project summary..."), }; - const { filePathForDisplay } = - await executeSummaryGeneration(history); + const { filePathForDisplay } = await executeSummaryGeneration(history); yield { - messageType: 'info' as const, + messageType: "info" as const, content: formatSuccessMessage(filePathForDisplay), }; } catch (error) { failInteractive(error); yield { - messageType: 'error' as const, + messageType: "error" as const, content: formatErrorMessage(error), }; } }; return { - type: 'stream_messages', + type: "stream_messages", messages: messages(), }; } @@ -296,26 +281,26 @@ export const summaryCommand: SlashCommand = { try { const { filePathForDisplay } = await executeSummaryGeneration(history); - if (executionMode === 'non_interactive') { + if (executionMode === "non_interactive") { return { - type: 'message', - messageType: 'info', + type: "message", + messageType: "info", content: formatSuccessMessage(filePathForDisplay), }; } // Interactive mode: UI components already display progress and completion. return { - type: 'message', - messageType: 'info', - content: '', + type: "message", + messageType: "info", + content: "", }; } catch (error) { failInteractive(error); return { - type: 'message', - messageType: 'error', + type: "message", + messageType: "error", content: formatErrorMessage(error), }; } diff --git a/apps/airiscode-cli/src/ui/commands/terminalSetupCommand.ts b/apps/airiscode-cli/src/ui/commands/terminalSetupCommand.ts index 3fb854466..54b9177cf 100644 --- a/apps/airiscode-cli/src/ui/commands/terminalSetupCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/terminalSetupCommand.ts @@ -4,10 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { MessageActionReturn, SlashCommand } from './types.js'; -import { CommandKind } from './types.js'; -import { terminalSetup } from '../utils/terminalSetup.js'; -import { t } from '../../i18n/index.js'; +import { t } from "../../i18n/index.js"; +import { terminalSetup } from "../utils/terminalSetup.js"; +import type { MessageActionReturn, SlashCommand } from "./types.js"; +import { CommandKind } from "./types.js"; /** * Command to configure terminal keybindings for multiline input support. @@ -16,10 +16,10 @@ import { t } from '../../i18n/index.js'; * to support Shift+Enter and Ctrl+Enter for multiline input. */ export const terminalSetupCommand: SlashCommand = { - name: 'terminal-setup', + name: "terminal-setup", get description() { return t( - 'Configure terminal keybindings for multiline input (VS Code, Cursor, Windsurf, Trae)', + "Configure terminal keybindings for multiline input (VS Code, Cursor, Windsurf, Trae)", ); }, kind: CommandKind.BUILT_IN, @@ -30,23 +30,21 @@ export const terminalSetupCommand: SlashCommand = { let content = result.message; if (result.requiresRestart) { - content += - '\n\n' + - t('Please restart your terminal for the changes to take effect.'); + content += "\n\n" + t("Please restart your terminal for the changes to take effect."); } return { - type: 'message', + type: "message", content, - messageType: result.success ? 'info' : 'error', + messageType: result.success ? "info" : "error", }; } catch (error) { return { - type: 'message', - content: t('Failed to configure terminal: {{error}}', { + type: "message", + content: t("Failed to configure terminal: {{error}}", { error: String(error), }), - messageType: 'error', + messageType: "error", }; } }, diff --git a/apps/airiscode-cli/src/ui/commands/themeCommand.ts b/apps/airiscode-cli/src/ui/commands/themeCommand.ts index fd366366a..4f99fab7e 100644 --- a/apps/airiscode-cli/src/ui/commands/themeCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/themeCommand.ts @@ -4,18 +4,18 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { OpenDialogActionReturn, SlashCommand } from './types.js'; -import { CommandKind } from './types.js'; -import { t } from '../../i18n/index.js'; +import { t } from "../../i18n/index.js"; +import type { OpenDialogActionReturn, SlashCommand } from "./types.js"; +import { CommandKind } from "./types.js"; export const themeCommand: SlashCommand = { - name: 'theme', + name: "theme", get description() { - return t('change the theme'); + return t("change the theme"); }, kind: CommandKind.BUILT_IN, action: (_context, _args): OpenDialogActionReturn => ({ - type: 'dialog', - dialog: 'theme', + type: "dialog", + dialog: "theme", }), }; diff --git a/apps/airiscode-cli/src/ui/commands/toolsCommand.ts b/apps/airiscode-cli/src/ui/commands/toolsCommand.ts index a73403cda..3d6dbe927 100644 --- a/apps/airiscode-cli/src/ui/commands/toolsCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/toolsCommand.ts @@ -4,18 +4,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { - type CommandContext, - type SlashCommand, - CommandKind, -} from './types.js'; -import { MessageType, type HistoryItemToolsList } from '../types.js'; -import { t } from '../../i18n/index.js'; +import { t } from "../../i18n/index.js"; +import { type HistoryItemToolsList, MessageType } from "../types.js"; +import { type CommandContext, CommandKind, type SlashCommand } from "./types.js"; export const toolsCommand: SlashCommand = { - name: 'tools', + name: "tools", get description() { - return t('list available AIRIS Code tools. Usage: /tools [desc]'); + return t("list available AIRIS Code tools. Usage: /tools [desc]"); }, kind: CommandKind.BUILT_IN, action: async (context: CommandContext, args?: string): Promise => { @@ -23,7 +19,7 @@ export const toolsCommand: SlashCommand = { // Default to NOT showing descriptions. The user must opt in with an argument. let useShowDescriptions = false; - if (subCommand === 'desc' || subCommand === 'descriptions') { + if (subCommand === "desc" || subCommand === "descriptions") { useShowDescriptions = true; } @@ -32,7 +28,7 @@ export const toolsCommand: SlashCommand = { context.ui.addItem( { type: MessageType.ERROR, - text: t('Could not retrieve tool registry.'), + text: t("Could not retrieve tool registry."), }, Date.now(), ); @@ -41,7 +37,7 @@ export const toolsCommand: SlashCommand = { const tools = toolRegistry.getAllTools(); // Filter out MCP tools by checking for the absence of a serverName property - const geminiTools = tools.filter((tool) => !('serverName' in tool)); + const geminiTools = tools.filter((tool) => !("serverName" in tool)); const toolsListItem: HistoryItemToolsList = { type: MessageType.TOOLS_LIST, diff --git a/apps/airiscode-cli/src/ui/commands/trustCommand.ts b/apps/airiscode-cli/src/ui/commands/trustCommand.ts index 9fa566db2..f12467a2d 100644 --- a/apps/airiscode-cli/src/ui/commands/trustCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/trustCommand.ts @@ -4,18 +4,18 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { OpenDialogActionReturn, SlashCommand } from './types.js'; -import { CommandKind } from './types.js'; -import { t } from '../../i18n/index.js'; +import { t } from "../../i18n/index.js"; +import type { OpenDialogActionReturn, SlashCommand } from "./types.js"; +import { CommandKind } from "./types.js"; export const trustCommand: SlashCommand = { - name: 'trust', + name: "trust", get description() { - return t('Manage folder trust settings'); + return t("Manage folder trust settings"); }, kind: CommandKind.BUILT_IN, action: (): OpenDialogActionReturn => ({ - type: 'dialog', - dialog: 'trust', + type: "dialog", + dialog: "trust", }), }; diff --git a/apps/airiscode-cli/src/ui/commands/types.ts b/apps/airiscode-cli/src/ui/commands/types.ts index 28b212b52..5e73587f8 100644 --- a/apps/airiscode-cli/src/ui/commands/types.ts +++ b/apps/airiscode-cli/src/ui/commands/types.ts @@ -4,22 +4,18 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { MutableRefObject, ReactNode } from 'react'; -import type { Content, PartListUnion } from '@airiscode/core'; -import type { Config, GitService, Logger } from '@airiscode/core'; +import type { Config, Content, GitService, Logger, PartListUnion } from "@airiscode/core"; +import type { MutableRefObject, ReactNode } from "react"; +import type { LoadedSettings } from "../../config/settings.js"; +import type { SessionStatsState } from "../contexts/SessionContext.js"; +import type { UseHistoryManagerReturn } from "../hooks/useHistoryManager.js"; +import type { ExtensionUpdateAction, ExtensionUpdateStatus } from "../state/extensions.js"; import type { - HistoryItemWithoutId, + ConfirmationRequest, HistoryItem, HistoryItemBtw, - ConfirmationRequest, -} from '../types.js'; -import type { LoadedSettings } from '../../config/settings.js'; -import type { UseHistoryManagerReturn } from '../hooks/useHistoryManager.js'; -import type { SessionStatsState } from '../contexts/SessionContext.js'; -import type { - ExtensionUpdateAction, - ExtensionUpdateStatus, -} from '../state/extensions.js'; + HistoryItemWithoutId, +} from "../types.js"; // Grouped dependencies for clarity and easier mocking export interface CommandContext { @@ -30,7 +26,7 @@ export interface CommandContext { * - non_interactive: non-interactive CLI mode (text/json) * - acp: ACP/Zed integration mode */ - executionMode?: 'interactive' | 'non_interactive' | 'acp'; + executionMode?: "interactive" | "non_interactive" | "acp"; // Invocation properties for when commands are called. invocation?: { /** The raw, untrimmed input string from the user. */ @@ -51,7 +47,7 @@ export interface CommandContext { // UI state and history management ui: { /** Adds a new item to the history display. */ - addItem: UseHistoryManagerReturn['addItem']; + addItem: UseHistoryManagerReturn["addItem"]; /** Clears all history items and the console screen. */ clear: () => void; /** @@ -80,7 +76,7 @@ export interface CommandContext { * * @param history The array of history items to load. */ - loadHistory: UseHistoryManagerReturn['loadHistory']; + loadHistory: UseHistoryManagerReturn["loadHistory"]; toggleVimEnabled: () => Promise; setGeminiMdFileCount: (count: number) => void; reloadCommands: () => void; @@ -106,14 +102,14 @@ export interface CommandContext { * The return type for a command action that results in scheduling a tool call. */ export interface ToolActionReturn { - type: 'tool'; + type: "tool"; toolName: string; toolArgs: Record; } /** The return type for a command action that results in the app quitting. */ export interface QuitActionReturn { - type: 'quit'; + type: "quit"; messages: HistoryItem[]; } @@ -122,8 +118,8 @@ export interface QuitActionReturn { * being displayed to the user. */ export interface MessageActionReturn { - type: 'message'; - messageType: 'info' | 'error'; + type: "message"; + messageType: "info" | "error"; content: string; } @@ -132,41 +128,37 @@ export interface MessageActionReturn { * Used for long-running operations that need to send progress updates. */ export interface StreamMessagesActionReturn { - type: 'stream_messages'; - messages: AsyncGenerator< - { messageType: 'info' | 'error'; content: string }, - void, - unknown - >; + type: "stream_messages"; + messages: AsyncGenerator<{ messageType: "info" | "error"; content: string }, void, unknown>; } /** * The return type for a command action that needs to open a dialog. */ export interface OpenDialogActionReturn { - type: 'dialog'; + type: "dialog"; dialog: - | 'help' - | 'arena_start' - | 'arena_select' - | 'arena_stop' - | 'arena_status' - | 'auth' - | 'theme' - | 'editor' - | 'settings' - | 'model' - | 'fast-model' - | 'subagent_create' - | 'subagent_list' - | 'trust' - | 'permissions' - | 'approval-mode' - | 'resume' - | 'extensions_manage' - | 'hooks' - | 'mcp'; + | "help" + | "arena_start" + | "arena_select" + | "arena_stop" + | "arena_status" + | "auth" + | "theme" + | "editor" + | "settings" + | "model" + | "fast-model" + | "subagent_create" + | "subagent_list" + | "trust" + | "permissions" + | "approval-mode" + | "resume" + | "extensions_manage" + | "hooks" + | "mcp"; } /** @@ -174,7 +166,7 @@ export interface OpenDialogActionReturn { * the entire conversation history. */ export interface LoadHistoryActionReturn { - type: 'load_history'; + type: "load_history"; history: HistoryItemWithoutId[]; clientHistory: Content[]; // The history for the generative client } @@ -184,7 +176,7 @@ export interface LoadHistoryActionReturn { * content as a prompt to the Gemini model. */ export interface SubmitPromptActionReturn { - type: 'submit_prompt'; + type: "submit_prompt"; content: PartListUnion; } @@ -193,7 +185,7 @@ export interface SubmitPromptActionReturn { * confirmation for a set of shell commands before proceeding. */ export interface ConfirmShellCommandsActionReturn { - type: 'confirm_shell_commands'; + type: "confirm_shell_commands"; /** The list of shell commands that require user confirmation. */ commandsToConfirm: string[]; /** The original invocation context to be re-run after confirmation. */ @@ -203,7 +195,7 @@ export interface ConfirmShellCommandsActionReturn { } export interface ConfirmActionReturn { - type: 'confirm_action'; + type: "confirm_action"; /** The React node to display as the confirmation prompt. */ prompt: ReactNode; /** The original invocation context to be re-run after confirmation. */ @@ -224,10 +216,10 @@ export type SlashCommandActionReturn = | ConfirmActionReturn; export enum CommandKind { - BUILT_IN = 'built-in', - FILE = 'file', - MCP_PROMPT = 'mcp-prompt', - SKILL = 'skill', + BUILT_IN = "built-in", + FILE = "file", + MCP_PROMPT = "mcp-prompt", + SKILL = "skill", } export interface CommandCompletionItem { @@ -252,10 +244,7 @@ export interface SlashCommand { action?: ( context: CommandContext, args: string, // TODO: Remove args. CommandContext now contains the complete invocation. - ) => - | void - | SlashCommandActionReturn - | Promise; + ) => void | SlashCommandActionReturn | Promise; // Provides argument completion completion?: ( diff --git a/apps/airiscode-cli/src/ui/commands/vimCommand.ts b/apps/airiscode-cli/src/ui/commands/vimCommand.ts index 8f3dc6bd0..aeb4e4786 100644 --- a/apps/airiscode-cli/src/ui/commands/vimCommand.ts +++ b/apps/airiscode-cli/src/ui/commands/vimCommand.ts @@ -4,25 +4,23 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { SlashCommand } from './types.js'; -import { CommandKind } from './types.js'; -import { t } from '../../i18n/index.js'; +import { t } from "../../i18n/index.js"; +import type { SlashCommand } from "./types.js"; +import { CommandKind } from "./types.js"; export const vimCommand: SlashCommand = { - name: 'vim', + name: "vim", get description() { - return t('toggle vim mode on/off'); + return t("toggle vim mode on/off"); }, kind: CommandKind.BUILT_IN, action: async (context, _args) => { const newVimState = await context.ui.toggleVimEnabled(); - const message = newVimState - ? 'Entered Vim mode. Run /vim again to exit.' - : 'Exited Vim mode.'; + const message = newVimState ? "Entered Vim mode. Run /vim again to exit." : "Exited Vim mode."; return { - type: 'message', - messageType: 'info', + type: "message", + messageType: "info", content: message, }; }, diff --git a/apps/airiscode-cli/src/ui/components/AboutBox.tsx b/apps/airiscode-cli/src/ui/components/AboutBox.tsx index 70ef13711..6b7ff1cad 100644 --- a/apps/airiscode-cli/src/ui/components/AboutBox.tsx +++ b/apps/airiscode-cli/src/ui/components/AboutBox.tsx @@ -4,12 +4,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type React from 'react'; -import { Box, Text } from 'ink'; -import { theme } from '../semantic-colors.js'; -import type { ExtendedSystemInfo } from '../../utils/systemInfo.js'; -import { getSystemInfoFields } from '../../utils/systemInfoFields.js'; -import { t } from '../../i18n/index.js'; +import { Box, Text } from "ink"; +import type React from "react"; +import { t } from "../../i18n/index.js"; +import type { ExtendedSystemInfo } from "../../utils/systemInfo.js"; +import { getSystemInfoFields } from "../../utils/systemInfoFields.js"; +import { theme } from "../semantic-colors.js"; type AboutBoxProps = ExtendedSystemInfo & { width?: number; @@ -28,15 +28,11 @@ export const AboutBox: React.FC = ({ width, ...props }) => { > - {t('Status')} + {t("Status")} {fields.map((field) => ( - + {field.label} diff --git a/apps/airiscode-cli/src/ui/components/AnsiOutput.tsx b/apps/airiscode-cli/src/ui/components/AnsiOutput.tsx index 7541a2eda..f26127177 100644 --- a/apps/airiscode-cli/src/ui/components/AnsiOutput.tsx +++ b/apps/airiscode-cli/src/ui/components/AnsiOutput.tsx @@ -4,13 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type React from 'react'; -import { Text } from 'ink'; -import type { - AnsiLine, - AnsiOutput, - AnsiToken, -} from '@airiscode/core'; +import type { AnsiLine, AnsiOutput, AnsiToken } from "@airiscode/core"; +import { Text } from "ink"; +import type React from "react"; const DEFAULT_HEIGHT = 24; @@ -19,10 +15,7 @@ interface AnsiOutputProps { availableTerminalHeight?: number; } -export const AnsiOutputText: React.FC = ({ - data, - availableTerminalHeight, -}) => { +export const AnsiOutputText: React.FC = ({ data, availableTerminalHeight }) => { const lastLines = data.slice( -(availableTerminalHeight && availableTerminalHeight > 0 ? availableTerminalHeight diff --git a/apps/airiscode-cli/src/ui/components/ApiKeyInput.tsx b/apps/airiscode-cli/src/ui/components/ApiKeyInput.tsx index bf885b30d..907c9cf43 100644 --- a/apps/airiscode-cli/src/ui/components/ApiKeyInput.tsx +++ b/apps/airiscode-cli/src/ui/components/ApiKeyInput.tsx @@ -4,15 +4,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type React from 'react'; -import { useState } from 'react'; -import { Box, Text } from 'ink'; -import { TextInput } from './shared/TextInput.js'; -import { theme } from '../semantic-colors.js'; -import { useKeypress } from '../hooks/useKeypress.js'; -import { t } from '../../i18n/index.js'; -import { CodingPlanRegion } from '../../constants/codingPlan.js'; -import Link from 'ink-link'; +import { Box, Text } from "ink"; +import Link from "ink-link"; +import type React from "react"; +import { useState } from "react"; +import { CodingPlanRegion } from "../../constants/codingPlan.js"; +import { t } from "../../i18n/index.js"; +import { useKeypress } from "../hooks/useKeypress.js"; +import { theme } from "../semantic-colors.js"; +import { TextInput } from "./shared/TextInput.js"; interface ApiKeyInputProps { onSubmit: (apiKey: string) => void; @@ -20,45 +20,35 @@ interface ApiKeyInputProps { region?: CodingPlanRegion; } -const CODING_PLAN_API_KEY_URL = - 'https://bailian.console.aliyun.com/?tab=model#/efm/coding_plan'; +const CODING_PLAN_API_KEY_URL = "https://bailian.console.aliyun.com/?tab=model#/efm/coding_plan"; const CODING_PLAN_INTL_API_KEY_URL = - 'https://modelstudio.console.alibabacloud.com/?tab=dashboard#/efm/coding_plan'; + "https://modelstudio.console.alibabacloud.com/?tab=dashboard#/efm/coding_plan"; export function ApiKeyInput({ onSubmit, onCancel, region = CodingPlanRegion.CHINA, }: ApiKeyInputProps): React.JSX.Element { - const [apiKey, setApiKey] = useState(''); + const [apiKey, setApiKey] = useState(""); const [error, setError] = useState(null); const apiKeyUrl = - region === CodingPlanRegion.GLOBAL - ? CODING_PLAN_INTL_API_KEY_URL - : CODING_PLAN_API_KEY_URL; + region === CodingPlanRegion.GLOBAL ? CODING_PLAN_INTL_API_KEY_URL : CODING_PLAN_API_KEY_URL; useKeypress( (key) => { - if (key.name === 'escape') { + if (key.name === "escape") { onCancel(); - } else if (key.name === 'return') { + } else if (key.name === "return") { const trimmedKey = apiKey.trim(); if (!trimmedKey) { - setError(t('API key cannot be empty.')); + setError(t("API key cannot be empty.")); return; } // Only validate sk-sp- prefix for China region (aliyun.com) - if ( - region === CodingPlanRegion.CHINA && - !trimmedKey.startsWith('sk-sp-') - ) { - setError( - t( - 'Invalid API key. Coding Plan API keys start with "sk-sp-". Please check.', - ), - ); + if (region === CodingPlanRegion.CHINA && !trimmedKey.startsWith("sk-sp-")) { + setError(t('Invalid API key. Coding Plan API keys start with "sk-sp-". Please check.')); return; } onSubmit(trimmedKey); @@ -76,7 +66,7 @@ export function ApiKeyInput({ )} - {t('You can get your Coding Plan API key here')} + {t("You can get your Coding Plan API key here")} @@ -86,9 +76,7 @@ export function ApiKeyInput({ - - {t('Enter to submit, Esc to go back')} - + {t("Enter to submit, Esc to go back")} ); diff --git a/apps/airiscode-cli/src/ui/components/AppHeader.tsx b/apps/airiscode-cli/src/ui/components/AppHeader.tsx index 8270827fe..57f703257 100644 --- a/apps/airiscode-cli/src/ui/components/AppHeader.tsx +++ b/apps/airiscode-cli/src/ui/components/AppHeader.tsx @@ -4,14 +4,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { Box } from 'ink'; -import { AuthType } from '@airiscode/core'; -import { Header, AuthDisplayType } from './Header.js'; -import { Tips } from './Tips.js'; -import { useSettings } from '../contexts/SettingsContext.js'; -import { useConfig } from '../contexts/ConfigContext.js'; -import { useUIState } from '../contexts/UIStateContext.js'; -import { isCodingPlanConfig } from '../../constants/codingPlan.js'; +import { AuthType } from "@airiscode/core"; +import { Box } from "ink"; +import { isCodingPlanConfig } from "../../constants/codingPlan.js"; +import { useConfig } from "../contexts/ConfigContext.js"; +import { useSettings } from "../contexts/SettingsContext.js"; +import { useUIState } from "../contexts/UIStateContext.js"; +import { AuthDisplayType, Header } from "./Header.js"; +import { Tips } from "./Tips.js"; interface AppHeaderProps { version: string; diff --git a/apps/airiscode-cli/src/ui/components/ApprovalModeDialog.tsx b/apps/airiscode-cli/src/ui/components/ApprovalModeDialog.tsx index 4fea0b557..1e5f5debf 100644 --- a/apps/airiscode-cli/src/ui/components/ApprovalModeDialog.tsx +++ b/apps/airiscode-cli/src/ui/components/ApprovalModeDialog.tsx @@ -4,18 +4,18 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type React from 'react'; -import { useCallback, useState } from 'react'; -import { Box, Text } from 'ink'; -import { theme } from '../semantic-colors.js'; -import { ApprovalMode, APPROVAL_MODES } from '@airiscode/core'; -import { RadioButtonSelect } from './shared/RadioButtonSelect.js'; -import type { LoadedSettings } from '../../config/settings.js'; -import { SettingScope } from '../../config/settings.js'; -import { getScopeMessageForSetting } from '../../utils/dialogScopeUtils.js'; -import { useKeypress } from '../hooks/useKeypress.js'; -import { ScopeSelector } from './shared/ScopeSelector.js'; -import { t } from '../../i18n/index.js'; +import { APPROVAL_MODES, ApprovalMode } from "@airiscode/core"; +import { Box, Text } from "ink"; +import type React from "react"; +import { useCallback, useState } from "react"; +import type { LoadedSettings } from "../../config/settings.js"; +import { SettingScope } from "../../config/settings.js"; +import { t } from "../../i18n/index.js"; +import { getScopeMessageForSetting } from "../../utils/dialogScopeUtils.js"; +import { useKeypress } from "../hooks/useKeypress.js"; +import { theme } from "../semantic-colors.js"; +import { RadioButtonSelect } from "./shared/RadioButtonSelect.js"; +import { ScopeSelector } from "./shared/ScopeSelector.js"; interface ApprovalModeDialogProps { /** Callback function when an approval mode is selected */ @@ -34,15 +34,15 @@ interface ApprovalModeDialogProps { const formatModeDescription = (mode: ApprovalMode): string => { switch (mode) { case ApprovalMode.PLAN: - return t('Analyze only, do not modify files or execute commands'); + return t("Analyze only, do not modify files or execute commands"); case ApprovalMode.DEFAULT: - return t('Require approval for file edits or shell commands'); + return t("Require approval for file edits or shell commands"); case ApprovalMode.AUTO_EDIT: - return t('Automatically approve file edits'); + return t("Automatically approve file edits"); case ApprovalMode.YOLO: - return t('Automatically approve all tools'); + return t("Automatically approve all tools"); default: - return t('{{mode}} mode', { mode }); + return t("{{mode}} mode", { mode }); } }; @@ -53,9 +53,7 @@ export function ApprovalModeDialog({ availableTerminalHeight: _availableTerminalHeight, }: ApprovalModeDialogProps): React.JSX.Element { // Start with User scope by default - const [selectedScope, setSelectedScope] = useState( - SettingScope.User, - ); + const [selectedScope, setSelectedScope] = useState(SettingScope.User); // Track the currently highlighted approval mode const [highlightedMode, setHighlightedMode] = useState( @@ -70,9 +68,7 @@ export function ApprovalModeDialog({ })); // Find the index of the current mode - const initialModeIndex = modeItems.findIndex( - (item) => item.value === highlightedMode, - ); + const initialModeIndex = modeItems.findIndex((item) => item.value === highlightedMode); const safeInitialModeIndex = initialModeIndex >= 0 ? initialModeIndex : 0; const handleModeSelect = useCallback( @@ -92,17 +88,17 @@ export function ApprovalModeDialog({ const handleScopeSelect = useCallback((scope: SettingScope) => { setSelectedScope(scope); - setMode('mode'); + setMode("mode"); }, []); - const [mode, setMode] = useState<'mode' | 'scope'>('mode'); + const [mode, setMode] = useState<"mode" | "scope">("mode"); useKeypress( (key) => { - if (key.name === 'tab') { - setMode((prev) => (prev === 'mode' ? 'scope' : 'mode')); + if (key.name === "tab") { + setMode((prev) => (prev === "mode" ? "scope" : "mode")); } - if (key.name === 'escape') { + if (key.name === "escape") { onSelect(undefined, selectedScope); } }, @@ -111,7 +107,7 @@ export function ApprovalModeDialog({ // Generate scope message for approval mode setting const otherScopeModifiedMessage = getScopeMessageForSetting( - 'tools.approvalMode', + "tools.approvalMode", selectedScope, settings, ); @@ -119,7 +115,7 @@ export function ApprovalModeDialog({ // Check if user scope is selected but workspace has the setting const showWorkspacePriorityWarning = selectedScope === SettingScope.User && - otherScopeModifiedMessage.toLowerCase().includes('workspace'); + otherScopeModifiedMessage.toLowerCase().includes("workspace"); return ( - {mode === 'mode' ? ( + {mode === "mode" ? ( {/* Approval Mode Selection */} - - {mode === 'mode' ? '> ' : ' '} - {t('Approval Mode')}{' '} - - {otherScopeModifiedMessage} - + + {mode === "mode" ? "> " : " "} + {t("Approval Mode")}{" "} + {otherScopeModifiedMessage} {/* Warning when workspace setting will override user setting */} {showWorkspacePriorityWarning && ( - ⚠{' '} + ⚠{" "} {t( - 'Workspace approval mode exists and takes priority. User-level change will have no effect.', + "Workspace approval mode exists and takes priority. User-level change will have no effect.", )} @@ -166,15 +160,15 @@ export function ApprovalModeDialog({ )} - {mode === 'mode' - ? t('(Use Enter to select, Tab to configure scope)') - : t('(Use Enter to apply scope, Tab to go back)')} + {mode === "mode" + ? t("(Use Enter to select, Tab to configure scope)") + : t("(Use Enter to apply scope, Tab to go back)")} diff --git a/apps/airiscode-cli/src/ui/components/AutoAcceptIndicator.tsx b/apps/airiscode-cli/src/ui/components/AutoAcceptIndicator.tsx index ec7249625..57adce86e 100644 --- a/apps/airiscode-cli/src/ui/components/AutoAcceptIndicator.tsx +++ b/apps/airiscode-cli/src/ui/components/AutoAcceptIndicator.tsx @@ -4,42 +4,38 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type React from 'react'; -import { Text } from 'ink'; -import { theme } from '../semantic-colors.js'; -import { ApprovalMode } from '@airiscode/core'; -import { t } from '../../i18n/index.js'; +import { ApprovalMode } from "@airiscode/core"; +import { Text } from "ink"; +import type React from "react"; +import { t } from "../../i18n/index.js"; +import { theme } from "../semantic-colors.js"; interface AutoAcceptIndicatorProps { approvalMode: ApprovalMode; } -export const AutoAcceptIndicator: React.FC = ({ - approvalMode, -}) => { - let textColor = ''; - let textContent = ''; - let subText = ''; +export const AutoAcceptIndicator: React.FC = ({ approvalMode }) => { + let textColor = ""; + let textContent = ""; + let subText = ""; const cycleText = - process.platform === 'win32' - ? ` ${t('(tab to cycle)')}` - : ` ${t('(shift + tab to cycle)')}`; + process.platform === "win32" ? ` ${t("(tab to cycle)")}` : ` ${t("(shift + tab to cycle)")}`; switch (approvalMode) { case ApprovalMode.PLAN: textColor = theme.status.success; - textContent = t('plan mode'); + textContent = t("plan mode"); subText = cycleText; break; case ApprovalMode.AUTO_EDIT: textColor = theme.status.warning; - textContent = t('auto-accept edits'); + textContent = t("auto-accept edits"); subText = cycleText; break; case ApprovalMode.YOLO: textColor = theme.status.error; - textContent = t('YOLO mode'); + textContent = t("YOLO mode"); subText = cycleText; break; case ApprovalMode.DEFAULT: diff --git a/apps/airiscode-cli/src/ui/components/BaseTextInput.tsx b/apps/airiscode-cli/src/ui/components/BaseTextInput.tsx index 07eb1a693..1c27acc48 100644 --- a/apps/airiscode-cli/src/ui/components/BaseTextInput.tsx +++ b/apps/airiscode-cli/src/ui/components/BaseTextInput.tsx @@ -19,16 +19,16 @@ * and AgentComposer (with minimal customization). */ -import type React from 'react'; -import { useCallback } from 'react'; -import { Box, Text } from 'ink'; -import chalk from 'chalk'; -import type { TextBuffer } from './shared/text-buffer.js'; -import type { Key } from '../hooks/useKeypress.js'; -import { useKeypress } from '../hooks/useKeypress.js'; -import { keyMatchers, Command } from '../keyMatchers.js'; -import { cpSlice, cpLen } from '../utils/textUtils.js'; -import { theme } from '../semantic-colors.js'; +import chalk from "chalk"; +import { Box, Text } from "ink"; +import type React from "react"; +import { useCallback } from "react"; +import type { Key } from "../hooks/useKeypress.js"; +import { useKeypress } from "../hooks/useKeypress.js"; +import { Command, keyMatchers } from "../keyMatchers.js"; +import { theme } from "../semantic-colors.js"; +import { cpLen, cpSlice } from "../utils/textUtils.js"; +import type { TextBuffer } from "./shared/text-buffer.js"; // ─── Types ────────────────────────────────────────────────── @@ -91,7 +91,7 @@ export function defaultRenderLine({ showCursor, }: RenderLineOptions): React.ReactNode { if (!isOnCursorLine || !showCursor) { - return {lineText || ' '}; + return {lineText || " "}; } const len = cpLen(lineText); @@ -101,7 +101,7 @@ export function defaultRenderLine({ return ( {lineText} - {chalk.inverse(' ') + '\u200B'} + {chalk.inverse(" ") + "\u200B"} ); } @@ -147,7 +147,7 @@ export const BaseTextInput: React.FC = ({ if (keyMatchers[Command.SUBMIT](key)) { if (buffer.text.trim()) { const text = buffer.text; - buffer.setText(''); + buffer.setText(""); onSubmit(text); } return; @@ -162,7 +162,7 @@ export const BaseTextInput: React.FC = ({ // Escape → clear input if (keyMatchers[Command.ESCAPE](key)) { if (buffer.text.length > 0) { - buffer.setText(''); + buffer.setText(""); } return; } @@ -170,20 +170,20 @@ export const BaseTextInput: React.FC = ({ // Ctrl+C → clear input if (keyMatchers[Command.CLEAR_INPUT](key)) { if (buffer.text.length > 0) { - buffer.setText(''); + buffer.setText(""); } return; } // Ctrl+A → home if (keyMatchers[Command.HOME](key)) { - buffer.move('home'); + buffer.move("home"); return; } // Ctrl+E → end if (keyMatchers[Command.END](key)) { - buffer.move('end'); + buffer.move("end"); return; } @@ -212,11 +212,7 @@ export const BaseTextInput: React.FC = ({ } // Backspace - if ( - key.name === 'backspace' || - key.sequence === '\x7f' || - (key.ctrl && key.name === 'h') - ) { + if (key.name === "backspace" || key.sequence === "\x7f" || (key.ctrl && key.name === "h")) { buffer.backspace(); return; } @@ -236,9 +232,7 @@ export const BaseTextInput: React.FC = ({ const scrollVisualRow = buffer.visualScrollRow; const resolvedBorderColor = borderColor ?? theme.border.focused; - const resolvedPrefix = prefix ?? ( - {'> '} - ); + const resolvedPrefix = prefix ?? {"> "}; return ( { const config = useConfig(); @@ -107,8 +107,8 @@ export const Composer = () => { isEmbeddedShellFocused={uiState.embeddedShellFocused} placeholder={ vimEnabled - ? ' ' + t("Press 'i' for INSERT mode and 'Esc' for NORMAL mode.") - : ' ' + t('Type your message or @path/to/file') + ? " " + t("Press 'i' for INSERT mode and 'Esc' for NORMAL mode.") + : " " + t("Type your message or @path/to/file") } promptSuggestion={uiState.promptSuggestion} onPromptSuggestionDismiss={uiState.dismissPromptSuggestion} @@ -119,11 +119,7 @@ export const Composer = () => { {/* Hide footer when a confirmation dialog (e.g. ask_user_question) is active */} {uiState.isInputActive && !showSuggestions && - (showShortcuts ? ( - - ) : ( - !isScreenReaderEnabled &&