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/spotlight-indexing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@kilocode/cli": patch
"kilo-code": patch
---

Prevent macOS Spotlight from indexing Kilo-generated data directories.
5 changes: 5 additions & 0 deletions packages/core/src/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { xdgData, xdgCache, xdgConfig, xdgState } from "xdg-basedir"
import os from "os"
import { Context, Effect, Layer } from "effect"
import { Flock } from "./util/flock"
import { markNoIndex } from "./kilocode/spotlight" // kilocode_change

const app = "kilo" // kilocode_change
// kilocode_change start
Expand Down Expand Up @@ -45,6 +46,10 @@ await Promise.all([
fs.mkdir(Path.bin, { recursive: true }),
])

// kilocode_change start - keep generated Kilo data out of macOS Spotlight
await Promise.all([Path.data, Path.cache, Path.state].map(markNoIndex))
// kilocode_change end

export class Service extends Context.Service<Service, Interface>()("@opencode/Global") {}

export interface Interface {
Expand Down
23 changes: 23 additions & 0 deletions packages/core/src/kilocode/spotlight.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import fs from "fs/promises"
import path from "path"

const marker = ".metadata_never_index"

function exists(err: unknown): boolean {
if (typeof err !== "object" || err === null) return false
return "code" in err && err.code === "EEXIST"
}

function message(err: unknown): string {
if (err instanceof Error) return err.message
return String(err)
}

export async function markNoIndex(dir: string): Promise<void> {
if (process.platform !== "darwin") return
const file = path.join(dir, marker)
await fs.writeFile(file, "", { flag: "wx" }).catch((err) => {
if (exists(err)) return
process.emitWarning(`Failed to mark ${dir} as Spotlight-excluded: ${message(err)}`)
})
}
3 changes: 3 additions & 0 deletions packages/kilo-vscode/src/agent-manager/WorktreeManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import simpleGit, { type SimpleGit } from "simple-git"
import { generateBranchName, sanitizeBranchName } from "./branch-name"
import { type GitOps, nonInteractiveEnv } from "./GitOps"
import { execWithShellEnv } from "./shell-env"
import { markNoIndex } from "../util/spotlight"
import {
parsePRUrl,
localBranchName,
Expand Down Expand Up @@ -424,6 +425,7 @@ export class WorktreeManager {
async discoverWorktrees(): Promise<WorktreeInfo[]> {
await this.ensureMigrated()
if (!fs.existsSync(this.dir)) return []
await markNoIndex(this.dir, this.log)

const entries = await fs.promises.readdir(this.dir, { withFileTypes: true })
this.cleanupOrphanedTempDirs()
Expand Down Expand Up @@ -575,6 +577,7 @@ export class WorktreeManager {
if (!fs.existsSync(this.dir)) {
await fs.promises.mkdir(this.dir, { recursive: true })
}
await markNoIndex(this.dir, this.log)
}

private async resolveGitDir(): Promise<string> {
Expand Down
5 changes: 5 additions & 0 deletions packages/kilo-vscode/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { registerCodeActions, registerTerminalActions, KiloCodeActionProvider }
import { registerToggleAutoApprove } from "./commands/toggle-auto-approve"
import { registerHeapSnapshot } from "./commands/heap-snapshot"
import { RemoteStatusService } from "./services/RemoteStatusService"
import { markWorkspace } from "./util/spotlight"

// Activated via "onStartupFinished" (package.json) so that commands, code actions, keybindings,
// autocomplete, commit-message generation, and URI deep links all work immediately — without
Expand Down Expand Up @@ -67,6 +68,10 @@ export function activate(context: vscode.ExtensionContext) {
// Prewarm the CLI backend early so autocomplete is ready before first editor use.
ensureBackendForAutocomplete(connectionService)

for (const folder of vscode.workspace.workspaceFolders ?? []) {
void markWorkspace(folder.uri.fsPath, (msg) => console.warn(`[Kilo New] ${msg}`))
}

// Track all open tab panel providers so toolbar button commands can target them.
// NOTE: The editor/title toolbar for tab panels intentionally omits Agent Manager
// and Marketplace buttons (unlike the sidebar). Too many icons causes VS Code to
Expand Down
49 changes: 49 additions & 0 deletions packages/kilo-vscode/src/util/spotlight.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import * as fs from "fs"
import * as path from "path"

const marker = ".metadata_never_index"

function exists(err: unknown): boolean {
if (typeof err !== "object" || err === null) return false
return "code" in err && err.code === "EEXIST"
}

function message(err: unknown): string {
if (err instanceof Error) return err.message
return String(err)
}

export async function markNoIndex(dir: string, log: (msg: string) => void): Promise<void> {
if (process.platform !== "darwin") return
const file = path.join(dir, marker)
await fs.promises.writeFile(file, "", { flag: "wx" }).catch((err) => {
if (exists(err)) return
log(`Warning: Failed to mark ${dir} as Spotlight-excluded: ${message(err)}`)
})
}

async function directory(dir: string): Promise<boolean> {
return fs.promises
.stat(dir)
.then((stat) => stat.isDirectory())
.catch(() => false)
}

function parent(dir: string): string | undefined {
const parts = path.resolve(dir).split(path.sep)
for (let i = 0; i < parts.length - 1; i++) {
const hidden = parts[i] === ".kilo" || parts[i] === ".kilocode"
if (hidden && parts[i + 1] === "worktrees") return parts.slice(0, i + 2).join(path.sep) || path.sep
}
return undefined
}

export async function markWorkspace(root: string, log: (msg: string) => void): Promise<void> {
const ancestor = parent(root)
if (ancestor) await markNoIndex(ancestor, log)

for (const name of [".kilo", ".kilocode"]) {
const dir = path.join(root, name, "worktrees")
if (await directory(dir)) await markNoIndex(dir, log)
}
}
Loading