feat(server): Add settings migration for phaseModels

- Add migratePhaseModels() to handle legacy enhancementModel/validationModel fields
- Deep merge phaseModels in updateGlobalSettings()
- Export PhaseModelConfig, PhaseModelKey, and DEFAULT_PHASE_MODELS from types
- Backwards compatible: legacy fields migrate to phaseModels structure

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Kacper
2025-12-30 14:16:53 +01:00
parent 45d93f28bf
commit 24599e0b8c
2 changed files with 52 additions and 0 deletions

View File

@@ -27,11 +27,13 @@ import type {
TrashedProjectRef,
BoardBackgroundSettings,
WorktreeInfo,
PhaseModelConfig,
} from '../types/settings.js';
import {
DEFAULT_GLOBAL_SETTINGS,
DEFAULT_CREDENTIALS,
DEFAULT_PROJECT_SETTINGS,
DEFAULT_PHASE_MODELS,
SETTINGS_VERSION,
CREDENTIALS_VERSION,
PROJECT_SETTINGS_VERSION,
@@ -130,6 +132,9 @@ export class SettingsService {
const settingsPath = getGlobalSettingsPath(this.dataDir);
const settings = await readJsonFile<GlobalSettings>(settingsPath, DEFAULT_GLOBAL_SETTINGS);
// Migrate legacy enhancementModel/validationModel to phaseModels
const migratedPhaseModels = this.migratePhaseModels(settings);
// Apply any missing defaults (for backwards compatibility)
return {
...DEFAULT_GLOBAL_SETTINGS,
@@ -138,9 +143,45 @@ export class SettingsService {
...DEFAULT_GLOBAL_SETTINGS.keyboardShortcuts,
...settings.keyboardShortcuts,
},
phaseModels: migratedPhaseModels,
};
}
/**
* Migrate legacy enhancementModel/validationModel fields to phaseModels structure
*
* Handles backwards compatibility for settings created before phaseModels existed.
* Legacy fields take precedence over defaults but phaseModels takes precedence over legacy.
*
* @param settings - Raw settings from file
* @returns Complete PhaseModelConfig with all fields populated
*/
private migratePhaseModels(settings: Partial<GlobalSettings>): PhaseModelConfig {
// Start with defaults
const result: PhaseModelConfig = { ...DEFAULT_PHASE_MODELS };
// If phaseModels exists, use it (with defaults for any missing fields)
if (settings.phaseModels) {
return {
...DEFAULT_PHASE_MODELS,
...settings.phaseModels,
};
}
// Migrate legacy fields if phaseModels doesn't exist
// These were the only two legacy fields that existed
if (settings.enhancementModel) {
result.enhancementModel = settings.enhancementModel;
logger.debug(`Migrated legacy enhancementModel: ${settings.enhancementModel}`);
}
if (settings.validationModel) {
result.validationModel = settings.validationModel;
logger.debug(`Migrated legacy validationModel: ${settings.validationModel}`);
}
return result;
}
/**
* Update global settings with partial changes
*
@@ -169,6 +210,14 @@ export class SettingsService {
};
}
// Deep merge phaseModels if provided
if (updates.phaseModels) {
updated.phaseModels = {
...current.phaseModels,
...updates.phaseModels,
};
}
await atomicWriteJson(settingsPath, updated);
logger.info('Global settings updated');