diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index 717d98fc4c6..451b706c615 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -1270,6 +1270,14 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper } } + private async fetchAndSendMarketplaceData(): Promise { + const workspace = this.getProjectDirectory(this.currentSession?.id) + const mp = this.getMarketplace() + const skills = await this.fetchCliSkills() + const data = await mp.fetchData(workspace, skills) + this.postMessage({ type: "marketplaceData", ...data }) + } + private async fetchCliSkills(): Promise | undefined> { if (!this.client) return undefined try { @@ -1333,38 +1341,14 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper const stub = { id: name, type: "mode" as const, name, description: "", content: "" } const project = await mp.remove(stub, "project", workspace) const global = await mp.remove(stub, "global", workspace) - if (project.success || global.success) { - await this.disposeCliInstance("global") - removed = true - } + if (project.success || global.success) removed = true } if (!removed) { console.error("[Kilo New] KiloProvider: Failed to remove mode:", name) } - this.cachedAgentsMessage = null - await this.fetchAndSendAgents() - } - - /** - * Dispose the CLI backend instance so it re-reads config from disk. - * Call after any marketplace install/remove that writes config files directly. - * Global-scope changes need global.dispose() to also reset the global config cache. - */ - private async disposeCliInstance(scope: "project" | "global"): Promise { - if (!this.client) return - if (scope === "global") { - await this.client.global.dispose().catch((e: unknown) => { - console.warn("[Kilo New] global.dispose() after marketplace change failed:", e) - }) - } - // Always dispose the per-project instance so it rebuilds state from - // the (possibly updated) global + project config on the next request. - const dir = this.getWorkspaceDirectory() - await this.client.instance.dispose({ directory: dir }).catch((e: unknown) => { - console.warn("[Kilo New] instance.dispose() after marketplace change failed:", e) - }) + await this.invalidateAfterMarketplaceChange("global") } /** @@ -1401,7 +1385,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper }) this.cachedAgentsMessage = null this.cachedConfigMessage = null - await Promise.all([this.fetchAndSendAgents(), this.fetchAndSendConfig()]) + await Promise.all([this.fetchAndSendAgents(), this.fetchAndSendConfig(), this.fetchAndSendMarketplaceData()]) } /** diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 2ffc1e5e958..3203886c12b 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -24,6 +24,7 @@ import { } from "jsonc-parser" // kilocode_change end import { Instance } from "../project/instance" +import { State } from "../project/state" // kilocode_change import { LSPServer } from "../lsp/server" import { BunProc } from "@/bun" import { Installation } from "@/installation" @@ -1589,6 +1590,13 @@ export namespace Config { global.reset() + // kilocode_change start - reset all derived caches (Config.state, Agent.state, + // Provider.state, etc.) so they re-read the updated global config on next access. + // Only cache entries without dispose callbacks are cleared — side-effectful state + // like sessions, MCP connections, and file watchers are preserved. + State.resetCaches() + // kilocode_change end + GlobalBus.emit("event", { directory: "global", payload: { diff --git a/packages/opencode/src/project/state.ts b/packages/opencode/src/project/state.ts index a9dce565b5e..dda791952a5 100644 --- a/packages/opencode/src/project/state.ts +++ b/packages/opencode/src/project/state.ts @@ -28,6 +28,21 @@ export namespace State { } } + /** + * Reset all cache-only entries (those without dispose callbacks) across all + * instances. This invalidates derived state like Config, Agent, Provider, + * etc. without tearing down side-effectful resources (MCP connections, + * sessions, file watchers, etc.). + */ + export function resetCaches() { + for (const entries of recordsByKey.values()) { + for (const [init, entry] of entries) { + if (entry.dispose) continue + entries.delete(init) + } + } + } + export async function dispose(key: string) { const entries = recordsByKey.get(key) if (!entries) return