mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-28 19:11:03 +08:00
fix(vscode): only clear autocomplete defaults when stored at user scope
config.get() returns the merged effective value, so a workspace-level pin or the schema default would falsely look like a stored global default. Switch to config.inspect().globalValue so the migration only acts on values actually stored in user settings, and update the test stub to model scoped values.
This commit is contained in:
@@ -21,8 +21,12 @@ export async function migrateDefaultAutocompleteSettings(context: vscode.Extensi
|
||||
if (context.globalState.get<boolean>(FLAG)) return
|
||||
|
||||
const config = vscode.workspace.getConfiguration("kilo-code.new.autocomplete")
|
||||
const provider = config.get<string>("provider")
|
||||
const model = config.get<string>("model")
|
||||
// Read the user/global scope specifically. `get()` returns the merged
|
||||
// effective value (workspace > global > default), which would let a
|
||||
// workspace-level pin or the schema default falsely look like a stored
|
||||
// global default and cause us to no-op while still flipping the flag.
|
||||
const provider = config.inspect<string>("provider")?.globalValue
|
||||
const model = config.inspect<string>("model")?.globalValue
|
||||
|
||||
const matchesDefault =
|
||||
provider === DEFAULT_AUTOCOMPLETE_MODEL.providerID && model === DEFAULT_AUTOCOMPLETE_MODEL.modelID
|
||||
|
||||
@@ -3,9 +3,13 @@ import * as vscode from "vscode"
|
||||
import { migrateDefaultAutocompleteSettings } from "../../src/services/autocomplete/migrate-default"
|
||||
import { DEFAULT_AUTOCOMPLETE_MODEL } from "../../src/shared/autocomplete-models"
|
||||
|
||||
type Scoped = { globalValue?: unknown; workspaceValue?: unknown }
|
||||
type State = Map<string, Scoped>
|
||||
|
||||
type Stub = {
|
||||
getConfiguration: (section?: string) => {
|
||||
get: (key: string, fallback?: unknown) => unknown
|
||||
inspect: (key: string) => Scoped | undefined
|
||||
update: (key: string, value: unknown, target: unknown) => Promise<void>
|
||||
}
|
||||
}
|
||||
@@ -27,78 +31,120 @@ function makeContext(initial: Record<string, unknown> = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
function stubConfig(state: Map<string, unknown>) {
|
||||
function stubConfig(state: State) {
|
||||
function entry(key: string): Scoped {
|
||||
const e = state.get(key)
|
||||
if (e) return e
|
||||
const fresh: Scoped = {}
|
||||
state.set(key, fresh)
|
||||
return fresh
|
||||
}
|
||||
;(vscode.workspace as unknown as Stub).getConfiguration = (section?: string) => {
|
||||
if (section !== "kilo-code.new.autocomplete") {
|
||||
return { get: () => undefined, update: async () => {} }
|
||||
return {
|
||||
get: () => undefined,
|
||||
inspect: () => undefined,
|
||||
update: async () => {},
|
||||
}
|
||||
}
|
||||
return {
|
||||
get: (key: string, fallback?: unknown) => state.get(key) ?? fallback,
|
||||
update: async (key: string, value: unknown) => {
|
||||
if (value === undefined) state.delete(key)
|
||||
else state.set(key, value)
|
||||
get: (key: string, fallback?: unknown) => {
|
||||
const e = state.get(key)
|
||||
return e?.workspaceValue ?? e?.globalValue ?? fallback
|
||||
},
|
||||
inspect: (key: string) => state.get(key) ?? {},
|
||||
update: async (key: string, value: unknown, target: unknown) => {
|
||||
// vscode.ConfigurationTarget.Global = 1, Workspace = 2
|
||||
const scope: keyof Scoped = target === 2 ? "workspaceValue" : "globalValue"
|
||||
const e = entry(key)
|
||||
if (value === undefined) delete e[scope]
|
||||
else e[scope] = value
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setGlobal(state: State, key: string, value: unknown) {
|
||||
const e = state.get(key) ?? {}
|
||||
e.globalValue = value
|
||||
state.set(key, e)
|
||||
}
|
||||
|
||||
function setWorkspace(state: State, key: string, value: unknown) {
|
||||
const e = state.get(key) ?? {}
|
||||
e.workspaceValue = value
|
||||
state.set(key, e)
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
;(vscode.workspace as unknown as Stub).getConfiguration = original as Stub["getConfiguration"]
|
||||
})
|
||||
|
||||
describe("migrateDefaultAutocompleteSettings", () => {
|
||||
let state: Map<string, unknown>
|
||||
let state: State
|
||||
|
||||
beforeEach(() => {
|
||||
state = new Map()
|
||||
stubConfig(state)
|
||||
})
|
||||
|
||||
it("clears provider/model when both equal the current default", async () => {
|
||||
state.set("provider", DEFAULT_AUTOCOMPLETE_MODEL.providerID)
|
||||
state.set("model", DEFAULT_AUTOCOMPLETE_MODEL.modelID)
|
||||
it("clears provider/model when both equal the current default at global scope", async () => {
|
||||
setGlobal(state, "provider", DEFAULT_AUTOCOMPLETE_MODEL.providerID)
|
||||
setGlobal(state, "model", DEFAULT_AUTOCOMPLETE_MODEL.modelID)
|
||||
const { context, flag } = makeContext()
|
||||
|
||||
await migrateDefaultAutocompleteSettings(context)
|
||||
|
||||
expect(state.has("provider")).toBe(false)
|
||||
expect(state.has("model")).toBe(false)
|
||||
expect(state.get("provider")?.globalValue).toBeUndefined()
|
||||
expect(state.get("model")?.globalValue).toBeUndefined()
|
||||
expect(flag.get("kilo.autocomplete.defaultClearMigrationV1")).toBe(true)
|
||||
})
|
||||
|
||||
it("leaves an explicitly chosen non-default model untouched", async () => {
|
||||
state.set("provider", "inception")
|
||||
state.set("model", "mercury-edit-2")
|
||||
setGlobal(state, "provider", "inception")
|
||||
setGlobal(state, "model", "mercury-edit-2")
|
||||
const { context, flag } = makeContext()
|
||||
|
||||
await migrateDefaultAutocompleteSettings(context)
|
||||
|
||||
expect(state.get("provider")).toBe("inception")
|
||||
expect(state.get("model")).toBe("mercury-edit-2")
|
||||
expect(state.get("provider")?.globalValue).toBe("inception")
|
||||
expect(state.get("model")?.globalValue).toBe("mercury-edit-2")
|
||||
expect(flag.get("kilo.autocomplete.defaultClearMigrationV1")).toBe(true)
|
||||
})
|
||||
|
||||
it("leaves a partial match untouched", async () => {
|
||||
state.set("provider", DEFAULT_AUTOCOMPLETE_MODEL.providerID)
|
||||
state.set("model", "inception/mercury-edit-2")
|
||||
setGlobal(state, "provider", DEFAULT_AUTOCOMPLETE_MODEL.providerID)
|
||||
setGlobal(state, "model", "inception/mercury-edit-2")
|
||||
const { context } = makeContext()
|
||||
|
||||
await migrateDefaultAutocompleteSettings(context)
|
||||
|
||||
expect(state.get("provider")).toBe(DEFAULT_AUTOCOMPLETE_MODEL.providerID)
|
||||
expect(state.get("model")).toBe("inception/mercury-edit-2")
|
||||
expect(state.get("provider")?.globalValue).toBe(DEFAULT_AUTOCOMPLETE_MODEL.providerID)
|
||||
expect(state.get("model")?.globalValue).toBe("inception/mercury-edit-2")
|
||||
})
|
||||
|
||||
it("ignores workspace-scoped pins so they aren't mistaken for global defaults", async () => {
|
||||
setWorkspace(state, "provider", DEFAULT_AUTOCOMPLETE_MODEL.providerID)
|
||||
setWorkspace(state, "model", DEFAULT_AUTOCOMPLETE_MODEL.modelID)
|
||||
const { context } = makeContext()
|
||||
|
||||
await migrateDefaultAutocompleteSettings(context)
|
||||
|
||||
// Workspace value is intact — we only clear at global scope.
|
||||
expect(state.get("provider")?.workspaceValue).toBe(DEFAULT_AUTOCOMPLETE_MODEL.providerID)
|
||||
expect(state.get("model")?.workspaceValue).toBe(DEFAULT_AUTOCOMPLETE_MODEL.modelID)
|
||||
})
|
||||
|
||||
it("only runs once per machine", async () => {
|
||||
state.set("provider", DEFAULT_AUTOCOMPLETE_MODEL.providerID)
|
||||
state.set("model", DEFAULT_AUTOCOMPLETE_MODEL.modelID)
|
||||
setGlobal(state, "provider", DEFAULT_AUTOCOMPLETE_MODEL.providerID)
|
||||
setGlobal(state, "model", DEFAULT_AUTOCOMPLETE_MODEL.modelID)
|
||||
const { context } = makeContext({ "kilo.autocomplete.defaultClearMigrationV1": true })
|
||||
|
||||
await migrateDefaultAutocompleteSettings(context)
|
||||
|
||||
// Setting was preserved — second run is a no-op.
|
||||
expect(state.get("provider")).toBe(DEFAULT_AUTOCOMPLETE_MODEL.providerID)
|
||||
expect(state.get("model")).toBe(DEFAULT_AUTOCOMPLETE_MODEL.modelID)
|
||||
expect(state.get("provider")?.globalValue).toBe(DEFAULT_AUTOCOMPLETE_MODEL.providerID)
|
||||
expect(state.get("model")?.globalValue).toBe(DEFAULT_AUTOCOMPLETE_MODEL.modelID)
|
||||
})
|
||||
|
||||
it("sets the flag even when nothing needed clearing", async () => {
|
||||
|
||||
Reference in New Issue
Block a user