mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-24 16:02:55 +08:00
fix(jetbrains): finish legacy migration progress
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@kilocode/kilo-jetbrains": patch
|
||||
---
|
||||
|
||||
Migrate JetBrains autocomplete settings and finish skipped chat history migration progress.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@kilocode/kilo-jetbrains": patch
|
||||
---
|
||||
|
||||
Open the migration flow directly on settings selection and support removing the legacy settings file after migration.
|
||||
+9
@@ -68,6 +68,15 @@ class LegacySettingsFileMigrationStore(
|
||||
|
||||
override fun cleanup(targets: LegacyCleanupTargets): LegacyCleanupReport {
|
||||
val root = read()?.toMutableMap() ?: return LegacyCleanupReport(cleaned = emptyList(), errors = emptyList())
|
||||
if (targets.legacySettingsFile) {
|
||||
val err = runCatching {
|
||||
if (file.delete()) null else "Failed to delete ${file.absolutePath}"
|
||||
}.getOrElse { it.message ?: "Failed to delete ${file.absolutePath}" }
|
||||
return LegacyCleanupReport(
|
||||
cleaned = if (err == null) listOf("legacySettingsFile") else emptyList(),
|
||||
errors = listOfNotNull(err),
|
||||
)
|
||||
}
|
||||
val cleaned = mutableListOf<String>()
|
||||
if (targets.providerProfiles && root.remove("providerProfiles") != null) cleaned.add("providerProfiles")
|
||||
if (targets.mcpSettings && root.remove("mcpSettings") != null) cleaned.add("mcpSettings")
|
||||
|
||||
+16
-7
@@ -218,15 +218,19 @@ class LegacyMigrationEngine(
|
||||
|
||||
// Check if already exists (unless force)
|
||||
if (!sel.force && backend.sessionExists(sessionId)) {
|
||||
val msg = "Session already exists, skipped"
|
||||
val progress = LegacyMigrationSessionProgress(info, idx, selections.sessions.size, MigrationSessionPhase.skipped)
|
||||
sink.session(progress)
|
||||
results.add(LegacyMigrationResultItem(sel.id, MigrationItemCategory.session, MigrationItemStatus.success, "Session already exists, skipped"))
|
||||
results.add(LegacyMigrationResultItem(sel.id, MigrationItemCategory.session, MigrationItemStatus.success, msg))
|
||||
sink.item(LegacyMigrationItemProgress(sel.id, MigrationItemProgressStatus.success, msg))
|
||||
continue
|
||||
}
|
||||
|
||||
if (conversationRaw == null) {
|
||||
sink.session(LegacyMigrationSessionProgress(info, idx, selections.sessions.size, MigrationSessionPhase.error, "Conversation file not found"))
|
||||
results.add(LegacyMigrationResultItem(sel.id, MigrationItemCategory.session, MigrationItemStatus.error, "Conversation file not found"))
|
||||
val msg = "Conversation file not found"
|
||||
sink.session(LegacyMigrationSessionProgress(info, idx, selections.sessions.size, MigrationSessionPhase.error, msg))
|
||||
results.add(LegacyMigrationResultItem(sel.id, MigrationItemCategory.session, MigrationItemStatus.error, msg))
|
||||
sink.item(LegacyMigrationItemProgress(sel.id, MigrationItemProgressStatus.error, msg))
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -238,6 +242,7 @@ class LegacyMigrationEngine(
|
||||
val msg = e.message ?: "Parse error"
|
||||
sink.session(LegacyMigrationSessionProgress(info, idx, selections.sessions.size, MigrationSessionPhase.error, msg))
|
||||
results.add(LegacyMigrationResultItem(sel.id, MigrationItemCategory.session, MigrationItemStatus.error, msg))
|
||||
sink.item(LegacyMigrationItemProgress(sel.id, MigrationItemProgressStatus.error, msg))
|
||||
null
|
||||
} ?: continue
|
||||
|
||||
@@ -247,6 +252,7 @@ class LegacyMigrationEngine(
|
||||
val msg = e.message ?: "Project import failed"
|
||||
sink.session(LegacyMigrationSessionProgress(info, idx, selections.sessions.size, MigrationSessionPhase.error, msg))
|
||||
results.add(LegacyMigrationResultItem(sel.id, MigrationItemCategory.session, MigrationItemStatus.error, msg))
|
||||
sink.item(LegacyMigrationItemProgress(sel.id, MigrationItemProgressStatus.error, msg))
|
||||
null
|
||||
} ?: continue
|
||||
|
||||
@@ -267,12 +273,15 @@ class LegacyMigrationEngine(
|
||||
val msg = e.message ?: "Session import failed"
|
||||
sink.session(LegacyMigrationSessionProgress(info, idx, selections.sessions.size, MigrationSessionPhase.error, msg))
|
||||
results.add(LegacyMigrationResultItem(sel.id, MigrationItemCategory.session, MigrationItemStatus.error, msg))
|
||||
sink.item(LegacyMigrationItemProgress(sel.id, MigrationItemProgressStatus.error, msg))
|
||||
null
|
||||
} ?: continue
|
||||
|
||||
if (importResult.skipped) {
|
||||
val msg = "Session already exists, skipped"
|
||||
sink.session(LegacyMigrationSessionProgress(info, idx, selections.sessions.size, MigrationSessionPhase.skipped))
|
||||
results.add(LegacyMigrationResultItem(sel.id, MigrationItemCategory.session, MigrationItemStatus.success, "Session already exists, skipped"))
|
||||
results.add(LegacyMigrationResultItem(sel.id, MigrationItemCategory.session, MigrationItemStatus.success, msg))
|
||||
sink.item(LegacyMigrationItemProgress(sel.id, MigrationItemProgressStatus.success, msg))
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -339,11 +348,11 @@ class LegacyMigrationEngine(
|
||||
}
|
||||
}
|
||||
|
||||
// Autocomplete — report skipped/warning (no JetBrains equivalent known yet)
|
||||
// Autocomplete settings are persisted by the JetBrains frontend before backend migration starts.
|
||||
if (selections.settings.autocomplete && settings.autocomplete != null) {
|
||||
sink.item(LegacyMigrationItemProgress("Autocomplete settings", MigrationItemProgressStatus.migrating))
|
||||
results.add(LegacyMigrationResultItem("Autocomplete settings", MigrationItemCategory.settings, MigrationItemStatus.warning, "Autocomplete settings migration is not yet available for JetBrains"))
|
||||
sink.item(LegacyMigrationItemProgress("Autocomplete settings", MigrationItemProgressStatus.warning, "Autocomplete settings migration is not yet available for JetBrains"))
|
||||
results.add(LegacyMigrationResultItem("Autocomplete settings", MigrationItemCategory.settings, MigrationItemStatus.success))
|
||||
sink.item(LegacyMigrationItemProgress("Autocomplete settings", MigrationItemProgressStatus.success))
|
||||
}
|
||||
|
||||
return LegacyMigrationReport(results)
|
||||
|
||||
+2
@@ -155,6 +155,7 @@ data class LegacyMigrationSelections(
|
||||
val sessions: List<MigrationSessionSelection>,
|
||||
val defaultModel: Boolean,
|
||||
val settings: MigrationSettingsSelections,
|
||||
val keepLegacySettingsFile: Boolean = true,
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -219,6 +220,7 @@ data class LegacyCleanupTargets(
|
||||
val customModes: Boolean = false,
|
||||
val globalState: Boolean = false,
|
||||
val taskHistory: Boolean = false,
|
||||
val legacySettingsFile: Boolean = false,
|
||||
)
|
||||
|
||||
data class LegacyCleanupReport(
|
||||
|
||||
+2
-2
@@ -113,7 +113,7 @@ class KiloMigrationRpcApiImpl : KiloMigrationRpcApi {
|
||||
}
|
||||
|
||||
override suspend fun cleanup(targets: LegacyCleanupTargetsDto): LegacyCleanupReportDto {
|
||||
LOG.info("Migration RPC cleanup: providerProfiles=${targets.providerProfiles} mcp=${targets.mcpSettings} modes=${targets.customModes} state=${targets.globalState} history=${targets.taskHistory}")
|
||||
LOG.info("Migration RPC cleanup: providerProfiles=${targets.providerProfiles} mcp=${targets.mcpSettings} modes=${targets.customModes} state=${targets.globalState} history=${targets.taskHistory} file=${targets.legacySettingsFile}")
|
||||
val mgr = manager()
|
||||
val store = storeService.store()
|
||||
val report = withContext(Dispatchers.IO) { mgr.cleanup(store, MigrationRpcMapper.fromDto(targets)) }
|
||||
@@ -122,5 +122,5 @@ class KiloMigrationRpcApiImpl : KiloMigrationRpcApi {
|
||||
}
|
||||
|
||||
private fun selectionSummary(selections: LegacyMigrationSelectionsDto): String =
|
||||
"providers=${selections.providers.size} mcp=${selections.mcpServers.size} modes=${selections.customModes.size} sessions=${selections.sessions.size} model=${selections.defaultModel} settings=true"
|
||||
"providers=${selections.providers.size} mcp=${selections.mcpServers.size} modes=${selections.customModes.size} sessions=${selections.sessions.size} model=${selections.defaultModel} settings=true keepFile=${selections.keepLegacySettingsFile}"
|
||||
}
|
||||
|
||||
+2
@@ -135,6 +135,7 @@ internal object MigrationRpcMapper {
|
||||
sessions = dto.sessions.map(::fromDto),
|
||||
defaultModel = dto.defaultModel,
|
||||
settings = fromDto(dto.settings),
|
||||
keepLegacySettingsFile = dto.keepLegacySettingsFile,
|
||||
)
|
||||
|
||||
private fun fromDto(dto: MigrationSessionSelectionDto): MigrationSessionSelection =
|
||||
@@ -223,6 +224,7 @@ internal object MigrationRpcMapper {
|
||||
customModes = dto.customModes,
|
||||
globalState = dto.globalState,
|
||||
taskHistory = dto.taskHistory,
|
||||
legacySettingsFile = dto.legacySettingsFile,
|
||||
)
|
||||
|
||||
fun toDto(r: LegacyCleanupReport): LegacyCleanupReportDto =
|
||||
|
||||
+71
-2
@@ -1,9 +1,11 @@
|
||||
package ai.kilocode.backend.migration
|
||||
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
/**
|
||||
@@ -142,10 +144,13 @@ class LegacyMigrationOrchestrationTest {
|
||||
val sessionId = ai.kilocode.backend.migration.session.LegacySessionIds.createSessionId("t1")
|
||||
backend.existingSessionIds = setOf(sessionId)
|
||||
val sel = noSelections().copy(sessions = listOf(MigrationSessionSelection("t1", force = false)))
|
||||
val report = eng.migrate(sel)
|
||||
val items = mutableListOf<LegacyMigrationItemProgress>()
|
||||
val report = eng.migrate(sel, itemSink(items))
|
||||
val item = report.items.find { it.category == MigrationItemCategory.session }
|
||||
assertNotNull(item)
|
||||
assertTrue(item!!.message?.contains("skipped", ignoreCase = true) == true)
|
||||
assertEquals(listOf(MigrationItemProgressStatus.migrating, MigrationItemProgressStatus.success), items.map { it.status })
|
||||
assertTrue(items[1].message?.contains("skipped", ignoreCase = true) == true)
|
||||
assertEquals(0, backend.projectCalls.size)
|
||||
}
|
||||
|
||||
@@ -173,9 +178,42 @@ class LegacyMigrationOrchestrationTest {
|
||||
}
|
||||
backend.sessionImportSkipped = true
|
||||
val sel = noSelections().copy(sessions = listOf(MigrationSessionSelection("t1")))
|
||||
eng.migrate(sel)
|
||||
val items = mutableListOf<LegacyMigrationItemProgress>()
|
||||
eng.migrate(sel, itemSink(items))
|
||||
assertEquals(0, backend.messageCalls.size)
|
||||
assertEquals(0, backend.partCalls.size)
|
||||
assertEquals(listOf(MigrationItemProgressStatus.migrating, MigrationItemProgressStatus.success), items.map { it.status })
|
||||
assertTrue(items[1].message?.contains("skipped", ignoreCase = true) == true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `migrate - missing session conversation emits terminal error progress`() {
|
||||
val (eng, _, _) = setup {
|
||||
taskHistory = """[{"id":"t1","task":"Test"}]"""
|
||||
}
|
||||
val sel = noSelections().copy(sessions = listOf(MigrationSessionSelection("t1")))
|
||||
val items = mutableListOf<LegacyMigrationItemProgress>()
|
||||
eng.migrate(sel, itemSink(items))
|
||||
assertEquals(listOf(MigrationItemProgressStatus.migrating, MigrationItemProgressStatus.error), items.map { it.status })
|
||||
assertEquals("Conversation file not found", items[1].message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `migrate - autocomplete settings report success`() {
|
||||
val (eng, _, _) = setup {
|
||||
globalState["ghostServiceSettings"] = kotlinx.serialization.json.JsonObject(
|
||||
mapOf(
|
||||
"enableAutoTrigger" to JsonPrimitive(true),
|
||||
"enableSmartInlineTaskKeybinding" to JsonPrimitive(true),
|
||||
"enableChatAutocomplete" to JsonPrimitive(true),
|
||||
)
|
||||
)
|
||||
}
|
||||
val sel = noSelections().copy(settings = noSelections().settings.copy(autocomplete = true))
|
||||
val report = eng.migrate(sel)
|
||||
val item = report.items.single { it.item == "Autocomplete settings" }
|
||||
assertEquals(MigrationItemStatus.success, item.status)
|
||||
assertEquals(null, item.message)
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
@@ -209,9 +247,40 @@ class LegacyMigrationOrchestrationTest {
|
||||
assertTrue(report.hasErrors)
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Cleanup
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
fun `cleanup - legacy settings file target deletes file`() {
|
||||
val (eng, fixture, _) = setup {
|
||||
providerProfiles = """{"currentApiConfigName":"p","apiConfigs":{}}"""
|
||||
}
|
||||
val report = eng.cleanup(LegacyCleanupTargets(legacySettingsFile = true))
|
||||
assertEquals(listOf("legacySettingsFile"), report.cleaned)
|
||||
assertEquals(emptyList<String>(), report.errors)
|
||||
assertFalse(fixture.exists())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `cleanup - data target preserves legacy settings file`() {
|
||||
val (eng, fixture, _) = setup {
|
||||
providerProfiles = """{"currentApiConfigName":"p","apiConfigs":{}}"""
|
||||
}
|
||||
val report = eng.cleanup(LegacyCleanupTargets(providerProfiles = true))
|
||||
assertEquals(listOf("providerProfiles"), report.cleaned)
|
||||
assertEquals(emptyList<String>(), report.errors)
|
||||
assertTrue(fixture.exists())
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Helpers
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
private fun assertNotNull(actual: Any?) = kotlin.test.assertNotNull(actual)
|
||||
|
||||
private fun itemSink(items: MutableList<LegacyMigrationItemProgress>) = object : LegacyMigrationSink {
|
||||
override fun item(progress: LegacyMigrationItemProgress) { items.add(progress) }
|
||||
override fun session(progress: LegacyMigrationSessionProgress) = Unit
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -28,6 +28,8 @@ internal class LegacySettingsFileFixture {
|
||||
migrationStatus = LegacySettingsFileMigrationStore(file).status()
|
||||
}
|
||||
|
||||
fun exists() = file.exists()
|
||||
|
||||
private fun flush() {
|
||||
val root = mutableMapOf<String, JsonElement>()
|
||||
migrationStatus?.let { root["migrationStatus"] = JsonPrimitive(it.name) }
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package ai.kilocode.client.autocomplete
|
||||
|
||||
import ai.kilocode.rpc.dto.LegacyAutocompleteSettingsDto
|
||||
import com.intellij.openapi.components.PersistentStateComponent
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.State
|
||||
import com.intellij.openapi.components.Storage
|
||||
import com.intellij.openapi.components.service
|
||||
|
||||
@Service(Service.Level.APP)
|
||||
@State(
|
||||
name = "KiloAutocompleteSettings",
|
||||
storages = [Storage("kiloAutocompleteSettings.xml")],
|
||||
)
|
||||
class KiloAutocompleteSettingsService : PersistentStateComponent<KiloAutocompleteSettingsService.State> {
|
||||
|
||||
data class State(
|
||||
var enableAutoTrigger: Boolean? = null,
|
||||
var enableSmartInlineTaskKeybinding: Boolean? = null,
|
||||
var enableChatAutocomplete: Boolean? = null,
|
||||
)
|
||||
|
||||
private var state = State()
|
||||
|
||||
override fun getState(): State = state
|
||||
|
||||
override fun loadState(state: State) {
|
||||
this.state = state
|
||||
}
|
||||
|
||||
fun applyLegacy(settings: LegacyAutocompleteSettingsDto) {
|
||||
settings.enableAutoTrigger?.let { state.enableAutoTrigger = it }
|
||||
settings.enableSmartInlineTaskKeybinding?.let { state.enableSmartInlineTaskKeybinding = it }
|
||||
settings.enableChatAutocomplete?.let { state.enableChatAutocomplete = it }
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getInstance(): KiloAutocompleteSettingsService = service()
|
||||
}
|
||||
}
|
||||
+39
-3
@@ -3,10 +3,13 @@
|
||||
package ai.kilocode.client.migration
|
||||
|
||||
import ai.kilocode.client.app.KiloAppService
|
||||
import ai.kilocode.client.autocomplete.KiloAutocompleteSettingsService
|
||||
import ai.kilocode.log.KiloLog
|
||||
import ai.kilocode.rpc.KiloMigrationRpcApi
|
||||
import ai.kilocode.rpc.dto.KiloAppStateDto
|
||||
import ai.kilocode.rpc.dto.KiloAppStatusDto
|
||||
import ai.kilocode.rpc.dto.LegacyAutocompleteSettingsDto
|
||||
import ai.kilocode.rpc.dto.LegacyCleanupTargetsDto
|
||||
import ai.kilocode.rpc.dto.LegacyMigrationEventDto
|
||||
import ai.kilocode.rpc.dto.LegacyMigrationResultItemDto
|
||||
import ai.kilocode.rpc.dto.LegacyMigrationStatusDto
|
||||
@@ -49,12 +52,19 @@ class KiloMigrationService internal constructor(
|
||||
private val cs: CoroutineScope,
|
||||
private val rpc: KiloMigrationRpcApi?,
|
||||
appState: StateFlow<KiloAppStateDto>?,
|
||||
private val autocomplete: ((LegacyAutocompleteSettingsDto) -> Unit)?,
|
||||
) : MigrationUiController {
|
||||
|
||||
/** Platform constructor — resolves RPC lazily. */
|
||||
constructor(cs: CoroutineScope) : this(cs, null, service<KiloAppService>().state)
|
||||
constructor(cs: CoroutineScope) : this(cs, null, service<KiloAppService>().state, null)
|
||||
|
||||
internal constructor(cs: CoroutineScope, rpc: KiloMigrationRpcApi?) : this(cs, rpc, null)
|
||||
internal constructor(cs: CoroutineScope, rpc: KiloMigrationRpcApi?) : this(cs, rpc, null, null)
|
||||
|
||||
internal constructor(
|
||||
cs: CoroutineScope,
|
||||
rpc: KiloMigrationRpcApi?,
|
||||
appState: StateFlow<KiloAppStateDto>?,
|
||||
) : this(cs, rpc, appState, null)
|
||||
|
||||
companion object {
|
||||
private val LOG = KiloLog.create(KiloMigrationService::class.java)
|
||||
@@ -67,6 +77,7 @@ class KiloMigrationService internal constructor(
|
||||
|
||||
private val migrating = AtomicBoolean(false)
|
||||
private val migrateJob = AtomicReference<Job?>(null)
|
||||
private val lastSelections = AtomicReference<MigrationUiSelections?>(null)
|
||||
|
||||
init {
|
||||
if (appState != null) {
|
||||
@@ -95,9 +106,11 @@ class KiloMigrationService internal constructor(
|
||||
return
|
||||
}
|
||||
LOG.info("Migration wizard: user started migration ${selectionSummary(selections)}")
|
||||
lastSelections.set(selections)
|
||||
|
||||
val dto = MigrationSelectionBuilder.toDto(selections)
|
||||
val initialProgress = buildInitialProgress(selections, current.detection)
|
||||
applyAutocomplete(selections, current.detection)
|
||||
|
||||
_state.value = current.copy(
|
||||
phase = MigrationUiPhase.migrating,
|
||||
@@ -194,10 +207,14 @@ class KiloMigrationService internal constructor(
|
||||
}
|
||||
val hasErrors = current.results.any { it.status == MigrationItemStatusDto.error }
|
||||
val status = if (hasErrors) LegacyMigrationStatusDto.completed_with_errors else LegacyMigrationStatusDto.completed
|
||||
val selections = lastSelections.get()
|
||||
LOG.info("Migration wizard: user finished migration status=$status results=${current.results.size} errors=${current.results.count { it.status == MigrationItemStatusDto.error }}")
|
||||
cs.launch {
|
||||
try {
|
||||
call { finalize(status) }
|
||||
if (selections?.keepLegacySettingsFile == false) {
|
||||
call { cleanup(cleanupTargets()) }
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
LOG.warn("migration finalize failed", e)
|
||||
}
|
||||
@@ -304,8 +321,27 @@ class KiloMigrationService internal constructor(
|
||||
)
|
||||
}
|
||||
|
||||
private fun applyAutocomplete(
|
||||
selections: MigrationUiSelections,
|
||||
detection: ai.kilocode.rpc.dto.LegacyMigrationDetectionDto,
|
||||
) {
|
||||
if (!selections.settings.autocomplete) return
|
||||
val settings = detection.settings?.autocomplete ?: return
|
||||
val apply = autocomplete ?: { KiloAutocompleteSettingsService.getInstance().applyLegacy(it) }
|
||||
apply(settings)
|
||||
}
|
||||
|
||||
private fun selectionSummary(selections: MigrationUiSelections): String =
|
||||
"providers=${selections.providers.size}:${selections.providers.joinToString(",")} mcp=${selections.mcpServers.size}:${selections.mcpServers.joinToString(",")} modes=${selections.customModes.size}:${selections.customModes.joinToString(",")} sessions=${selections.sessions.size} model=${selections.defaultModel} settings=${settingsSummary(selections.settings)}"
|
||||
"providers=${selections.providers.size}:${selections.providers.joinToString(",")} mcp=${selections.mcpServers.size}:${selections.mcpServers.joinToString(",")} modes=${selections.customModes.size}:${selections.customModes.joinToString(",")} sessions=${selections.sessions.size} model=${selections.defaultModel} settings=${settingsSummary(selections.settings)} keepFile=${selections.keepLegacySettingsFile}"
|
||||
|
||||
private fun cleanupTargets() = LegacyCleanupTargetsDto(
|
||||
providerProfiles = true,
|
||||
mcpSettings = true,
|
||||
customModes = true,
|
||||
globalState = true,
|
||||
taskHistory = true,
|
||||
legacySettingsFile = true,
|
||||
)
|
||||
|
||||
private fun settingsSummary(settings: MigrationSettingsUiSelections): String =
|
||||
"commandRules=${settings.autoApproval.commandRules},read=${settings.autoApproval.readPermission},write=${settings.autoApproval.writePermission},execute=${settings.autoApproval.executePermission},mcp=${settings.autoApproval.mcpPermission},task=${settings.autoApproval.taskPermission},language=${settings.language},autocomplete=${settings.autocomplete}"
|
||||
|
||||
+3
@@ -56,6 +56,7 @@ object MigrationSelectionBuilder {
|
||||
sessions = sessions,
|
||||
defaultModel = defaultModel,
|
||||
settings = settingsSel,
|
||||
keepLegacySettingsFile = true,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -83,6 +84,7 @@ object MigrationSelectionBuilder {
|
||||
language = selections.settings.language,
|
||||
autocomplete = selections.settings.autocomplete,
|
||||
),
|
||||
keepLegacySettingsFile = selections.keepLegacySettingsFile,
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -106,5 +108,6 @@ object MigrationSelectionBuilder {
|
||||
language = false,
|
||||
autocomplete = false,
|
||||
),
|
||||
keepLegacySettingsFile = true,
|
||||
)
|
||||
}
|
||||
|
||||
+1
@@ -34,6 +34,7 @@ data class MigrationUiSelections(
|
||||
val sessions: List<String> = emptyList(),
|
||||
val defaultModel: Boolean = false,
|
||||
val settings: MigrationSettingsUiSelections = MigrationSettingsUiSelections(),
|
||||
val keepLegacySettingsFile: Boolean = true,
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<applicationService serviceImplementation="ai.kilocode.client.migration.KiloMigrationService"/>
|
||||
<applicationService serviceImplementation="ai.kilocode.client.autocomplete.KiloAutocompleteSettingsService"/>
|
||||
|
||||
<toolWindow id="Kilo Code"
|
||||
anchor="left"
|
||||
|
||||
+60
-1
@@ -3,10 +3,12 @@ package ai.kilocode.client.migration
|
||||
import ai.kilocode.client.testing.FakeMigrationRpcApi
|
||||
import ai.kilocode.rpc.dto.KiloAppStateDto
|
||||
import ai.kilocode.rpc.dto.KiloAppStatusDto
|
||||
import ai.kilocode.rpc.dto.LegacyAutocompleteSettingsDto
|
||||
import ai.kilocode.rpc.dto.LegacyMigrationDetectionDto
|
||||
import ai.kilocode.rpc.dto.LegacyMigrationEventDto
|
||||
import ai.kilocode.rpc.dto.LegacyMigrationResultItemDto
|
||||
import ai.kilocode.rpc.dto.LegacyMigrationStatusDto
|
||||
import ai.kilocode.rpc.dto.LegacySettingsDto
|
||||
import ai.kilocode.rpc.dto.MigrationItemCategoryDto
|
||||
import ai.kilocode.rpc.dto.MigrationItemProgressStatusDto
|
||||
import ai.kilocode.rpc.dto.MigrationItemStatusDto
|
||||
@@ -27,13 +29,15 @@ class KiloMigrationServiceTest : BasePlatformTestCase() {
|
||||
private lateinit var rpc: FakeMigrationRpcApi
|
||||
private lateinit var service: KiloMigrationService
|
||||
private lateinit var app: MutableStateFlow<KiloAppStateDto>
|
||||
private val autocomplete = mutableListOf<LegacyAutocompleteSettingsDto>()
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
scope = CoroutineScope(SupervisorJob())
|
||||
rpc = FakeMigrationRpcApi()
|
||||
app = MutableStateFlow(KiloAppStateDto(KiloAppStatusDto.DISCONNECTED))
|
||||
service = KiloMigrationService(scope, rpc, app)
|
||||
autocomplete.clear()
|
||||
service = KiloMigrationService(scope, rpc, app) { autocomplete.add(it) }
|
||||
}
|
||||
|
||||
override fun tearDown() {
|
||||
@@ -95,9 +99,30 @@ class KiloMigrationServiceTest : BasePlatformTestCase() {
|
||||
settle()
|
||||
assertEquals(1, rpc.finalizeCalls.size)
|
||||
assertEquals(LegacyMigrationStatusDto.completed, rpc.finalizeCalls[0])
|
||||
assertEquals(0, rpc.cleanupCalls.size)
|
||||
assertEquals(MigrationUiState.Hidden, service.state.value)
|
||||
}
|
||||
|
||||
fun `test finish after unchecked keep file cleans up legacy settings file`() {
|
||||
app.value = KiloAppStateDto(KiloAppStatusDto.MIGRATION_REQUIRED, migration = sampleDetection())
|
||||
settle()
|
||||
service.start(MigrationUiSelections(providers = listOf("profile1"), keepLegacySettingsFile = false))
|
||||
settle()
|
||||
|
||||
service.finish()
|
||||
settle()
|
||||
|
||||
assertEquals(1, rpc.finalizeCalls.size)
|
||||
assertEquals(1, rpc.cleanupCalls.size)
|
||||
val targets = rpc.cleanupCalls[0]
|
||||
assertTrue(targets.providerProfiles)
|
||||
assertTrue(targets.mcpSettings)
|
||||
assertTrue(targets.customModes)
|
||||
assertTrue(targets.globalState)
|
||||
assertTrue(targets.taskHistory)
|
||||
assertTrue(targets.legacySettingsFile)
|
||||
}
|
||||
|
||||
fun `test start emits migrating state and initial pending progress`() = runBlocking {
|
||||
app.value = KiloAppStateDto(KiloAppStatusDto.MIGRATION_REQUIRED, migration = sampleDetection())
|
||||
delay(100)
|
||||
@@ -172,6 +197,40 @@ class KiloMigrationServiceTest : BasePlatformTestCase() {
|
||||
assertEquals(2, dto.sessions.size)
|
||||
assertTrue(dto.sessions.all { it.force })
|
||||
assertEquals(listOf("ses_1", "ses_2"), dto.sessions.map { it.id })
|
||||
assertTrue(dto.keepLegacySettingsFile)
|
||||
}
|
||||
|
||||
fun `test start persists selected legacy autocomplete settings`() {
|
||||
val detection = sampleDetection().copy(
|
||||
settings = LegacySettingsDto(
|
||||
autoApprovalEnabled = null,
|
||||
allowedCommands = null,
|
||||
deniedCommands = null,
|
||||
alwaysAllowReadOnly = null,
|
||||
alwaysAllowReadOnlyOutsideWorkspace = null,
|
||||
alwaysAllowWrite = null,
|
||||
alwaysAllowExecute = null,
|
||||
alwaysAllowMcp = null,
|
||||
alwaysAllowModeSwitch = null,
|
||||
alwaysAllowSubtasks = null,
|
||||
language = null,
|
||||
autocomplete = LegacyAutocompleteSettingsDto(
|
||||
enableAutoTrigger = true,
|
||||
enableSmartInlineTaskKeybinding = false,
|
||||
enableChatAutocomplete = true,
|
||||
),
|
||||
)
|
||||
)
|
||||
app.value = KiloAppStateDto(KiloAppStatusDto.MIGRATION_REQUIRED, migration = detection)
|
||||
settle()
|
||||
|
||||
service.start(MigrationUiSelections(settings = MigrationSettingsUiSelections(autocomplete = true)))
|
||||
settle()
|
||||
|
||||
assertEquals(1, autocomplete.size)
|
||||
assertEquals(true, autocomplete[0].enableAutoTrigger)
|
||||
assertEquals(false, autocomplete[0].enableSmartInlineTaskKeybinding)
|
||||
assertEquals(true, autocomplete[0].enableChatAutocomplete)
|
||||
}
|
||||
|
||||
private fun sampleDetection() = LegacyMigrationDetectionDto(
|
||||
|
||||
@@ -125,6 +125,7 @@ data class LegacyMigrationSelectionsDto(
|
||||
val sessions: List<MigrationSessionSelectionDto>,
|
||||
val defaultModel: Boolean,
|
||||
val settings: MigrationSettingsSelectionsDto,
|
||||
val keepLegacySettingsFile: Boolean = true,
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -201,6 +202,7 @@ data class LegacyCleanupTargetsDto(
|
||||
val customModes: Boolean = false,
|
||||
val globalState: Boolean = false,
|
||||
val taskHistory: Boolean = false,
|
||||
val legacySettingsFile: Boolean = false,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
|
||||
Reference in New Issue
Block a user