diff --git a/.changeset/indexing-global-project-toggles.md b/.changeset/indexing-global-project-toggles.md new file mode 100644 index 00000000000..d304ed89008 --- /dev/null +++ b/.changeset/indexing-global-project-toggles.md @@ -0,0 +1,6 @@ +--- +"@kilocode/cli": patch +"kilo-code": patch +--- + +Support global and per-project codebase indexing enablement. diff --git a/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/settings/indexing-provider-blur-race-chromium-linux.png b/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/settings/indexing-provider-blur-race-chromium-linux.png index 2283986b267..18c542093c0 100644 --- a/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/settings/indexing-provider-blur-race-chromium-linux.png +++ b/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/settings/indexing-provider-blur-race-chromium-linux.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f2130db1ea14a5e8fbf879e96529198592a785b8460d29e12746175e102d50d5 -size 53617 +oid sha256:a67058f86869ec7457058feec03f880b7a3ac31c00b87ae6db5a55d977295856 +size 57421 diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index ca77476aa76..844b1a9e734 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -170,6 +170,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper private cachedCommandsMessage: unknown = null /** Cached configLoaded payload so requestConfig can be served before client is ready */ private cachedConfigMessage: unknown = null + private cachedGlobalConfig: Config | null = null /** Cached indexingStatusLoaded payload so requestIndexingStatus can be served before client is ready */ private cachedIndexingStatusMessage: unknown = null /** Cached mcpStatusLoaded payload so requestMcpStatus can be served before client is ready */ @@ -1715,6 +1716,8 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper ) const set = (m: unknown) => { this.cachedConfigMessage = m + if (m && typeof m === "object" && "globalConfig" in m) + this.cachedGlobalConfig = (m as { globalConfig?: Config }).globalConfig ?? null } const method = typeof msg.method === "number" ? msg.method : 0 const key = typeof msg.apiKey === "string" ? msg.apiKey : undefined @@ -2068,10 +2071,13 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper const { data: config } = await retry(() => this.client!.config.get({ directory: workspaceDir }, { throwOnError: true }), ) + const { data: global } = await this.client.global.config.get({ throwOnError: true }) + this.cachedGlobalConfig = global ?? null const message = { type: "configLoaded", config, + globalConfig: global, features: configFeatures(config), } this.cachedConfigMessage = message @@ -2086,6 +2092,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper if (!this.client || this.connectionState !== "connected") return try { const { data: config } = await this.client.global.config.get({ throwOnError: true }) + this.cachedGlobalConfig = config ?? null this.postMessage({ type: "globalConfigLoaded", config }) } catch (error) { console.error("[Kilo New] KiloProvider: Failed to fetch global config:", error) @@ -2149,8 +2156,15 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper try { const dir = this.getWorkspaceDirectory() const { data: config } = await retry(() => this.client!.config.get({ directory: dir }, { throwOnError: true })) - this.cachedConfigMessage = { type: "configLoaded", config, features: configFeatures(config) } - this.postMessage({ type: "configUpdated", config, features: configFeatures(config) }) + const { data: global } = await this.client.global.config.get({ throwOnError: true }) + this.cachedGlobalConfig = global ?? null + this.cachedConfigMessage = { + type: "configLoaded", + config, + globalConfig: global, + features: configFeatures(config), + } + this.postMessage({ type: "configUpdated", config, globalConfig: global, features: configFeatures(config) }) } catch (error) { console.error("[Kilo New] KiloProvider: Failed to fetch config after update:", error) } @@ -2334,8 +2348,20 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper try { const { data: merged } = await retry(() => this.client!.config.get({ directory: dir }, { throwOnError: true })) - this.cachedConfigMessage = { type: "configLoaded", config: merged, features: configFeatures(merged) } - this.postMessage({ type: "configUpdated", config: merged, features: configFeatures(merged) }) + const { data: global } = await this.client.global.config.get({ throwOnError: true }) + this.cachedGlobalConfig = global ?? null + this.cachedConfigMessage = { + type: "configLoaded", + config: merged, + globalConfig: global, + features: configFeatures(merged), + } + this.postMessage({ + type: "configUpdated", + config: merged, + globalConfig: global, + features: configFeatures(merged), + }) if (refreshProviders) await this.fetchAndSendProviders() } catch (error) { console.error("[Kilo New] KiloProvider: Config write succeeded but post-write refresh failed:", error) @@ -2350,6 +2376,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper this.postMessage({ type: "configUpdated", config: optimistic, + globalConfig: this.cachedGlobalConfig ?? undefined, features: features ?? configFeatures(optimistic as Config), }) } finally { diff --git a/packages/kilo-vscode/src/provider-actions.ts b/packages/kilo-vscode/src/provider-actions.ts index 3d8e4089224..f95e3ba8609 100644 --- a/packages/kilo-vscode/src/provider-actions.ts +++ b/packages/kilo-vscode/src/provider-actions.ts @@ -187,11 +187,14 @@ async function configs(ctx: ActionContext) { } async function refreshConfig(ctx: ActionContext, setCachedConfig: SetCachedConfig) { - const { data: config } = await ctx.client.config.get({ directory: ctx.workspaceDir }, { throwOnError: true }) + const [{ data: config }, { data: global }] = await Promise.all([ + ctx.client.config.get({ directory: ctx.workspaceDir }, { throwOnError: true }), + ctx.client.global.config.get({ throwOnError: true }), + ]) if (!config) return const features = configFeatures(config) - setCachedConfig({ type: "configLoaded", config, features }) - ctx.postMessage({ type: "configUpdated", config, features }) + setCachedConfig({ type: "configLoaded", config, globalConfig: global, features }) + ctx.postMessage({ type: "configUpdated", config, globalConfig: global, features }) } async function saveGlobal(ctx: ActionContext, config: Config) { @@ -403,9 +406,11 @@ export async function saveCustomProvider( { throwOnError: true }, ) - const msg = { type: "configLoaded", config: updated, features: configFeatures(updated) } + const merged = await ctx.client.config.get({ directory: ctx.workspaceDir }, { throwOnError: true }) + const config = merged.data ?? updated + const msg = { type: "configLoaded", config, globalConfig: updated, features: configFeatures(config) } setCachedConfig(msg) - ctx.postMessage({ type: "configUpdated", config: updated, features: configFeatures(updated) }) + ctx.postMessage({ type: "configUpdated", config, globalConfig: updated, features: configFeatures(config) }) const auth = resolveCustomProviderAuth(apiKey, apiKeyChanged) diff --git a/packages/kilo-vscode/tests/unit/config-scope.test.ts b/packages/kilo-vscode/tests/unit/config-scope.test.ts index 8c353eb2b1a..16b433009b5 100644 --- a/packages/kilo-vscode/tests/unit/config-scope.test.ts +++ b/packages/kilo-vscode/tests/unit/config-scope.test.ts @@ -13,4 +13,23 @@ describe("splitConfigByScope", () => { expect(split.global).toEqual({ indexing: { provider: "ollama" } }) expect(split.project).toEqual({ indexing: { enabled: true } }) }) + + it("writes indexing provider settings to global config", () => { + const split = splitConfigByScope({ + indexing: { + provider: "ollama", + }, + }) + + expect(split.global).toEqual({ indexing: { provider: "ollama" } }) + expect(split.project).toEqual({}) + }) + + it("can write indexing enablement to global config through a global draft", () => { + const split = splitConfigByScope({ username: "marius" }) + const draft = { indexing: { enabled: true } } + + expect({ ...split.global, ...draft }).toEqual({ username: "marius", indexing: { enabled: true } }) + expect(split.project).toEqual({}) + }) }) diff --git a/packages/kilo-vscode/tests/unit/kilo-provider-indexing-refresh.test.ts b/packages/kilo-vscode/tests/unit/kilo-provider-indexing-refresh.test.ts index c1f8386e2bc..a8b1981daa4 100644 --- a/packages/kilo-vscode/tests/unit/kilo-provider-indexing-refresh.test.ts +++ b/packages/kilo-vscode/tests/unit/kilo-provider-indexing-refresh.test.ts @@ -23,11 +23,13 @@ function createConnection() { const client = { global: { config: { + get: async () => ({ data: {} }), update: async () => ({ data: {} }), }, }, config: { get: async () => ({ data: {} }), + update: async () => ({ data: {} }), }, } diff --git a/packages/kilo-vscode/webview-ui/src/components/settings/AboutKiloCodeTab.tsx b/packages/kilo-vscode/webview-ui/src/components/settings/AboutKiloCodeTab.tsx index 4c33f7be326..60fa97e37e0 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/AboutKiloCodeTab.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/settings/AboutKiloCodeTab.tsx @@ -5,7 +5,7 @@ import { showToast } from "@kilocode/kilo-ui/toast" import { useLanguage } from "../../context/language" import { useVSCode } from "../../context/vscode" import { useConfig } from "../../context/config" -import type { ConnectionState, ExtensionMessage } from "../../types/messages" +import type { Config, ConnectionState, ExtensionMessage } from "../../types/messages" import { buildExport, parseImport, MAX_IMPORT_SIZE } from "./settings-io" export interface AboutKiloCodeTabProps { @@ -18,7 +18,7 @@ export interface AboutKiloCodeTabProps { const AboutKiloCodeTab: Component = (props) => { const language = useLanguage() const vscode = useVSCode() - const { updateConfig } = useConfig() + const { updateConfig, updateGlobalConfig } = useConfig() const [importing, setImporting] = createSignal(false) const [exporting, setExporting] = createSignal(false) let epoch = 0 @@ -27,6 +27,23 @@ const AboutKiloCodeTab: Component = (props) => { vscode.postMessage({ type: "openExternal", url }) } + const importConfig = (config: Config) => { + const enabled = config.indexing?.enabled + if (enabled === undefined) { + updateConfig(config) + return + } + + const indexing = { ...config.indexing } + delete indexing.enabled + const next = { ...config } + if (Object.keys(indexing).length > 0) next.indexing = indexing + else delete next.indexing + + updateConfig(next) + updateGlobalConfig({ indexing: { enabled } }) + } + // Listen for globalConfigLoaded response const handler = (event: MessageEvent) => { const msg = event.data as ExtensionMessage @@ -91,7 +108,7 @@ const AboutKiloCodeTab: Component = (props) => { title: language.t("settings.aboutKiloCode.importSettings.newerVersion"), }) } - updateConfig(result.config) + importConfig(result.config) showToast({ variant: "success", title: language.t("settings.aboutKiloCode.importSettings.success"), diff --git a/packages/kilo-vscode/webview-ui/src/components/settings/IndexingTab.tsx b/packages/kilo-vscode/webview-ui/src/components/settings/IndexingTab.tsx index 2846bec0dd5..8dece26a03d 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/IndexingTab.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/settings/IndexingTab.tsx @@ -3,6 +3,7 @@ import { Card } from "@kilocode/kilo-ui/card" import { Select } from "@kilocode/kilo-ui/select" import { Switch } from "@kilocode/kilo-ui/switch" import { TextField } from "@kilocode/kilo-ui/text-field" +import { Tooltip } from "@kilocode/kilo-ui/tooltip" import { useConfig } from "../../context/config" import { formatIndexingLabel, useIndexing } from "../../context/indexing" import { useLanguage } from "../../context/language" @@ -65,7 +66,7 @@ function providerFields(provider: ProviderId | undefined): Array<{ key: string; } const IndexingTab: Component = () => { - const { config, updateConfig } = useConfig() + const { config, globalConfig, updateConfig, updateGlobalConfig } = useConfig() const indexing = useIndexing() const language = useLanguage() const [providerDrafts, setProviderDrafts] = createSignal>({}) @@ -73,6 +74,8 @@ const IndexingTab: Component = () => { const [tuningDrafts, setTuningDrafts] = createSignal>({}) const cfg = createMemo(() => config().indexing ?? {}) + const globalCfg = createMemo(() => globalConfig().indexing ?? {}) + const globalOn = createMemo(() => globalCfg().enabled === true) const updateIndexing = (partial: IndexingConfig) => { updateConfig({ indexing: { ...cfg(), ...partial } }) @@ -142,18 +145,37 @@ const IndexingTab: Component = () => { updateIndexing({ enabled: checked })} + checked={globalCfg().enabled ?? false} + onChange={(checked) => updateGlobalConfig({ indexing: { enabled: checked } })} hideLabel > - {language.t("settings.indexing.enable.title")} + {language.t("settings.indexing.globalEnable.title")} + + + updateIndexing({ enabled: checked })} + disabled={globalOn()} + hideLabel + > + {language.t("settings.indexing.projectEnable.title")} + + + diff --git a/packages/kilo-vscode/webview-ui/src/context/config.tsx b/packages/kilo-vscode/webview-ui/src/context/config.tsx index 4d354469978..c2eeaa0214b 100644 --- a/packages/kilo-vscode/webview-ui/src/context/config.tsx +++ b/packages/kilo-vscode/webview-ui/src/context/config.tsx @@ -26,6 +26,7 @@ export interface SaveError { interface ConfigContextValue { config: Accessor + globalConfig: Accessor settings: Accessor> features: Accessor loading: Accessor @@ -33,6 +34,7 @@ interface ConfigContextValue { saving: Accessor saveError: Accessor updateConfig: (partial: Partial) => void + updateGlobalConfig: (partial: Partial) => void updateSetting: (key: string, value: unknown) => void saveConfig: () => void discardConfig: () => void @@ -44,14 +46,20 @@ export const ConfigProvider: ParentComponent = (props) => { const vscode = useVSCode() const [config, setConfig] = createSignal({}) + const [globalConfig, setGlobalConfig] = createSignal({}) const [settings, setSettings] = createSignal>({}) const [features, setFeatures] = createSignal({ indexing: false }) const [loading, setLoading] = createSignal(true) const [draft, setDraft] = createSignal>({}) + const [globalDraft, setGlobalDraft] = createSignal>({}) const [settingsDraft, setSettingsDraft] = createSignal>({}) - const isDirty = createMemo(() => has(draft() as Record) || has(settingsDraft())) + const isDirty = createMemo( + () => + has(draft() as Record) || has(globalDraft() as Record) || has(settingsDraft()), + ) // Last config received from the server — used to revert on discard const [saved, setSaved] = createSignal({}) + const [savedGlobal, setSavedGlobal] = createSignal({}) const [savedSettings, setSavedSettings] = createSignal>({}) // True while a saveConfig() write is in-flight — used to clear draft on success // and to guard against stale configLoaded messages overwriting optimistic state. @@ -83,22 +91,41 @@ export const ConfigProvider: ParentComponent = (props) => { setConfig(resolveConfig(message.config, draft(), has(draft() as Record))) setFeatures(message.features) setSaved(message.config) + if (message.globalConfig !== undefined) { + setGlobalConfig(stripNulls(deepMerge(message.globalConfig, globalDraft()))) + setSavedGlobal(message.globalConfig) + } setLoading(false) return } + if (message.type === "globalConfigLoaded") { + if (saving()) return + setGlobalConfig(stripNulls(deepMerge(message.config, globalDraft()))) + setSavedGlobal(message.config) + return + } if (message.type === "configUpdated") { if (saving()) { // This configUpdated is the confirmation of our saveConfig() write. // Clear the draft now that the server has confirmed the write. setSaving(false) setDraft({}) + setGlobalDraft({}) setSaveError(null) setConfig(message.config) + if (message.globalConfig !== undefined) { + setGlobalConfig(stripNulls(deepMerge(message.globalConfig, globalDraft()))) + setSavedGlobal(message.globalConfig) + } setFeatures(message.features) } else { // configUpdated from a different source (e.g. PermissionDock save). // Re-apply the draft on top so pending settings changes are preserved. setConfig(resolveConfig(message.config, draft(), has(draft() as Record))) + if (message.globalConfig !== undefined) { + setGlobalConfig(stripNulls(deepMerge(message.globalConfig, globalDraft()))) + setSavedGlobal(message.globalConfig) + } setFeatures(message.features) } setSaved(message.config) @@ -152,6 +179,12 @@ export const ConfigProvider: ParentComponent = (props) => { setSaveError(null) } + function updateGlobalConfig(partial: Partial) { + setGlobalConfig((prev) => stripNulls(deepMerge(prev, partial))) + setGlobalDraft((prev) => deepMerge(prev as Config, partial)) + setSaveError(null) + } + function updateSetting(key: string, value: unknown) { setSettings((prev) => ({ ...prev, [key]: value })) setSettingsDraft((prev) => ({ ...prev, [key]: value })) @@ -160,10 +193,12 @@ export const ConfigProvider: ParentComponent = (props) => { function saveConfig() { const changes = draft() + const globals = globalDraft() const pending = settingsDraft() const configDirty = has(changes as Record) + const globalDirty = has(globals as Record) const settingsDirty = has(pending) - if (!configDirty && !settingsDirty) return + if (!configDirty && !globalDirty && !settingsDirty) return // Don't clear draft/isDirty yet — wait for configUpdated confirmation. // If the write fails, the save bar stays visible so the user can retry. setSaving(true) @@ -175,7 +210,7 @@ export const ConfigProvider: ParentComponent = (props) => { setSavedSettings((prev) => ({ ...prev, ...pending })) setSettingsDraft({}) } - if (!configDirty) { + if (!configDirty && !globalDirty) { setSaving(false) return } @@ -183,12 +218,15 @@ export const ConfigProvider: ParentComponent = (props) => { // workspace's kilo.json instead of the global one. Send one message so the // extension confirms only after both scopes are saved. const split = splitConfigByScope(changes) - vscode.postMessage({ type: "updateConfig", config: split.global, projectConfig: split.project }) + const next = deepMerge(split.global as Config, globals) + vscode.postMessage({ type: "updateConfig", config: next, projectConfig: split.project }) } function discardConfig() { setConfig(saved()) + setGlobalConfig(savedGlobal()) setDraft({}) + setGlobalDraft({}) setSettings(savedSettings()) setSettingsDraft({}) setSaveError(null) @@ -196,6 +234,7 @@ export const ConfigProvider: ParentComponent = (props) => { const value: ConfigContextValue = { config, + globalConfig, settings, features, loading, @@ -203,6 +242,7 @@ export const ConfigProvider: ParentComponent = (props) => { saving, saveError, updateConfig, + updateGlobalConfig, updateSetting, saveConfig, discardConfig, diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ar.ts b/packages/kilo-vscode/webview-ui/src/i18n/ar.ts index 3a39e5fa4cb..e474755bb21 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ar.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ar.ts @@ -1026,6 +1026,11 @@ export const dict = { "settings.indexing.title": "الفهرسة", "settings.indexing.enable.title": "تمكين الفهرسة", "settings.indexing.enable.description": "تشغيل أو إيقاف فهرسة قاعدة الكود الدلالية.", + "settings.indexing.globalEnable.title": "تمكين عام", + "settings.indexing.globalEnable.description": "تمكين الفهرسة لكل مساحة عمل.", + "settings.indexing.projectEnable.title": "تمكين لهذا المشروع", + "settings.indexing.projectEnable.description": "تمكين الفهرسة لمساحة العمل هذه عندما تكون الفهرسة العامة متوقفة.", + "settings.indexing.projectEnable.disabledTooltip": "الفهرسة العامة مفعلة، لذا هذا المشروع مشمول بالفعل.", "settings.indexing.provider.title": "موفر التضمين", "settings.indexing.provider.description": "اختر الموفر المستخدم لإنشاء التضمينات للبحث الدلالي.", "settings.indexing.model.title": "نموذج التضمين", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/br.ts b/packages/kilo-vscode/webview-ui/src/i18n/br.ts index 35f64e96ae6..63793141d73 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/br.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/br.ts @@ -1045,6 +1045,13 @@ export const dict = { "settings.indexing.title": "Indexação", "settings.indexing.enable.title": "Ativar indexação", "settings.indexing.enable.description": "Ativar ou desativar a indexação semântica da base de código.", + "settings.indexing.globalEnable.title": "Habilitar globalmente", + "settings.indexing.globalEnable.description": "Habilitar indexação para todos os workspaces.", + "settings.indexing.projectEnable.title": "Habilitar para este projeto", + "settings.indexing.projectEnable.description": + "Habilitar indexação para este workspace quando a indexação global estiver desativada.", + "settings.indexing.projectEnable.disabledTooltip": + "A indexação global está ativada, então este projeto já está coberto.", "settings.indexing.provider.title": "Provedor de embedding", "settings.indexing.provider.description": "Escolha o provedor usado para gerar embeddings para busca semântica.", "settings.indexing.model.title": "Modelo de embedding", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/bs.ts b/packages/kilo-vscode/webview-ui/src/i18n/bs.ts index b50ac60e5b4..1fe3bcdea6d 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/bs.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/bs.ts @@ -759,6 +759,13 @@ export const dict = { "settings.indexing.dimension.title": "Dimenzija vektora", "settings.indexing.enable.description": "Uključite ili isključite semantičko indeksiranje baze koda.", "settings.indexing.enable.title": "Omogući indeksiranje", + "settings.indexing.globalEnable.title": "Omogući globalno", + "settings.indexing.globalEnable.description": "Omogući indeksiranje za svaki radni prostor.", + "settings.indexing.projectEnable.title": "Omogući za ovaj projekat", + "settings.indexing.projectEnable.description": + "Omogući indeksiranje za ovaj radni prostor kada je globalno indeksiranje isključeno.", + "settings.indexing.projectEnable.disabledTooltip": + "Globalno indeksiranje je omogućeno, tako da je ovaj projekat već obuhvaćen.", "settings.indexing.lancedbDirectory.description": "Opcionalni direktorij za lokalno LanceDB skladište.", "settings.indexing.lancedbDirectory.placeholder": "Ostavite prazno za zadano", "settings.indexing.lancedbDirectory.title": "LanceDB direktorij", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/da.ts b/packages/kilo-vscode/webview-ui/src/i18n/da.ts index 094edf0114b..a1a699174b4 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/da.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/da.ts @@ -768,6 +768,13 @@ export const dict = { "settings.indexing.title": "Indeksering", "settings.indexing.enable.title": "Aktivér indeksering", "settings.indexing.enable.description": "Slå semantisk kodebase-indeksering til eller fra.", + "settings.indexing.globalEnable.title": "Aktivér globalt", + "settings.indexing.globalEnable.description": "Aktivér indeksering for alle arbejdsområder.", + "settings.indexing.projectEnable.title": "Aktivér for dette projekt", + "settings.indexing.projectEnable.description": + "Aktivér indeksering for dette arbejdsområde, når global indeksering er slået fra.", + "settings.indexing.projectEnable.disabledTooltip": + "Global indeksering er aktiveret, så dette projekt er allerede dækket.", "settings.indexing.provider.title": "Embedding-udbyder", "settings.indexing.provider.description": "Vælg udbyderen, der bruges til at generere embeddings til semantisk søgning.", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/de.ts b/packages/kilo-vscode/webview-ui/src/i18n/de.ts index 50cd5959cc0..6cfd211a00a 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/de.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/de.ts @@ -765,6 +765,13 @@ export const dict = { "settings.indexing.dimension.title": "Vektordimension", "settings.indexing.enable.description": "Semantische Codebasis-Indizierung ein- oder ausschalten.", "settings.indexing.enable.title": "Indizierung aktivieren", + "settings.indexing.globalEnable.title": "Global aktivieren", + "settings.indexing.globalEnable.description": "Indizierung für jeden Workspace aktivieren.", + "settings.indexing.projectEnable.title": "Für dieses Projekt aktivieren", + "settings.indexing.projectEnable.description": + "Indizierung für diesen Workspace aktivieren, wenn die globale Indizierung deaktiviert ist.", + "settings.indexing.projectEnable.disabledTooltip": + "Die globale Indizierung ist aktiviert, dieses Projekt wird also bereits erfasst.", "settings.indexing.lancedbDirectory.description": "Optionaler Ordner für den lokalen LanceDB-Speicher.", "settings.indexing.lancedbDirectory.placeholder": "Leer lassen für Standard", "settings.indexing.lancedbDirectory.title": "LanceDB-Ordner", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/en.ts b/packages/kilo-vscode/webview-ui/src/i18n/en.ts index 1f83fbe97d3..274127fb275 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/en.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/en.ts @@ -1004,6 +1004,11 @@ export const dict = { "settings.indexing.status.title": "Status", "settings.indexing.enable.title": "Enable indexing", "settings.indexing.enable.description": "Turn semantic codebase indexing on or off.", + "settings.indexing.globalEnable.title": "Enable globally", + "settings.indexing.globalEnable.description": "Enable indexing for every workspace.", + "settings.indexing.projectEnable.title": "Enable for this project", + "settings.indexing.projectEnable.description": "Enable indexing for this workspace when global indexing is off.", + "settings.indexing.projectEnable.disabledTooltip": "Global indexing is enabled, so this project is already covered.", "settings.indexing.provider.title": "Embedding provider", "settings.indexing.provider.description": "Choose the provider used to generate embeddings for semantic search.", "settings.indexing.model.title": "Embedding model", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/es.ts b/packages/kilo-vscode/webview-ui/src/i18n/es.ts index 54e93d50567..70fa3e20764 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/es.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/es.ts @@ -755,6 +755,13 @@ export const dict = { "settings.indexing.title": "Indexación", "settings.indexing.enable.title": "Habilitar indexación", "settings.indexing.enable.description": "Activar o desactivar la indexación semántica de la base de código.", + "settings.indexing.globalEnable.title": "Habilitar globalmente", + "settings.indexing.globalEnable.description": "Habilitar la indexación para cada área de trabajo.", + "settings.indexing.projectEnable.title": "Habilitar para este proyecto", + "settings.indexing.projectEnable.description": + "Habilitar la indexación para esta área de trabajo cuando la indexación global está desactivada.", + "settings.indexing.projectEnable.disabledTooltip": + "La indexación global está habilitada, por lo que este proyecto ya está cubierto.", "settings.indexing.provider.title": "Proveedor de embeddings", "settings.indexing.provider.description": "Elige el proveedor utilizado para generar embeddings para búsqueda semántica.", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/fr.ts b/packages/kilo-vscode/webview-ui/src/i18n/fr.ts index d3135871015..68a704affcc 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/fr.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/fr.ts @@ -684,6 +684,13 @@ export const dict = { "settings.indexing.dimension.title": "Dimension vectorielle", "settings.indexing.enable.description": "Activer ou désactiver l'indexation sémantique de la base de code.", "settings.indexing.enable.title": "Activer l'indexation", + "settings.indexing.globalEnable.title": "Activer globalement", + "settings.indexing.globalEnable.description": "Activer l'indexation pour chaque espace de travail.", + "settings.indexing.projectEnable.title": "Activer pour ce projet", + "settings.indexing.projectEnable.description": + "Activer l'indexation pour cet espace de travail lorsque l'indexation globale est désactivée.", + "settings.indexing.projectEnable.disabledTooltip": + "L'indexation globale est activée, ce projet est donc déjà pris en compte.", "settings.indexing.lancedbDirectory.description": "Répertoire optionnel pour le stockage local LanceDB.", "settings.indexing.lancedbDirectory.placeholder": "Laissez vide pour la valeur par défaut", "settings.indexing.lancedbDirectory.title": "Répertoire LanceDB", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ja.ts b/packages/kilo-vscode/webview-ui/src/i18n/ja.ts index b1ac37e4c27..8f39c291435 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ja.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ja.ts @@ -750,6 +750,13 @@ export const dict = { "settings.indexing.dimension.title": "ベクトル次元", "settings.indexing.enable.description": "セマンティックコードベースインデックスをオンまたはオフにします。", "settings.indexing.enable.title": "インデックスを有効にする", + "settings.indexing.globalEnable.title": "グローバルで有効にする", + "settings.indexing.globalEnable.description": "すべてのワークスペースでインデックス作成を有効にします。", + "settings.indexing.projectEnable.title": "このプロジェクトで有効にする", + "settings.indexing.projectEnable.description": + "グローバルなインデックス作成がオフの場合に、このワークスペースでのインデックス作成を有効にします。", + "settings.indexing.projectEnable.disabledTooltip": + "グローバルインデックスが有効なため、このプロジェクトはすでにカバーされています。", "settings.indexing.lancedbDirectory.description": "ローカルLanceDBストアのオプションのディレクトリ。", "settings.indexing.lancedbDirectory.placeholder": "デフォルトの場合は空のままにする", "settings.indexing.lancedbDirectory.title": "LanceDBディレクトリ", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ko.ts b/packages/kilo-vscode/webview-ui/src/i18n/ko.ts index 1098bb3bd39..46753b0ce94 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ko.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ko.ts @@ -1039,6 +1039,13 @@ export const dict = { "settings.indexing.dimension.title": "벡터 차원", "settings.indexing.enable.description": "의미적 코드베이스 인덱싱을 켜거나 끕니다.", "settings.indexing.enable.title": "인덱싱 활성화", + "settings.indexing.globalEnable.title": "전역으로 활성화", + "settings.indexing.globalEnable.description": "모든 작업 영역에 대해 인덱싱을 활성화합니다.", + "settings.indexing.projectEnable.title": "이 프로젝트에 대해 활성화", + "settings.indexing.projectEnable.description": + "전역 인덱싱이 꺼져 있을 때 이 작업 영역에 대해 인덱싱을 활성화합니다.", + "settings.indexing.projectEnable.disabledTooltip": + "글로벌 인덱싱이 활성화되어 있어 이 프로젝트는 이미 포함되어 있습니다.", "settings.indexing.lancedbDirectory.description": "로컬 LanceDB 저장소의 선택적 디렉터리입니다.", "settings.indexing.lancedbDirectory.placeholder": "기본값 사용을 위해 비워두세요", "settings.indexing.lancedbDirectory.title": "LanceDB 디렉터리", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/nl.ts b/packages/kilo-vscode/webview-ui/src/i18n/nl.ts index f89fb42915a..706db7fa53e 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/nl.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/nl.ts @@ -1042,6 +1042,13 @@ export const dict = { "settings.indexing.status.title": "Status", "settings.indexing.enable.title": "Indexering inschakelen", "settings.indexing.enable.description": "Schakel semantische codebase-indexering in of uit.", + "settings.indexing.globalEnable.title": "Globaal inschakelen", + "settings.indexing.globalEnable.description": "Indexering inschakelen voor elke werkruimte.", + "settings.indexing.projectEnable.title": "Inschakelen voor dit project", + "settings.indexing.projectEnable.description": + "Indexering inschakelen voor deze werkruimte wanneer globale indexering is uitgeschakeld.", + "settings.indexing.projectEnable.disabledTooltip": + "Globale indexering is ingeschakeld, dus dit project is al opgenomen.", "settings.indexing.provider.title": "Embedding-provider", "settings.indexing.provider.description": "Kies de provider die wordt gebruikt om embeddings te genereren voor semantisch zoeken.", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/no.ts b/packages/kilo-vscode/webview-ui/src/i18n/no.ts index b84a006adfd..23a84152969 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/no.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/no.ts @@ -1201,6 +1201,13 @@ export const dict = { "settings.indexing.title": "Indeksering", "settings.indexing.enable.title": "Aktiver indeksering", "settings.indexing.enable.description": "Slå semantisk kodebaseindeksering på eller av.", + "settings.indexing.globalEnable.title": "Aktiver globalt", + "settings.indexing.globalEnable.description": "Aktiver indeksering for hvert arbeidsområde.", + "settings.indexing.projectEnable.title": "Aktiver for dette prosjektet", + "settings.indexing.projectEnable.description": + "Aktiver indeksering for dette arbeidsområdet når global indeksering er slått av.", + "settings.indexing.projectEnable.disabledTooltip": + "Global indeksering er aktivert, så dette prosjektet er allerede dekket.", "settings.indexing.provider.title": "Embedding-leverandør", "settings.indexing.provider.description": "Velg leverandøren som brukes til å generere embeddings for semantisk søk.", "settings.indexing.model.title": "Embedding-modell", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/pl.ts b/packages/kilo-vscode/webview-ui/src/i18n/pl.ts index 75ca08d9aaf..160c726925f 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/pl.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/pl.ts @@ -1203,6 +1203,13 @@ export const dict = { "settings.indexing.dimension.title": "Wymiar wektora", "settings.indexing.enable.description": "Włącz lub wyłącz semantyczne indeksowanie bazy kodu.", "settings.indexing.enable.title": "Włącz indeksowanie", + "settings.indexing.globalEnable.title": "Włącz globalnie", + "settings.indexing.globalEnable.description": "Włącz indeksowanie dla każdego obszaru roboczego.", + "settings.indexing.projectEnable.title": "Włącz dla tego projektu", + "settings.indexing.projectEnable.description": + "Włącz indeksowanie dla tego obszaru roboczego, gdy globalne indeksowanie jest wyłączone.", + "settings.indexing.projectEnable.disabledTooltip": + "Globalne indeksowanie jest włączone, więc ten projekt jest już uwzględniony.", "settings.indexing.lancedbDirectory.description": "Opcjonalny katalog dla lokalnego magazynu LanceDB.", "settings.indexing.lancedbDirectory.placeholder": "Pozostaw puste dla domyślnego", "settings.indexing.lancedbDirectory.title": "Katalog LanceDB", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ru.ts b/packages/kilo-vscode/webview-ui/src/i18n/ru.ts index 0440767fca6..b61937eeb2d 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ru.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ru.ts @@ -754,6 +754,13 @@ export const dict = { "settings.indexing.title": "Индексация", "settings.indexing.enable.title": "Включить индексацию", "settings.indexing.enable.description": "Включить или отключить семантическую индексацию кодовой базы.", + "settings.indexing.globalEnable.title": "Включить глобально", + "settings.indexing.globalEnable.description": "Включить индексирование для каждого рабочего пространства.", + "settings.indexing.projectEnable.title": "Включить для этого проекта", + "settings.indexing.projectEnable.description": + "Включить индексирование для этого рабочего пространства, если глобальное индексирование отключено.", + "settings.indexing.projectEnable.disabledTooltip": + "Глобальное индексирование включено, поэтому этот проект уже охвачен.", "settings.indexing.status.title": "Статус", "settings.indexing.provider.title": "Провайдер эмбеддингов", "settings.indexing.provider.description": "Выберите провайдера для генерации эмбеддингов при семантическом поиске.", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/th.ts b/packages/kilo-vscode/webview-ui/src/i18n/th.ts index 4ba5b0707fd..cccec39218e 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/th.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/th.ts @@ -745,6 +745,12 @@ export const dict = { "settings.indexing.title": "การสร้างดัชนี", "settings.indexing.enable.title": "เปิดใช้งานการสร้างดัชนี", "settings.indexing.enable.description": "เปิดหรือปิดการสร้างดัชนีโค้ดเบสเชิงความหมาย", + "settings.indexing.globalEnable.title": "เปิดใช้งานแบบโกลบอล", + "settings.indexing.globalEnable.description": "เปิดใช้งานการทำดัชนีสำหรับทุกพื้นที่ทำงาน", + "settings.indexing.projectEnable.title": "เปิดใช้งานสำหรับโปรเจกต์นี้", + "settings.indexing.projectEnable.description": + "เปิดใช้งานการทำดัชนีสำหรับพื้นที่ทำงานนี้เมื่อการทำดัชนีแบบโกลบอลถูกปิดใช้งาน", + "settings.indexing.projectEnable.disabledTooltip": "เปิดใช้งานการทำดัชนีระดับโลกแล้ว โปรเจ็กต์นี้จึงถูกรวมไว้แล้ว", "settings.indexing.provider.title": "ผู้ให้บริการการฝัง", "settings.indexing.provider.description": "เลือกผู้ให้บริการที่ใช้สร้างการฝังสำหรับการค้นหาเชิงความหมาย", "settings.indexing.model.title": "โมเดลการฝัง", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/tr.ts b/packages/kilo-vscode/webview-ui/src/i18n/tr.ts index 03502f7350e..dd17350ae91 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/tr.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/tr.ts @@ -1041,6 +1041,13 @@ export const dict = { "settings.indexing.status.title": "Durum", "settings.indexing.enable.title": "İndekslemeyi etkinleştir", "settings.indexing.enable.description": "Anlamsal kod tabanı indekslemeyi açın veya kapatın.", + "settings.indexing.globalEnable.title": "Genel olarak etkinleştir", + "settings.indexing.globalEnable.description": "Her çalışma alanı için dizine eklemeyi etkinleştir.", + "settings.indexing.projectEnable.title": "Bu proje için etkinleştir", + "settings.indexing.projectEnable.description": + "Genel dizine ekleme kapalı olduğunda bu çalışma alanı için dizine eklemeyi etkinleştir.", + "settings.indexing.projectEnable.disabledTooltip": + "Küresel indeksleme etkinleştirildi, bu nedenle bu proje zaten kapsanıyor.", "settings.indexing.provider.title": "Yerleştirme sağlayıcısı", "settings.indexing.provider.description": "Anlamsal arama için yerleştirmeleri oluşturmak amacıyla kullanılacak sağlayıcıyı seçin.", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/uk.ts b/packages/kilo-vscode/webview-ui/src/i18n/uk.ts index 8521e91971f..9e0d7eec06c 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/uk.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/uk.ts @@ -1041,6 +1041,12 @@ export const dict = { "settings.indexing.status.title": "Статус", "settings.indexing.enable.title": "Увімкнути індексування", "settings.indexing.enable.description": "Увімкніть або вимкніть семантичне індексування кодової бази.", + "settings.indexing.globalEnable.title": "Увімкнути глобально", + "settings.indexing.globalEnable.description": "Увімкнути індексування для кожного робочого простору.", + "settings.indexing.projectEnable.title": "Увімкнути для цього проєкту", + "settings.indexing.projectEnable.description": + "Увімкнути індексування для цього робочого простору, коли глобальне індексування вимкнено.", + "settings.indexing.projectEnable.disabledTooltip": "Глобальне індексування ввімкнено, тому цей проєкт уже охоплено.", "settings.indexing.provider.title": "Провайдер ембедингів", "settings.indexing.provider.description": "Виберіть провайдера для генерації ембедингів для семантичного пошуку.", "settings.indexing.model.title": "Модель ембедингів", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/zh.ts b/packages/kilo-vscode/webview-ui/src/i18n/zh.ts index bbbec131f2d..dfdaeea385b 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/zh.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/zh.ts @@ -735,6 +735,11 @@ export const dict = { "settings.indexing.title": "索引", "settings.indexing.enable.title": "启用索引", "settings.indexing.enable.description": "打开或关闭语义代码库索引。", + "settings.indexing.globalEnable.title": "全局启用", + "settings.indexing.globalEnable.description": "为每个工作区启用索引。", + "settings.indexing.projectEnable.title": "为此项目启用", + "settings.indexing.projectEnable.description": "当全局索引关闭时,为此工作区启用索引。", + "settings.indexing.projectEnable.disabledTooltip": "全局索引已启用,当前项目已包含在内。", "settings.indexing.provider.title": "嵌入提供商", "settings.indexing.provider.description": "选择用于生成语义搜索嵌入的提供商。", "settings.indexing.model.title": "嵌入模型", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/zht.ts b/packages/kilo-vscode/webview-ui/src/i18n/zht.ts index f11521864d3..4c27852ded3 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/zht.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/zht.ts @@ -1361,6 +1361,11 @@ export const dict = { "settings.indexing.dimension.title": "向量維度", "settings.indexing.enable.description": "開啟或關閉語意程式碼庫索引。", "settings.indexing.enable.title": "啟用索引", + "settings.indexing.globalEnable.title": "全域啟用", + "settings.indexing.globalEnable.description": "為每個工作區啟用索引。", + "settings.indexing.projectEnable.title": "為此專案啟用", + "settings.indexing.projectEnable.description": "當全域索引關閉時,為此工作區啟用索引。", + "settings.indexing.projectEnable.disabledTooltip": "全域索引已啟用,目前專案已包含在內。", "settings.indexing.lancedbDirectory.description": "本機 LanceDB 儲存的可選目錄。", "settings.indexing.lancedbDirectory.placeholder": "留空使用預設值", "settings.indexing.lancedbDirectory.title": "LanceDB 目錄", diff --git a/packages/kilo-vscode/webview-ui/src/stories/StoryProviders.tsx b/packages/kilo-vscode/webview-ui/src/stories/StoryProviders.tsx index a6b1b9050e2..d601907545f 100644 --- a/packages/kilo-vscode/webview-ui/src/stories/StoryProviders.tsx +++ b/packages/kilo-vscode/webview-ui/src/stories/StoryProviders.tsx @@ -286,6 +286,7 @@ const ConfigWrapper: ParentComponent<{ config?: Config; onConfigChange?: (config const value = { config: createMemo(() => cfg()), + globalConfig: createMemo(() => cfg()), settings, features, loading: () => false, @@ -300,6 +301,14 @@ const ConfigWrapper: ParentComponent<{ config?: Config; onConfigChange?: (config }) setDirty(true) }, + updateGlobalConfig: (partial: Partial) => { + setCfg((prev) => { + const next = merge(prev as Record, partial as Record) as Config + props.onConfigChange?.(next) + return next + }) + setDirty(true) + }, updateSetting: (key: string, value: unknown) => { setSettings((prev) => ({ ...prev, [key]: value })) setDirty(true) diff --git a/packages/kilo-vscode/webview-ui/src/types/messages/extension-messages.ts b/packages/kilo-vscode/webview-ui/src/types/messages/extension-messages.ts index 9641bef4ac3..b44428137ab 100644 --- a/packages/kilo-vscode/webview-ui/src/types/messages/extension-messages.ts +++ b/packages/kilo-vscode/webview-ui/src/types/messages/extension-messages.ts @@ -409,12 +409,14 @@ export interface ClaudeCompatSettingLoadedMessage { export interface ConfigLoadedMessage { type: "configLoaded" config: Config + globalConfig?: Config features: FeatureFlags } export interface ConfigUpdatedMessage { type: "configUpdated" config: Config + globalConfig?: Config features: FeatureFlags } diff --git a/packages/opencode/src/cli/cmd/tui/context/sync.tsx b/packages/opencode/src/cli/cmd/tui/context/sync.tsx index 880ed021896..e1abfbc6d8e 100644 --- a/packages/opencode/src/cli/cmd/tui/context/sync.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/sync.tsx @@ -66,6 +66,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ } // kilocode_change end config: Config + globalConfig: Config // kilocode_change session: Session[] session_status: { [sessionID: string]: SessionStatus @@ -101,6 +102,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ console_state: emptyConsoleState, provider_auth: {}, config: {}, + globalConfig: {}, // kilocode_change status: "loading", agent: [], permission: {}, @@ -487,6 +489,9 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ // kilocode_change start case "global.config.updated": { + sdk.client.global.config.get().then((x) => { + if (x.data) setStore("globalConfig", reconcile(x.data)) + }) sdk.client.config.get().then((x) => { if (x.data) setStore("config", reconcile(x.data)) }) @@ -522,11 +527,13 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ .catch(() => emptyConsoleState) const agentsPromise = sdk.client.app.agents({ workspace }, { throwOnError: true }) const configPromise = sdk.client.config.get({ workspace }, { throwOnError: true }) + const globalConfigPromise = sdk.client.global.config.get({ throwOnError: true }) // kilocode_change const blockingRequests: Promise[] = [ providersPromise, providerListPromise, agentsPromise, configPromise, + globalConfigPromise, // kilocode_change projectPromise, ...(args.continue ? [sessionListPromise] : []), ] @@ -538,6 +545,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ const consoleStateResponse = consoleStatePromise const agentsResponse = agentsPromise.then((x) => x.data ?? []) const configResponse = configPromise.then((x) => x.data!) + const globalConfigResponse = globalConfigPromise.then((x) => x.data!) // kilocode_change const sessionListResponse = args.continue ? sessionListPromise : undefined return Promise.all([ @@ -546,6 +554,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ consoleStateResponse, agentsResponse, configResponse, + globalConfigResponse, // kilocode_change ...(sessionListResponse ? [sessionListResponse] : []), ]).then((responses) => { const providers = responses[0] @@ -553,7 +562,8 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ const consoleState = responses[2] const agents = responses[3] const config = responses[4] - const sessions = responses[5] + const globalConfig = responses[5] // kilocode_change + const sessions = responses[6] // kilocode_change batch(() => { setStore("provider", reconcile(providers.providers)) @@ -562,6 +572,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ setStore("console_state", reconcile(consoleState)) setStore("agent", reconcile(agents)) setStore("config", reconcile(config)) + setStore("globalConfig", reconcile(globalConfig)) // kilocode_change if (sessions !== undefined) setStore("session", reconcile(sessions)) }) }) diff --git a/packages/opencode/src/kilocode/components/dialog-indexing.tsx b/packages/opencode/src/kilocode/components/dialog-indexing.tsx index b09c1dd957a..61f8c6ed847 100644 --- a/packages/opencode/src/kilocode/components/dialog-indexing.tsx +++ b/packages/opencode/src/kilocode/components/dialog-indexing.tsx @@ -11,6 +11,7 @@ import { DialogSelect, type DialogSelectOption } from "@tui/ui/dialog-select" import { DialogPrompt } from "@tui/ui/dialog-prompt" import { useSync } from "@tui/context/sync" import { useToast } from "@tui/ui/toast" +import { createResource } from "solid-js" import { reconcile } from "solid-js/store" import type { IndexingConfig, Config } from "@kilocode/sdk/v2" @@ -70,6 +71,10 @@ function getIndexing(sync: ReturnType): IndexingConfig { return (sync.data.config as Config & { indexing?: IndexingConfig }).indexing ?? {} } +function globalIndexing(data: Config | undefined): IndexingConfig { + return data?.indexing ?? {} +} + async function saveIndexing( sdk: SDK, sync: ReturnType, @@ -99,6 +104,40 @@ async function saveIndexing( return true } +async function saveGlobalIndexing( + sdk: SDK, + sync: ReturnType, + indexing: IndexingConfig, + toast: ReturnType, +): Promise { + const response = await sdk.client.global.config.update({ config: { indexing } }) + if (response.error) { + toast.show({ message: "Failed to save indexing config", variant: "error" }) + return false + } + const merged = await sdk.client.config.get({}) + if (merged.data) sync.set("config", reconcile(merged.data)) + toast.show({ message: "Indexing config saved", variant: "success" }) + return true +} + +async function saveProjectIndexing( + sdk: SDK, + sync: ReturnType, + indexing: IndexingConfig, + toast: ReturnType, +): Promise { + const response = await sdk.client.config.update({ config: { indexing: { enabled: indexing.enabled } } }) + if (response.error) { + toast.show({ message: "Failed to save indexing config", variant: "error" }) + return false + } + const configResponse = await sdk.client.config.get({}) + if (configResponse.data) sync.set("config", reconcile(configResponse.data)) + toast.show({ message: "Indexing config saved", variant: "success" }) + return true +} + function providerSettingsDescription(indexing: IndexingConfig, provider: EmbeddingProvider): string { const fields = PROVIDER_FIELDS[provider] const settings = indexing[provider] as Record | undefined @@ -356,6 +395,8 @@ export function DialogIndexing(props: DialogIndexingProps) { const sdk = props.useSDK() const toast = useToast() const indexing = getIndexing(sync) + const [global] = createResource(async () => (await sdk.client.global.config.get({})).data as Config | undefined) + const globalCfg = () => globalIndexing(global()) const providerLabel = indexing.provider ? PROVIDER_LABELS[indexing.provider] : "not set" const storeLabel = indexing.vectorStore @@ -367,10 +408,20 @@ export function DialogIndexing(props: DialogIndexingProps) { const options: DialogSelectOption[] = [ { - value: "toggle", - title: "Indexing", + value: "globalToggle", + title: "Indexing (Global)", + category: "General", + description: global.loading ? "loading" : globalCfg().enabled ? "enabled" : "disabled", + }, + { + value: "projectToggle", + title: "Indexing (Project)", category: "General", - description: indexing.enabled ? "enabled" : "disabled", + description: globalCfg().enabled + ? "controlled by global" + : indexing.enabled + ? "enabled" + : "disabled", }, { value: "provider", @@ -422,9 +473,18 @@ export function DialogIndexing(props: DialogIndexingProps) { skipFilter onSelect={async (option) => { switch (option.value) { - case "toggle": { - const updated = { ...getIndexing(sync), enabled: !indexing.enabled } - await saveIndexing(sdk, sync, updated, toast) + case "globalToggle": { + await saveGlobalIndexing(sdk, sync, { enabled: !globalCfg().enabled }, toast) + dialog.replace(() => ) + break + } + case "projectToggle": { + if (globalCfg().enabled) { + toast.show({ message: "Global indexing is enabled, so this project is already covered.", variant: "info" }) + dialog.replace(() => ) + break + } + await saveProjectIndexing(sdk, sync, { enabled: !indexing.enabled }, toast) dialog.replace(() => ) break } diff --git a/packages/opencode/src/kilocode/config/config.ts b/packages/opencode/src/kilocode/config/config.ts index dfbbcfcbe97..6332f22c0a0 100644 --- a/packages/opencode/src/kilocode/config/config.ts +++ b/packages/opencode/src/kilocode/config/config.ts @@ -117,7 +117,7 @@ export namespace KilocodeConfig { } function stripGlobalIndexing(info: Config.Info): Config.Info { - // Indexing provider/storage settings can be global, but enablement is a per-project decision. + // Indexing provider/storage settings can be global, but enablement is exposed separately from project enablement. if (info.indexing?.enabled === undefined) return info const indexing = Object.fromEntries(Object.entries(info.indexing).filter(([key]) => key !== "enabled")) if (Object.keys(indexing).length > 0) return { ...info, indexing } diff --git a/packages/opencode/src/kilocode/indexing.ts b/packages/opencode/src/kilocode/indexing.ts index e51dd5a624a..4b73dcc80e2 100644 --- a/packages/opencode/src/kilocode/indexing.ts +++ b/packages/opencode/src/kilocode/indexing.ts @@ -6,7 +6,7 @@ import { type IndexingTelemetryEvent, type VectorStoreSearchResult, } from "@kilocode/kilo-indexing/engine" -import { toIndexingConfigInput } from "@kilocode/kilo-indexing/config" +import { toIndexingConfigInput, type IndexingConfig } from "@kilocode/kilo-indexing/config" import { hasIndexingPlugin } from "@kilocode/kilo-indexing/detect" import { IndexingStatus, @@ -141,6 +141,13 @@ export namespace KiloIndexing { export const Status = IndexingStatus export type Status = z.infer + export function input(config?: IndexingConfig, global?: IndexingConfig) { + return toIndexingConfigInput({ + ...config, + enabled: config?.enabled === true || global?.enabled === true, + }) + } + // Mirror of IndexingStatus using Effect Schema for BusEvent.define, which // requires a Schema.Top. The zod form above is kept for consumers that still // depend on the z.infer-derived type. @@ -225,7 +232,8 @@ export namespace KiloIndexing { log.info("initializing project indexing", { workspacePath: dir }) const root = path.join(Global.Path.state, "indexing") const manager = new CodeIndexManager(dir, root) - const input = toIndexingConfigInput(cfg.indexing) + const globalConfig = await Config.getGlobal() + const cfgInput = input(cfg.indexing, globalConfig.indexing) const box = { status: pending() as Status | undefined } const current = () => box.status ?? normalizeIndexingStatus(manager) let disposed = false @@ -265,8 +273,8 @@ export namespace KiloIndexing { if (hit.disposed) return base // kilocode_change start - const err = await LanceDBRuntime.ensure(input.vectorStoreProvider) - .then(() => manager.initialize(input)) + const err = await LanceDBRuntime.ensure(cfgInput.vectorStoreProvider) + .then(() => manager.initialize(cfgInput)) .then( () => undefined, (err) => err, diff --git a/packages/opencode/test/cli/cmd/tui/sync.test.tsx b/packages/opencode/test/cli/cmd/tui/sync.test.tsx index 2ace0fa8a90..aa36d179b3d 100644 --- a/packages/opencode/test/cli/cmd/tui/sync.test.tsx +++ b/packages/opencode/test/cli/cmd/tui/sync.test.tsx @@ -52,6 +52,7 @@ function createFetch() { return json([]) case "/config": case "/experimental/resource": + case "/global/config": // kilocode_change case "/mcp": case "/provider/auth": case "/session/status": diff --git a/packages/opencode/test/kilocode/config/config.test.ts b/packages/opencode/test/kilocode/config/config.test.ts index 43e1e266a0b..cfe58435ac5 100644 --- a/packages/opencode/test/kilocode/config/config.test.ts +++ b/packages/opencode/test/kilocode/config/config.test.ts @@ -8,11 +8,11 @@ import { AppFileSystem } from "@opencode-ai/core/filesystem" import { EffectFlock } from "@opencode-ai/core/util/effect-flock" import * as CrossSpawnSpawner from "@opencode-ai/core/cross-spawn-spawner" import { Npm } from "@opencode-ai/core/npm" -import { toIndexingConfigInput } from "@kilocode/kilo-indexing/config" import { Account } from "../../../src/account/account" import { Auth } from "../../../src/auth" import { Config } from "../../../src/config/config" import { Env } from "../../../src/env" +import { KiloIndexing } from "../../../src/kilocode/indexing" import { Instance } from "../../../src/project/instance" import { Filesystem } from "../../../src/util/filesystem" import { disposeAllInstances, tmpdir } from "../../fixture/fixture" @@ -50,13 +50,27 @@ async function writeConfig(dir: string, config: object, name = "kilo.json") { await Filesystem.write(path.join(dir, name), JSON.stringify(config)) } +const cfg: Partial = { + plugin: ["@kilocode/kilo-indexing"], + experimental: { + semantic_indexing: true, + }, + indexing: { + provider: "ollama", + vectorStore: "qdrant", + ollama: { + baseUrl: "http://127.0.0.1:1", + }, + }, +} + describe("kilocode indexing config", () => { afterEach(async () => { await disposeAllInstances() await clear(true) }) - test("does not inherit global indexing enabled into project config", async () => { + test("keeps global indexing enabled in global config", async () => { await using globalTmp = await tmpdir() await using tmp = await tmpdir() @@ -77,9 +91,45 @@ describe("kilocode indexing config", () => { directory: tmp.path, fn: async () => { const config = await load() + const global = await Effect.runPromise( + Config.Service.use((svc) => svc.getGlobal()).pipe(Effect.scoped, Effect.provide(layer)), + ) expect(config.indexing?.provider).toBe("ollama") expect(config.indexing?.enabled).toBeUndefined() - expect(toIndexingConfigInput(config.indexing).enabled).toBe(false) + expect(global.indexing?.enabled).toBe(true) + }, + }) + } finally { + ;(Global.Path as { config: string }).config = prev + await clear(true) + } + }) + + test("uses global indexing enabled when project enablement is unset", async () => { + await using globalTmp = await tmpdir() + await using tmp = await tmpdir({ git: true, config: cfg }) + + const prev = Global.Path.config + ;(Global.Path as { config: string }).config = globalTmp.path + await clear(true) + + try { + await writeConfig(globalTmp.path, { + $schema: "https://app.kilo.ai/config.json", + indexing: { + enabled: true, + }, + }) + + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const global = await Effect.runPromise( + Config.Service.use((svc) => svc.getGlobal()).pipe(Effect.scoped, Effect.provide(layer)), + ) + const config = await load() + const input = KiloIndexing.input(config.indexing, global.indexing) + expect(input.enabled).toBe(true) }, }) } finally { @@ -87,4 +137,9 @@ describe("kilocode indexing config", () => { await clear(true) } }) + + test("global indexing enabled applies when project indexing is disabled", async () => { + const input = KiloIndexing.input({ enabled: false }, { enabled: true }) + expect(input.enabled).toBe(true) + }) })