feat: migrate command format

This commit is contained in:
LaZzyMan 2026-01-07 13:43:00 +08:00
parent 22504b0a5b
commit 50dac93c80
8 changed files with 297 additions and 32 deletions

View file

@ -38,6 +38,7 @@ import {
getErrorMessage,
getAllGeminiMdFilenames,
ShellExecutionService,
Storage,
} from '@qwen-code/qwen-code-core';
import { buildResumedHistoryItems } from './utils/resumeHistoryUtils.js';
import { validateAuthMethod } from '../config/auth.js';
@ -76,6 +77,9 @@ 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 { appEvents, AppEvent } from '../utils/events.js';
import { type UpdateObject } from './utils/updateCheck.js';
import { setUpdateHandler } from '../utils/handleAutoUpdate.js';
@ -845,6 +849,13 @@ export const AppContainer = (props: AppContainerProps) => {
!idePromptAnswered,
);
// Command migration nudge
const {
showMigrationNudge: shouldShowCommandMigrationNudge,
tomlFiles: commandMigrationTomlFiles,
setShowMigrationNudge: setShowCommandMigrationNudge,
} = useCommandMigration(settings, config.storage);
const [showErrorDetails, setShowErrorDetails] = useState<boolean>(false);
const [showToolDescriptions, setShowToolDescriptions] =
useState<boolean>(false);
@ -935,6 +946,92 @@ export const AppContainer = (props: AppContainerProps) => {
[handleSlashCommand, settings],
);
const handleCommandMigrationComplete = useCallback(
async (result: CommandMigrationNudgeResult) => {
setShowCommandMigrationNudge(false);
if (result.userSelection === 'yes') {
// Perform migration for both workspace and user levels
try {
const results = [];
// Migrate workspace commands
const workspaceCommandsDir = config.storage.getProjectCommandsDir();
const workspaceResult = await migrateTomlCommands({
commandDir: workspaceCommandsDir,
createBackup: true,
deleteOriginal: false,
});
if (
workspaceResult.convertedFiles.length > 0 ||
workspaceResult.failedFiles.length > 0
) {
results.push({ level: 'workspace', result: workspaceResult });
}
// Migrate user commands
const userCommandsDir = Storage.getUserCommandsDir();
const userResult = await migrateTomlCommands({
commandDir: userCommandsDir,
createBackup: true,
deleteOriginal: false,
});
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
) {
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`,
},
Date.now(),
);
}
if (migrationResult.failedFiles.length > 0) {
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')}`,
},
Date.now(),
);
}
}
if (results.length === 0) {
historyManager.addItem(
{
type: MessageType.INFO,
text: ' No TOML files found to migrate.',
},
Date.now(),
);
}
} catch (error) {
historyManager.addItem(
{
type: MessageType.ERROR,
text: `❌ Migration failed: ${getErrorMessage(error)}`,
},
Date.now(),
);
}
}
},
[historyManager, setShowCommandMigrationNudge, config.storage],
);
const { elapsedTime, currentLoadingPhrase } = useLoadingIndicator(
streamingState,
settings.merged.ui?.customWittyPhrases,
@ -1179,6 +1276,7 @@ export const AppContainer = (props: AppContainerProps) => {
showWelcomeBackDialog ||
showWorkspaceMigrationDialog ||
shouldShowIdePrompt ||
shouldShowCommandMigrationNudge ||
isFolderTrustDialogOpen ||
!!shellConfirmationRequest ||
!!confirmationRequest ||
@ -1244,6 +1342,8 @@ export const AppContainer = (props: AppContainerProps) => {
suggestionsWidth,
isInputActive,
shouldShowIdePrompt,
shouldShowCommandMigrationNudge,
commandMigrationTomlFiles,
isFolderTrustDialogOpen: isFolderTrustDialogOpen ?? false,
isTrustedFolder,
constrainHeight,
@ -1333,6 +1433,8 @@ export const AppContainer = (props: AppContainerProps) => {
suggestionsWidth,
isInputActive,
shouldShowIdePrompt,
shouldShowCommandMigrationNudge,
commandMigrationTomlFiles,
isFolderTrustDialogOpen,
isTrustedFolder,
constrainHeight,
@ -1404,6 +1506,7 @@ export const AppContainer = (props: AppContainerProps) => {
setShellModeActive,
vimHandleInput,
handleIdePromptComplete,
handleCommandMigrationComplete,
handleFolderTrustSelect,
setConstrainHeight,
onEscapePromptChange: handleEscapePromptChange,
@ -1441,6 +1544,7 @@ export const AppContainer = (props: AppContainerProps) => {
setShellModeActive,
vimHandleInput,
handleIdePromptComplete,
handleCommandMigrationComplete,
handleFolderTrustSelect,
setConstrainHeight,
handleEscapePromptChange,