diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index c3f8e66507c..f56e8c3cb7d 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -732,10 +732,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper const scope = message.mpInstallOptions?.target ?? "project" const result = await this.getMarketplace().install(message.mpItem, message.mpInstallOptions, workspace) if (result.success) { - await this.disposeCliInstance(scope) - this.cachedAgentsMessage = null - this.cachedConfigMessage = null - await Promise.all([this.fetchAndSendAgents(), this.fetchAndSendConfig()]) + await this.invalidateAfterMarketplaceChange(scope) } this.postMessage({ type: "marketplaceInstallResult", @@ -750,10 +747,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper const scope = message.mpInstallOptions?.target ?? "project" const result = await this.getMarketplace().remove(message.mpItem, scope, workspace) if (result.success) { - await this.disposeCliInstance(scope) - this.cachedAgentsMessage = null - this.cachedConfigMessage = null - await Promise.all([this.fetchAndSendAgents(), this.fetchAndSendConfig()]) + await this.invalidateAfterMarketplaceChange(scope) } this.postMessage({ type: "marketplaceRemoveResult", @@ -1373,6 +1367,43 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper }) } + /** + * Invalidate CLI caches and refresh the webview after a marketplace install/remove. + * + * For global scope: uses global.config.update with the freshly-written config file + * contents rather than global.dispose. This goes through Config.updateGlobal() which + * calls Config.global.reset() to invalidate the lazy-cached global config, ensuring + * the newly installed/removed MCP entry is visible on the next config.get call. + * (global.dispose alone is not sufficient on older CLI versions that lack the + * Config.global.reset() call in the dispose handler.) + * + * For project scope: instance.dispose is sufficient because the per-instance + * Config.state is cleared and re-reads all files (including global) on next access. + */ + private async invalidateAfterMarketplaceChange(scope: "project" | "global"): Promise { + if (!this.client) return + if (scope === "global") { + // Use global.config.update with an empty config to trigger Config.updateGlobal() + // which calls Config.global.reset(). This invalidates the lazy-cached global + // config in the CLI process so it re-reads kilo.json from disk. + // An empty object merge is a no-op for the file content but resets the cache. + // (global.dispose alone is insufficient on older CLI versions that lack + // the Config.global.reset() call in the dispose handler.) + await this.client.global.config.update({ config: {} }).catch((e: unknown) => { + console.warn("[Kilo New] global.config.update 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) + }) + this.cachedAgentsMessage = null + this.cachedConfigMessage = null + await Promise.all([this.fetchAndSendAgents(), this.fetchAndSendConfig()]) + } + /** * Fetch backend config and send to webview. */