Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/indexing-global-project-toggles.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@kilocode/cli": patch
"kilo-code": patch
---

Support global and per-project codebase indexing enablement.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 31 additions & 4 deletions packages/kilo-vscode/src/KiloProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
Expand All @@ -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 {
Expand Down
15 changes: 10 additions & 5 deletions packages/kilo-vscode/src/provider-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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)

Expand Down
19 changes: 19 additions & 0 deletions packages/kilo-vscode/tests/unit/config-scope.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ function createConnection() {
const client = {
global: {
config: {
get: async () => ({ data: {} }),
update: async () => ({ data: {} }),
},
},
config: {
get: async () => ({ data: {} }),
update: async () => ({ data: {} }),
},
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -18,7 +18,7 @@ export interface AboutKiloCodeTabProps {
const AboutKiloCodeTab: Component<AboutKiloCodeTabProps> = (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
Expand All @@ -27,6 +27,23 @@ const AboutKiloCodeTab: Component<AboutKiloCodeTabProps> = (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
Expand Down Expand Up @@ -91,7 +108,7 @@ const AboutKiloCodeTab: Component<AboutKiloCodeTabProps> = (props) => {
title: language.t("settings.aboutKiloCode.importSettings.newerVersion"),
})
}
updateConfig(result.config)
importConfig(result.config)
showToast({
variant: "success",
title: language.t("settings.aboutKiloCode.importSettings.success"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -65,14 +66,16 @@ 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<Record<string, string>>({})
const [storeDrafts, setStoreDrafts] = createSignal<Record<string, string>>({})
const [tuningDrafts, setTuningDrafts] = createSignal<Record<string, string>>({})

const cfg = createMemo<IndexingConfig>(() => config().indexing ?? {})
const globalCfg = createMemo<IndexingConfig>(() => globalConfig().indexing ?? {})
const globalOn = createMemo(() => globalCfg().enabled === true)

const updateIndexing = (partial: IndexingConfig) => {
updateConfig({ indexing: { ...cfg(), ...partial } })
Expand Down Expand Up @@ -142,18 +145,37 @@ const IndexingTab: Component = () => {
</span>
</SettingsRow>
<SettingsRow
title={language.t("settings.indexing.enable.title")}
description={language.t("settings.indexing.enable.description")}
last
title={language.t("settings.indexing.globalEnable.title")}
description={language.t("settings.indexing.globalEnable.description")}
>
<Switch
checked={cfg().enabled ?? false}
onChange={(checked) => 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")}
</Switch>
</SettingsRow>
<SettingsRow
title={language.t("settings.indexing.projectEnable.title")}
description={language.t("settings.indexing.projectEnable.description")}
last
>
<Tooltip
value={language.t("settings.indexing.projectEnable.disabledTooltip")}
placement="top"
inactive={!globalOn()}
>
<Switch
checked={cfg().enabled === true}
onChange={(checked) => updateIndexing({ enabled: checked })}
disabled={globalOn()}
hideLabel
>
{language.t("settings.indexing.projectEnable.title")}
</Switch>
</Tooltip>
</SettingsRow>
</Card>

<Card>
Expand Down
Loading
Loading