From 62923adb518371d1659ea65e5519768e4abf231b Mon Sep 17 00:00:00 2001 From: kirillk Date: Fri, 7 Aug 2026 15:26:38 -0400 Subject: [PATCH 01/14] feat(jetbrains): add automatic editor context Include active editor files, open tabs, visible files, selected ranges, and shell metadata in JetBrains chat prompts so agents can infer the user's current editor context without manual attachment. Filter paths through .kilocodeignore or fallback gitignore/.env rules before sending them to the backend. --- .changeset/jetbrains-auto-editor-context.md | 5 + .../.run/Run IDE (Backend).run.xml | 2 +- .../.run/Run IDE (Frontend).run.xml | 2 +- .../.run/runIdeSplitMode.run.xml | 2 +- .../kilocode/backend/cli/KiloCliDataParser.kt | 18 +++ .../backend/cli/ChatLogSummaryTest.kt | 22 +++ .../backend/cli/KiloCliDataParserTest.kt | 20 +++ .../client/plugin/KiloPluginSettings.kt | 11 ++ .../ai/kilocode/client/session/SessionUi.kt | 9 +- .../session/context/EditorContextGatherer.kt | 136 +++++++++++++++++ .../client/session/context/KiloIgnore.kt | 138 ++++++++++++++++++ .../session/controller/SessionController.kt | 12 +- .../settings/context/ContextSettingsUi.kt | 14 +- .../resources/messages/KiloBundle.properties | 3 + .../context/EditorContextGathererTest.kt | 91 ++++++++++++ .../client/session/context/KiloIgnoreTest.kt | 89 +++++++++++ .../controller/EditorContextPromptTest.kt | 30 ++++ .../kotlin/ai/kilocode/log/ChatLogSummary.kt | 12 ++ .../kotlin/ai/kilocode/rpc/dto/ChatDto.kt | 11 ++ 19 files changed, 617 insertions(+), 10 deletions(-) create mode 100644 .changeset/jetbrains-auto-editor-context.md create mode 100644 packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/context/EditorContextGatherer.kt create mode 100644 packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/context/KiloIgnore.kt create mode 100644 packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/context/EditorContextGathererTest.kt create mode 100644 packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/context/KiloIgnoreTest.kt create mode 100644 packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/controller/EditorContextPromptTest.kt diff --git a/.changeset/jetbrains-auto-editor-context.md b/.changeset/jetbrains-auto-editor-context.md new file mode 100644 index 0000000000..3a6b91d758 --- /dev/null +++ b/.changeset/jetbrains-auto-editor-context.md @@ -0,0 +1,5 @@ +--- +"@kilocode/kilo-jetbrains": minor +--- + +Include the active editor file, open files, visible files, and selected text in JetBrains chat context by default, with a Context settings toggle to disable it. Files matched by `.kilocodeignore` (or `.gitignore` plus `.env` files) are excluded, and the default shell is reported to the agent. diff --git a/packages/kilo-jetbrains/.run/Run IDE (Backend).run.xml b/packages/kilo-jetbrains/.run/Run IDE (Backend).run.xml index af1ad2c550..9e015ef719 100644 --- a/packages/kilo-jetbrains/.run/Run IDE (Backend).run.xml +++ b/packages/kilo-jetbrains/.run/Run IDE (Backend).run.xml @@ -5,7 +5,7 @@ diff --git a/packages/kilo-jetbrains/.run/Run IDE (Frontend).run.xml b/packages/kilo-jetbrains/.run/Run IDE (Frontend).run.xml index 8fd300cd6c..fc04e21f7d 100644 --- a/packages/kilo-jetbrains/.run/Run IDE (Frontend).run.xml +++ b/packages/kilo-jetbrains/.run/Run IDE (Frontend).run.xml @@ -5,7 +5,7 @@ diff --git a/packages/kilo-jetbrains/.run/runIdeSplitMode.run.xml b/packages/kilo-jetbrains/.run/runIdeSplitMode.run.xml index 5ab397fabf..f709383cb2 100644 --- a/packages/kilo-jetbrains/.run/runIdeSplitMode.run.xml +++ b/packages/kilo-jetbrains/.run/runIdeSplitMode.run.xml @@ -6,7 +6,7 @@ diff --git a/packages/kilo-jetbrains/backend/src/main/kotlin/ai/kilocode/backend/cli/KiloCliDataParser.kt b/packages/kilo-jetbrains/backend/src/main/kotlin/ai/kilocode/backend/cli/KiloCliDataParser.kt index 09e112dca1..85c08422b6 100644 --- a/packages/kilo-jetbrains/backend/src/main/kotlin/ai/kilocode/backend/cli/KiloCliDataParser.kt +++ b/packages/kilo-jetbrains/backend/src/main/kotlin/ai/kilocode/backend/cli/KiloCliDataParser.kt @@ -26,6 +26,7 @@ import ai.kilocode.rpc.dto.CustomModelDto import ai.kilocode.rpc.dto.CustomProviderConfigDto import ai.kilocode.rpc.dto.CustomProviderSaveDto import ai.kilocode.rpc.dto.DiffFileDto +import ai.kilocode.rpc.dto.EditorContextDto import ai.kilocode.rpc.dto.MessageDto import ai.kilocode.rpc.dto.MessageErrorDto import ai.kilocode.rpc.dto.MessageSummaryDto @@ -822,10 +823,27 @@ object KiloCliDataParser { if (variant != null) { sb.append(""","variant":${escape(variant)}""") } + val editor = prompt.editorContext + if (editor != null) { + sb.append(""","editorContext":${editorContextJson(editor)}""") + } sb.append("}") return sb.toString() } + private fun editorContextJson(ctx: EditorContextDto): String { + val fields = mutableListOf() + ctx.directory?.let { fields += "\"directory\":${escape(it)}" } + ctx.worktree?.let { fields += "\"worktree\":${escape(it)}" } + ctx.visibleFiles?.takeIf { it.isNotEmpty() }?.let { fields += "\"visibleFiles\":${array(it)}" } + ctx.openTabs?.takeIf { it.isNotEmpty() }?.let { fields += "\"openTabs\":${array(it)}" } + ctx.activeFile?.let { fields += "\"activeFile\":${escape(it)}" } + ctx.shell?.let { fields += "\"shell\":${escape(it)}" } + return "{${fields.joinToString(",")}}" + } + + private fun array(values: List): String = values.joinToString(",", "[", "]") { escape(it) } + private fun buildPromptPartJson(part: PromptPartDto): String { val fields = mutableListOf("\"type\":${escape(part.type)}") if (part.type == "file") { diff --git a/packages/kilo-jetbrains/backend/src/test/kotlin/ai/kilocode/backend/cli/ChatLogSummaryTest.kt b/packages/kilo-jetbrains/backend/src/test/kotlin/ai/kilocode/backend/cli/ChatLogSummaryTest.kt index 23afe33909..06c0259d1a 100644 --- a/packages/kilo-jetbrains/backend/src/test/kotlin/ai/kilocode/backend/cli/ChatLogSummaryTest.kt +++ b/packages/kilo-jetbrains/backend/src/test/kotlin/ai/kilocode/backend/cli/ChatLogSummaryTest.kt @@ -2,6 +2,7 @@ package ai.kilocode.backend.cli import ai.kilocode.log.ChatLogSummary import ai.kilocode.rpc.dto.ChatEventDto +import ai.kilocode.rpc.dto.EditorContextDto import ai.kilocode.rpc.dto.MessageDto import ai.kilocode.rpc.dto.MessageErrorDto import ai.kilocode.rpc.dto.MessageTimeDto @@ -156,6 +157,27 @@ class ChatLogSummaryTest { assertTrue(out.contains("variant=medium"), out) } + @Test + fun `prompt dto summary includes editor context`() { + System.setProperty("kilo.dev.log.chat.content", "preview") + + val out = ChatLogSummary.prompt( + PromptDto( + parts = listOf(PromptPartDto(type = "text", text = "hello")), + editorContext = EditorContextDto( + activeFile = "settings.gradle", + openTabs = listOf("settings.gradle", "src/App.kt"), + visibleFiles = listOf("settings.gradle"), + ), + ) + ) + + assertTrue(out.contains("editorContext=true"), out) + assertTrue(out.contains("activeFile=\"settings.gradle\""), out) + assertTrue(out.contains("openTabs=2"), out) + assertTrue(out.contains("visibleFiles=1"), out) + } + @Test fun `prompt dto summary redacts file attachment urls`() { System.setProperty("kilo.dev.log.chat.content", "preview") diff --git a/packages/kilo-jetbrains/backend/src/test/kotlin/ai/kilocode/backend/cli/KiloCliDataParserTest.kt b/packages/kilo-jetbrains/backend/src/test/kotlin/ai/kilocode/backend/cli/KiloCliDataParserTest.kt index dab1d5b713..132149fbfd 100644 --- a/packages/kilo-jetbrains/backend/src/test/kotlin/ai/kilocode/backend/cli/KiloCliDataParserTest.kt +++ b/packages/kilo-jetbrains/backend/src/test/kotlin/ai/kilocode/backend/cli/KiloCliDataParserTest.kt @@ -8,6 +8,7 @@ import ai.kilocode.rpc.dto.CompactionPatchDto import ai.kilocode.rpc.dto.ConfigDto import ai.kilocode.rpc.dto.ConfigPatchDto import ai.kilocode.rpc.dto.ConfigUpdateDto +import ai.kilocode.rpc.dto.EditorContextDto import ai.kilocode.rpc.dto.McpConfigDto import ai.kilocode.rpc.dto.PermissionAlwaysRulesDto import ai.kilocode.rpc.dto.PermissionReplyDto @@ -1958,6 +1959,25 @@ class KiloCliDataParserTest { assertEquals("""{"parts":[{"type":"text","text":"Hi"}],"noReply":true}""", result) } + @Test + fun `buildPromptJson - with editor context`() { + val prompt = PromptDto( + parts = listOf(PromptPartDto("text", "Hi")), + editorContext = EditorContextDto( + activeFile = "src/App.kt", + visibleFiles = listOf("src/App.kt"), + openTabs = listOf("src/App.kt", "src/Other.kt"), + ), + ) + + val result = KiloCliDataParser.buildPromptJson(prompt) + + assertEquals( + """{"parts":[{"type":"text","text":"Hi"}],"editorContext":{"visibleFiles":["src/App.kt"],"openTabs":["src/App.kt","src/Other.kt"],"activeFile":"src/App.kt"}}""", + result, + ) + } + @Test fun `buildProviderOAuthJson - numeric method index`() { val result = KiloCliDataParser.buildProviderOAuthJson("0", mapOf("deploymentType" to "github.com")) diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/plugin/KiloPluginSettings.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/plugin/KiloPluginSettings.kt index 16641aab32..1a23440286 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/plugin/KiloPluginSettings.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/plugin/KiloPluginSettings.kt @@ -4,6 +4,7 @@ import com.intellij.ide.util.PropertiesComponent object KiloPluginSettings { private const val AUTO_APPROVE_KEY = "kilo.session.autoApprove" + private const val AUTO_EDITOR_CONTEXT_KEY = "kilo.session.autoEditorContext" private const val PERMISSION_RULES_EXPANDED_KEY = "kilo.session.permissionRulesExpanded" fun getAutoApprove(): Boolean = PropertiesComponent.getInstance().getBoolean(AUTO_APPROVE_KEY, false) @@ -16,6 +17,16 @@ object KiloPluginSettings { PropertiesComponent.getInstance().unsetValue(AUTO_APPROVE_KEY) } + fun getAutoEditorContext(): Boolean = PropertiesComponent.getInstance().getBoolean(AUTO_EDITOR_CONTEXT_KEY, true) + + fun setAutoEditorContext(value: Boolean) { + PropertiesComponent.getInstance().setValue(AUTO_EDITOR_CONTEXT_KEY, value.toString()) + } + + internal fun unsetAutoEditorContext() { + PropertiesComponent.getInstance().unsetValue(AUTO_EDITOR_CONTEXT_KEY) + } + fun getPermissionRulesExpanded(): Boolean = PropertiesComponent.getInstance().getBoolean(PERMISSION_RULES_EXPANDED_KEY, false) fun setPermissionRulesExpanded(value: Boolean) { diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt index a7b30a796c..7635c282e9 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt @@ -47,6 +47,7 @@ import ai.kilocode.client.session.ui.style.SessionEditorStyleTarget import ai.kilocode.client.session.controller.EVENT_FLUSH_MS import ai.kilocode.client.session.controller.SessionController import ai.kilocode.client.session.controller.SessionControllerEvent +import ai.kilocode.client.session.context.EditorContextGatherer import ai.kilocode.client.session.ui.style.SessionUiStyle import ai.kilocode.client.session.views.LoginRequiredView import ai.kilocode.client.session.views.permission.PermissionView @@ -682,14 +683,16 @@ class SessionUi( private fun sendPrompt(text: String, files: List) { if (text.isBlank() && files.isEmpty()) return + val editor = EditorContextGatherer.gather(project, workspace.directory) + val allFiles = files + listOfNotNull(editor.selection) val parts = buildList { text.takeIf { it.isNotBlank() }?.let { add(PromptPartDto(type = "text", text = it)) } - addAll(files) + addAll(allFiles) } LOG.debug { val agent = controller.model.agent ?: "none" val model = controller.model.model ?: "none" - "${ChatLogSummary.prompt(PromptDto(parts = parts))} agent=$agent model=$model ready=${controller.ready}" + "${ChatLogSummary.prompt(PromptDto(parts = parts, editorContext = editor.context))} agent=$agent model=$model ready=${controller.ready}" } prompt.clear() val follow = scroll.atBottom() @@ -705,7 +708,7 @@ class SessionUi( scroll.followBottom(follow) return } - controller.prompt(text, files) + controller.prompt(text, allFiles, editor.context) scroll.followBottom(follow) } diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/context/EditorContextGatherer.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/context/EditorContextGatherer.kt new file mode 100644 index 0000000000..747a663c21 --- /dev/null +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/context/EditorContextGatherer.kt @@ -0,0 +1,136 @@ +package ai.kilocode.client.session.context + +import ai.kilocode.client.plugin.KiloPluginSettings +import ai.kilocode.client.vfs.KiloVirtualFileSystem +import ai.kilocode.log.KiloLog +import ai.kilocode.rpc.dto.EditorContextDto +import ai.kilocode.rpc.dto.PromptPartDto +import com.intellij.codeWithMe.ClientId +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.fileEditor.FileDocumentManager +import com.intellij.openapi.fileEditor.FileEditorManager +import com.intellij.openapi.fileEditor.impl.EditorHistoryManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.SystemInfo +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.util.EnvironmentUtil +import java.nio.file.Path +import kotlin.io.path.name + +/** + * Reads the active/open editors and current selection at prompt-send time. + * + * Split mode caveat: the chat UI runs under a non-local [ClientId], so + * [FileEditorManager.getOpenFiles]/[FileEditorManager.getSelectedTextEditor] take + * the per-client branch and return nothing. The `*WithRemotes` variants read the + * local composites directly and are the ones that actually see the user's tabs. + * These APIs are `@ApiStatus.Experimental`. + */ +internal object EditorContextGatherer { + private val LOG = KiloLog.create(EditorContextGatherer::class.java) + + // Resolved once per process; the login shell does not change during a session. + private val shell: String? by lazy { + if (SystemInfo.isWindows) EnvironmentUtil.getValue("COMSPEC") else EnvironmentUtil.getValue("SHELL") + } + + data class Result( + val context: EditorContextDto?, + val selection: PromptPartDto?, + ) + + fun gather(project: Project, root: String): Result { + if (!KiloPluginSettings.getAutoEditorContext()) { + LOG.debug { "kind=editor-context enabled=false" } + return Result(null, null) + } + val manager = FileEditorManager.getInstance(project) + val base = Path.of(root).toAbsolutePath().normalize() + val openFiles = manager.openFilesWithRemotes + val editor = manager.selectedTextEditorWithRemotes.firstOrNull() + val activeFile = editor?.let { file(it) } ?: lastOpen(project, openFiles) ?: openFiles.firstOrNull() + val ignore = KiloIgnore.load(rootDir(listOfNotNull(activeFile) + openFiles, base)) + + fun keep(file: VirtualFile?): String? = rel(file, base)?.takeUnless { ignore.ignored(it) } + + val active = keep(activeFile) + val openRel = openFiles.mapNotNull { rel(it, base) }.distinct() + val open = openRel.filterNot { ignore.ignored(it) }.take(20) + val visible = (listOfNotNull(activeFile) + manager.selectedTextEditorWithRemotes.mapNotNull { file(it) }) + .mapNotNull { keep(it) } + .distinct() + .take(200) + val ctx = EditorContextDto( + activeFile = active, + openTabs = open.takeIf { it.isNotEmpty() }, + visibleFiles = visible.takeIf { it.isNotEmpty() }, + shell = shell, + ).takeIf { active != null || open.isNotEmpty() || visible.isNotEmpty() || shell != null } + val part = editor?.let { selection(it, base, ignore) } + LOG.debug { + val first = openFiles.firstOrNull() + val filtered = openRel.count { ignore.ignored(it) } + "kind=editor-context enabled=true localId=${ClientId.isCurrentlyUnderLocalId}" + + " rawOpen=${openFiles.size} rawSel=${manager.selectedTextEditorWithRemotes.size}" + + " active=${active ?: "none"} open=${open.size} visible=${visible.size} selection=${part != null}" + + " ignored=$filtered shell=${shell ?: "none"}" + + " firstFs=${first?.fileSystem?.protocol ?: "none"} firstLocal=${first?.isInLocalFileSystem ?: false}" + + " firstPath=${first?.path ?: "none"}" + } + return Result(ctx, part) + } + + private fun lastOpen(project: Project, open: List): VirtualFile? { + val set = open.toHashSet() + return EditorHistoryManager.getInstance(project).fileList.lastOrNull { it in set } + } + + // Walks up from an open editor file to the workspace-root directory so ignore + // files can be read via the same (possibly remote) VFS as the editor files. + private fun rootDir(files: List, root: Path): VirtualFile? { + for (file in files) { + var cur: VirtualFile? = file + while (cur != null) { + if (runCatching { Path.of(cur.path).toAbsolutePath().normalize() }.getOrNull() == root) return cur + cur = cur.parent + } + } + return null + } + + private fun selection(editor: Editor, root: Path, ignore: KiloIgnore): PromptPartDto? { + val model = editor.selectionModel + if (!model.hasSelection()) return null + val file = file(editor) ?: return null + val path = local(file, root) ?: return null + if (ignore.ignored(root.relativize(path).toString())) return null + val start = model.selectionStart + val end = model.selectionEnd + if (start == end) return null + val doc = editor.document + val last = (end - 1).coerceAtLeast(start) + val first = doc.getLineNumber(start) + 1 + val line = doc.getLineNumber(last) + 1 + val url = "${path.toUri()}?start=$first&end=$line" + return PromptPartDto( + type = "file", + mime = "text/plain", + url = url, + filename = path.name, + ) + } + + private fun file(editor: Editor): VirtualFile? = FileDocumentManager.getInstance().getFile(editor.document) + + private fun rel(file: VirtualFile?, root: Path): String? { + val path = file?.let { local(it, root) } ?: return null + return root.relativize(path).toString() + } + + private fun local(file: VirtualFile, root: Path): Path? { + if (file.fileSystem.protocol == KiloVirtualFileSystem.PROTOCOL) return null + val path = Path.of(file.path).toAbsolutePath().normalize() + if (!path.startsWith(root)) return null + return path + } +} diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/context/KiloIgnore.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/context/KiloIgnore.kt new file mode 100644 index 0000000000..bef689e556 --- /dev/null +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/context/KiloIgnore.kt @@ -0,0 +1,138 @@ +package ai.kilocode.client.session.context + +import com.intellij.openapi.vfs.VfsUtilCore +import com.intellij.openapi.vfs.VirtualFile + +/** + * Minimal `.gitignore`-style matcher used to keep ignored or sensitive files out + * of the editor context sent to the model. + * + * Mirrors the VS Code `FileIgnoreController` precedence: + * - if `.kilocodeignore` exists and is non-empty, use only its patterns (plus the + * `.kilocodeignore` file itself); + * - otherwise fall back to `.gitignore` plus the sensitive `.env` / `.env.*` + * patterns. + * + * Paths are matched as workspace-relative POSIX paths. Only the subset of gitignore + * syntax relevant to path filtering is supported: comments (`#`), blank lines, + * negation (`!`), anchoring (leading or embedded `/`), directory-only (trailing + * `/`), and the `*`, `**`, `?`, and `[..]` globs. + */ +internal class KiloIgnore private constructor(private val rules: List) { + + /** True when [path] (workspace-relative) should be excluded from editor context. */ + fun ignored(path: String): Boolean { + val norm = path.replace('\\', '/').trim('/') + if (norm.isEmpty()) return false + var hit = false + for (rule in rules) { + if (rule.regex.matches(norm)) hit = !rule.negate + } + return hit + } + + private class Rule(val regex: Regex, val negate: Boolean) + + companion object { + val EMPTY = KiloIgnore(emptyList()) + + private const val KILO = ".kilocodeignore" + private const val GIT = ".gitignore" + private val SENSITIVE = listOf(".env", ".env.*") + + /** + * Builds the matcher from the ignore files under [root]. Reads through the VFS + * so it works in remote/split mode where the workspace lives on the host. + * Returns [EMPTY] (allow-all) when [root] is null or unreadable; the backend + * permission layer still guards file contents. + */ + fun load(root: VirtualFile?): KiloIgnore { + if (root == null) return EMPTY + val kilo = read(root, KILO) + if (!kilo.isNullOrBlank()) return KiloIgnore(compile(kilo) + compile(KILO)) + val rules = mutableListOf() + read(root, GIT)?.takeIf { it.isNotBlank() }?.let { rules += compile(it) } + rules += SENSITIVE.mapNotNull { rule(it) } + return KiloIgnore(rules) + } + + /** Test seam: build a matcher directly from ignore-file text. */ + fun of(text: String): KiloIgnore = KiloIgnore(compile(text)) + + private fun read(root: VirtualFile, name: String): String? { + val file = root.findChild(name) ?: return null + if (!file.isValid || file.isDirectory) return null + return runCatching { VfsUtilCore.loadText(file) }.getOrNull() + } + + private fun compile(text: String): List = text.lineSequence().mapNotNull { rule(it) }.toList() + + private fun rule(raw: String): Rule? { + var line = raw.trimEnd() + if (line.isEmpty() || line.startsWith("#")) return null + val negate = line.startsWith("!") + if (negate) line = line.substring(1) + val dirOnly = line.endsWith("/") + if (dirOnly) line = line.trimEnd('/') + val leading = line.startsWith("/") + if (leading) line = line.trimStart('/') + if (line.isEmpty()) return null + val anchored = leading || line.contains('/') + val prefix = if (anchored) "" else "(?:.*/)?" + val suffix = if (dirOnly) "/.*" else "(?:/.*)?" + return Rule(Regex("^$prefix${glob(line)}$suffix$"), negate) + } + + private fun glob(glob: String): String { + val sb = StringBuilder() + var i = 0 + while (i < glob.length) { + val c = glob[i] + when (c) { + '\\' -> { + val next = glob.getOrNull(i + 1) + if (next == null) sb.append("\\\\") + else { + if (!next.isLetterOrDigit()) sb.append('\\') + sb.append(next) + i++ + } + } + + '*' -> { + if (glob.getOrNull(i + 1) == '*') { + i++ + if (glob.getOrNull(i + 1) == '/') { + sb.append("(?:.*/)?") + i++ + } else { + sb.append(".*") + } + } else { + sb.append("[^/]*") + } + } + + '?' -> sb.append("[^/]") + + '[' -> { + val end = glob.indexOf(']', i + 1) + if (end == -1) { + sb.append("\\[") + } else { + val body = glob.substring(i + 1, end) + sb.append('[').append(if (body.startsWith("!")) "^${body.substring(1)}" else body).append(']') + i = end + } + } + + '.', '(', ')', '+', '|', '^', '$', '{', '}', ']' -> sb.append('\\').append(c) + + else -> sb.append(c) + } + i++ + } + return sb.toString() + } + } +} diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/controller/SessionController.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/controller/SessionController.kt index a0f0013b98..0ae42b857b 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/controller/SessionController.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/controller/SessionController.kt @@ -31,6 +31,7 @@ import ai.kilocode.client.util.UiTimers import ai.kilocode.rpc.dto.ChatEventDto import ai.kilocode.rpc.dto.ConfigWarningDto import ai.kilocode.rpc.dto.ConfigUpdateDto +import ai.kilocode.rpc.dto.EditorContextDto import ai.kilocode.rpc.dto.PartDto import ai.kilocode.rpc.dto.KiloAppStatusDto import ai.kilocode.rpc.dto.KiloWorkspaceStatusDto @@ -261,11 +262,11 @@ class SessionController( } } - fun prompt(text: String, files: List = emptyList()) { + fun prompt(text: String, files: List = emptyList(), editorContext: EditorContextDto? = null) { assertEdt() val start = sid ?: ref?.key ?: "pending" val exists = sid != null - val dto = promptDto(text, files) + val dto = promptDto(text, files, editorContext) val props = promptProps(files) LOG.debug { "${ChatLogSummary.sid(start)} ${ChatLogSummary.prompt(dto)} ${ChatLogSummary.dir(directory)}" } dispatch(Dispatch("prompt", "user", text, props, start, exists)) { id -> @@ -1820,7 +1821,11 @@ class SessionController( } } - private fun promptDto(text: String, files: List = emptyList()): PromptDto { + private fun promptDto( + text: String, + files: List = emptyList(), + editorContext: EditorContextDto? = null, + ): PromptDto { val full = model.model val sel = full?.let(::parseModel) val variant = model.variant?.takeIf { it in model.variants } @@ -1834,6 +1839,7 @@ class SessionController( modelID = sel?.second, agent = model.agent, variant = variant, + editorContext = editorContext, ) } diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/settings/context/ContextSettingsUi.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/settings/context/ContextSettingsUi.kt index 7a66f2ac59..0118542137 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/settings/context/ContextSettingsUi.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/settings/context/ContextSettingsUi.kt @@ -3,6 +3,7 @@ package ai.kilocode.client.settings.context import ai.kilocode.client.app.KiloAppService import ai.kilocode.client.app.KiloWorkspaceService import ai.kilocode.client.plugin.KiloBundle +import ai.kilocode.client.plugin.KiloPluginSettings import ai.kilocode.client.settings.base.BaseContentPanel import ai.kilocode.client.settings.base.BaseSettingsUi import ai.kilocode.client.settings.base.SettingsBannerKind @@ -134,6 +135,9 @@ internal class ContextSettingsContent( private val update: (ContextDraft.() -> ContextDraft) -> Unit, ) : BaseContentPanel() { private val auto = SettingsToggle { value -> update { copy(auto = value) } } + private val editor = SettingsToggle(KiloPluginSettings.getAutoEditorContext()) { value -> + KiloPluginSettings.setAutoEditorContext(value) + } private val prune = SettingsToggle { value -> update { copy(prune = value) } } private val threshold = ThresholdField( KiloBundle.message("settings.context.compaction.threshold.placeholder"), @@ -163,6 +167,13 @@ internal class ContextSettingsContent( prune, )) } + section( + KiloBundle.message("settings.context.editor.title"), + ).row(SettingsRow( + KiloBundle.message("settings.context.editor.auto.title"), + KiloBundle.message("settings.context.editor.auto.description"), + editor, + )) section( KiloBundle.message("settings.context.watcher.title"), KiloBundle.message("settings.context.watcher.description"), @@ -172,10 +183,11 @@ internal class ContextSettingsContent( @RequiresEdt fun sync(draft: ContextDraft, enabled: Boolean) { auto.isSelected = draft.auto + editor.isSelected = KiloPluginSettings.getAutoEditorContext() prune.isSelected = draft.prune threshold.sync(draft.threshold) patterns.sync(draft.ignore) - listOf(auto, prune, threshold, patterns).forEach { it.isEnabled = enabled } + listOf(auto, editor, prune, threshold, patterns).forEach { it.isEnabled = enabled } } } diff --git a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle.properties b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle.properties index 074f594204..f03000a254 100644 --- a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle.properties +++ b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle.properties @@ -363,6 +363,9 @@ settings.context.compaction.threshold.suffix=% settings.context.compaction.threshold.invalid=Enter a number from 0 to 100, or leave the field blank. settings.context.compaction.prune.title=Prune Old Outputs settings.context.compaction.prune.description=Remove old tool outputs during compaction +settings.context.editor.title=Editor Context +settings.context.editor.auto.title=Auto-Include Editor Context +settings.context.editor.auto.description=Include the active file, open files, visible files, and selected text when sending chat messages. settings.context.watcher.title=File Watcher Ignore Patterns settings.context.watcher.description=Glob patterns for files the watcher should ignore settings.context.watcher.add=Add pattern diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/context/EditorContextGathererTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/context/EditorContextGathererTest.kt new file mode 100644 index 0000000000..2e7fd54949 --- /dev/null +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/context/EditorContextGathererTest.kt @@ -0,0 +1,91 @@ +package ai.kilocode.client.session.context + +import ai.kilocode.client.plugin.KiloPluginSettings +import com.intellij.openapi.fileEditor.FileEditorManager +import com.intellij.openapi.util.SystemInfo +import com.intellij.testFramework.fixtures.BasePlatformTestCase +import com.intellij.util.EnvironmentUtil +import com.intellij.util.ui.UIUtil + +class EditorContextGathererTest : BasePlatformTestCase() { + override fun tearDown() { + try { + KiloPluginSettings.unsetAutoEditorContext() + } finally { + super.tearDown() + } + } + + fun `test gather includes active open visible files and selected range`() { + val psi = myFixture.addFileToProject( + "src/App.kt", + "fun main() {\n println(\"hi\")\n println(\"bye\")\n}\n", + ) + val manager = FileEditorManager.getInstance(project) + manager.openFile(psi.virtualFile, true) + UIUtil.dispatchAllInvocationEvents() + val editor = manager.selectedTextEditor!! + val doc = editor.document + editor.selectionModel.setSelection(doc.getLineStartOffset(1), doc.getLineEndOffset(2)) + val root = psi.virtualFile.parent.parent.path + + val result = EditorContextGatherer.gather(project, root) + + assertEquals("src/App.kt", result.context?.activeFile) + assertEquals(listOf("src/App.kt"), result.context?.openTabs) + assertEquals(listOf("src/App.kt"), result.context?.visibleFiles) + assertEquals("text/plain", result.selection?.mime) + assertEquals("App.kt", result.selection?.filename) + assertTrue(result.selection?.url, result.selection?.url.orEmpty().contains("/src/App.kt?start=2&end=3")) + val expectedShell = if (SystemInfo.isWindows) EnvironmentUtil.getValue("COMSPEC") else EnvironmentUtil.getValue("SHELL") + assertEquals(expectedShell, result.context?.shell) + } + + fun `test gather filters kilocodeignore files from open tabs`() { + val app = myFixture.addFileToProject("src/App.kt", "fun main() {}") + val secret = myFixture.addFileToProject("ignored/Secret.kt", "val token = 1") + myFixture.addFileToProject(".kilocodeignore", "ignored/\n") + val manager = FileEditorManager.getInstance(project) + manager.openFile(secret.virtualFile, true) + manager.openFile(app.virtualFile, true) + UIUtil.dispatchAllInvocationEvents() + val root = app.virtualFile.parent.parent.path + + val result = EditorContextGatherer.gather(project, root) + + assertEquals("src/App.kt", result.context?.activeFile) + assertEquals(listOf("src/App.kt"), result.context?.openTabs) + assertEquals(listOf("src/App.kt"), result.context?.visibleFiles) + } + + fun `test gather drops selection when active file is ignored`() { + val secret = myFixture.addFileToProject("ignored/Secret.kt", "val token = 1\nval other = 2\n") + myFixture.addFileToProject(".kilocodeignore", "ignored/\n") + val manager = FileEditorManager.getInstance(project) + manager.openFile(secret.virtualFile, true) + UIUtil.dispatchAllInvocationEvents() + val editor = manager.selectedTextEditor!! + val doc = editor.document + editor.selectionModel.setSelection(doc.getLineStartOffset(0), doc.getLineEndOffset(0)) + val root = secret.virtualFile.parent.parent.path + + val result = EditorContextGatherer.gather(project, root) + + assertNull(result.context?.activeFile) + assertNull(result.context?.openTabs) + assertNull(result.context?.visibleFiles) + assertNull(result.selection) + } + + fun `test gather returns empty when setting is off`() { + KiloPluginSettings.setAutoEditorContext(false) + val psi = myFixture.addFileToProject("src/App.kt", "fun main() {}") + FileEditorManager.getInstance(project).openFile(psi.virtualFile, true) + UIUtil.dispatchAllInvocationEvents() + + val result = EditorContextGatherer.gather(project, psi.virtualFile.parent.parent.path) + + assertNull(result.context) + assertNull(result.selection) + } +} diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/context/KiloIgnoreTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/context/KiloIgnoreTest.kt new file mode 100644 index 0000000000..fbe71fa5a8 --- /dev/null +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/context/KiloIgnoreTest.kt @@ -0,0 +1,89 @@ +package ai.kilocode.client.session.context + +import junit.framework.TestCase + +class KiloIgnoreTest : TestCase() { + fun `test empty allows everything`() { + val ignore = KiloIgnore.of("") + assertFalse(ignore.ignored("src/App.kt")) + assertFalse(ignore.ignored(".env")) + } + + fun `test basename matches at any depth`() { + val ignore = KiloIgnore.of("foo") + assertTrue(ignore.ignored("foo")) + assertTrue(ignore.ignored("a/b/foo")) + assertTrue(ignore.ignored("foo/child.txt")) + assertFalse(ignore.ignored("a/foobar")) + } + + fun `test extension glob`() { + val ignore = KiloIgnore.of("*.log") + assertTrue(ignore.ignored("a.log")) + assertTrue(ignore.ignored("nested/dir/a.log")) + assertFalse(ignore.ignored("a.log.kt")) + } + + fun `test directory only pattern matches contents`() { + val ignore = KiloIgnore.of("node_modules/") + assertTrue(ignore.ignored("node_modules/pkg/index.js")) + assertTrue(ignore.ignored("a/node_modules/pkg.js")) + assertFalse(ignore.ignored("node_modules")) + } + + fun `test leading slash anchors to root`() { + val ignore = KiloIgnore.of("/build") + assertTrue(ignore.ignored("build/out.js")) + assertFalse(ignore.ignored("src/build/out.js")) + } + + fun `test middle slash anchors to root`() { + val ignore = KiloIgnore.of("src/generated") + assertTrue(ignore.ignored("src/generated/A.kt")) + assertFalse(ignore.ignored("app/src/generated/A.kt")) + } + + fun `test double star matches across directories`() { + val ignore = KiloIgnore.of("**/dist") + assertTrue(ignore.ignored("dist/a.js")) + assertTrue(ignore.ignored("a/b/dist/a.js")) + + val nested = KiloIgnore.of("src/**/*.tmp") + assertTrue(nested.ignored("src/a/b/c.tmp")) + assertTrue(nested.ignored("src/x.tmp")) + assertFalse(nested.ignored("lib/a.tmp")) + } + + fun `test negation re-includes`() { + val ignore = KiloIgnore.of("*.log\n!keep.log") + assertTrue(ignore.ignored("debug.log")) + assertFalse(ignore.ignored("keep.log")) + } + + fun `test comments and blank lines ignored`() { + val ignore = KiloIgnore.of("# a comment\n\n*.secret\n") + assertTrue(ignore.ignored("api.secret")) + assertFalse(ignore.ignored("# a comment")) + } + + fun `test sensitive env patterns`() { + val ignore = KiloIgnore.of(".env\n.env.*") + assertTrue(ignore.ignored(".env")) + assertTrue(ignore.ignored(".env.local")) + assertTrue(ignore.ignored("cfg/.env.production")) + assertFalse(ignore.ignored("env")) + assertFalse(ignore.ignored("environment.ts")) + } + + fun `test char class`() { + val ignore = KiloIgnore.of("*.[oa]") + assertTrue(ignore.ignored("main.o")) + assertTrue(ignore.ignored("lib.a")) + assertFalse(ignore.ignored("main.c")) + } + + fun `test backslash separators normalized`() { + val ignore = KiloIgnore.of("node_modules/") + assertTrue(ignore.ignored("a\\node_modules\\pkg.js")) + } +} diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/controller/EditorContextPromptTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/controller/EditorContextPromptTest.kt new file mode 100644 index 0000000000..53cd3e1b05 --- /dev/null +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/controller/EditorContextPromptTest.kt @@ -0,0 +1,30 @@ +package ai.kilocode.client.session.controller + +import ai.kilocode.rpc.dto.EditorContextDto +import ai.kilocode.rpc.dto.PromptPartDto +import kotlin.test.assertEquals + +class EditorContextPromptTest : SessionControllerTestBase() { + fun `test prompt forwards editor context`() { + val (c, _, _) = prompted() + rpc.prompts.clear() + val ctx = EditorContextDto( + activeFile = "src/App.kt", + openTabs = listOf("src/App.kt"), + visibleFiles = listOf("src/App.kt"), + ) + val file = PromptPartDto( + type = "file", + mime = "text/plain", + url = "file:///test/src/App.kt?start=2&end=3", + filename = "App.kt", + ) + + edt { c.prompt("explain", listOf(file), ctx) } + flush() + + val prompt = rpc.prompts.single().third + assertEquals(ctx, prompt.editorContext) + assertEquals(file, prompt.parts.first { it.type == "file" }) + } +} diff --git a/packages/kilo-jetbrains/shared/src/main/kotlin/ai/kilocode/log/ChatLogSummary.kt b/packages/kilo-jetbrains/shared/src/main/kotlin/ai/kilocode/log/ChatLogSummary.kt index 588c0c9d0e..c6e1f3cc34 100644 --- a/packages/kilo-jetbrains/shared/src/main/kotlin/ai/kilocode/log/ChatLogSummary.kt +++ b/packages/kilo-jetbrains/shared/src/main/kotlin/ai/kilocode/log/ChatLogSummary.kt @@ -72,10 +72,22 @@ object ChatLogSummary { prompt.agent?.takeIf { it.isNotBlank() }?.let { out += "agent=$it" } model(prompt.providerID, prompt.modelID)?.let { out += "model=$it" } prompt.variant?.takeIf { it.isNotBlank() }?.let { out += "variant=$it" } + prompt.editorContext?.let { ctx -> + out += "editorContext=true" + ctx.activeFile?.let { file -> out += editorFile("activeFile", file) } + ctx.openTabs?.size?.takeIf { it > 0 }?.let { out += "openTabs=$it" } + ctx.visibleFiles?.size?.takeIf { it > 0 }?.let { out += "visibleFiles=$it" } + ctx.shell?.takeIf { it.isNotBlank() }?.let { out += "shell=$it" } + } preview(text)?.let { out += "preview=\"$it\"" } return out.joinToString(" ") } + private fun editorFile(key: String, file: String): String { + if (mode() == Mode.OFF) return "${key}Hash=${hash(file)}" + return "$key=\"${clean(file)}\"" + } + fun history(items: List): String { val out = mutableListOf() val parts = items.sumOf { it.parts.size } diff --git a/packages/kilo-jetbrains/shared/src/main/kotlin/ai/kilocode/rpc/dto/ChatDto.kt b/packages/kilo-jetbrains/shared/src/main/kotlin/ai/kilocode/rpc/dto/ChatDto.kt index ecf0da6a41..5e21bfdd98 100644 --- a/packages/kilo-jetbrains/shared/src/main/kotlin/ai/kilocode/rpc/dto/ChatDto.kt +++ b/packages/kilo-jetbrains/shared/src/main/kotlin/ai/kilocode/rpc/dto/ChatDto.kt @@ -122,6 +122,17 @@ data class PromptDto( val agent: String? = null, val variant: String? = null, val noReply: Boolean? = null, + val editorContext: EditorContextDto? = null, +) + +@Serializable +data class EditorContextDto( + val directory: String? = null, + val worktree: String? = null, + val visibleFiles: List? = null, + val openTabs: List? = null, + val activeFile: String? = null, + val shell: String? = null, ) @Serializable From 74470aa8611cdb48e3dc6c2e0deaa027b9af46f9 Mon Sep 17 00:00:00 2001 From: kirillk Date: Fri, 7 Aug 2026 17:54:40 -0400 Subject: [PATCH 02/14] fix(jetbrains): render prompt attachments as chips --- .changeset/jetbrains-prompt-attachments.md | 5 ++ .../backend/rpc/KiloWorkspaceRpcApiImpl.kt | 26 ++++++- .../client/app/KiloWorkspaceService.kt | 8 +-- .../client/session/SessionFileLinks.kt | 11 +-- .../ai/kilocode/client/session/SessionUi.kt | 15 +++- .../kilocode/client/session/model/Message.kt | 2 + .../client/session/model/SessionModel.kt | 20 ++++++ .../session/ui/attachment/AttachmentCard.kt | 72 +++++++++++++++++++ .../client/session/ui/style/SessionUiStyle.kt | 3 + .../client/session/views/AttachmentView.kt | 35 +++++++-- .../client/session/views/MessageView.kt | 64 +++++++++++------ .../session/views/PromptAttachmentView.kt | 30 +++++--- .../resources/messages/KiloBundle.properties | 2 + .../client/session/SessionFileLinksTest.kt | 14 +++- .../session/ui/SessionMessageListPanelTest.kt | 9 ++- .../client/session/ui/SessionUiUpdateTest.kt | 40 +++++++++-- .../client/testing/FakeWorkspaceRpcApi.kt | 6 +- .../ai/kilocode/rpc/KiloWorkspaceRpcApi.kt | 2 +- 18 files changed, 299 insertions(+), 65 deletions(-) create mode 100644 .changeset/jetbrains-prompt-attachments.md diff --git a/.changeset/jetbrains-prompt-attachments.md b/.changeset/jetbrains-prompt-attachments.md new file mode 100644 index 0000000000..ac1f1f117e --- /dev/null +++ b/.changeset/jetbrains-prompt-attachments.md @@ -0,0 +1,5 @@ +--- +"@kilocode/kilo-jetbrains": patch +--- + +Render prompt attachments inside the sent message bubble with file chips, image previews, and selection-aware file opening. diff --git a/packages/kilo-jetbrains/backend/src/main/kotlin/ai/kilocode/backend/rpc/KiloWorkspaceRpcApiImpl.kt b/packages/kilo-jetbrains/backend/src/main/kotlin/ai/kilocode/backend/rpc/KiloWorkspaceRpcApiImpl.kt index 8140b86bff..14f9cbe12d 100644 --- a/packages/kilo-jetbrains/backend/src/main/kotlin/ai/kilocode/backend/rpc/KiloWorkspaceRpcApiImpl.kt +++ b/packages/kilo-jetbrains/backend/src/main/kotlin/ai/kilocode/backend/rpc/KiloWorkspaceRpcApiImpl.kt @@ -26,6 +26,8 @@ import com.intellij.execution.process.CapturingProcessHandler import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ModalityState import com.intellij.openapi.components.service +import com.intellij.openapi.editor.ScrollType +import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.openapi.fileEditor.OpenFileDescriptor import com.intellij.openapi.project.Project import com.intellij.openapi.project.ProjectManager @@ -288,7 +290,7 @@ class KiloWorkspaceRpcApiImpl internal constructor( }.ifBlank { null } } - override suspend fun openFile(path: String, line: Int?, column: Int?): Boolean { + override suspend fun openFile(path: String, line: Int?, column: Int?, endLine: Int?): Boolean { val item = clean(path) ?: return false val target = file(item)?.takeIf { it.isAbsolute } ?: return false val vf = LocalFileSystem.getInstance().refreshAndFindFileByPath(target.toString()) ?: return false @@ -296,7 +298,7 @@ class KiloWorkspaceRpcApiImpl internal constructor( LOG.warn("No project available to open file: $path") return false } - navigate(project, vf, line, column) + navigate(project, vf, line, column, endLine) return true } @@ -374,8 +376,26 @@ class KiloWorkspaceRpcApiImpl internal constructor( null } - private suspend fun navigate(project: Project, file: VirtualFile, line: Int? = null, column: Int? = null) = suspendCancellableCoroutine { cont -> + private suspend fun navigate(project: Project, file: VirtualFile, line: Int? = null, column: Int? = null, endLine: Int? = null) = suspendCancellableCoroutine { cont -> ApplicationManager.getApplication().invokeLater({ + if (line != null && endLine != null) { + val editor = FileEditorManager.getInstance(project).openTextEditor( + OpenFileDescriptor(project, file, (line - 1).coerceAtLeast(0), 0), + true, + ) + val doc = editor?.document + if (editor != null && doc != null && doc.lineCount > 0) { + val start = (line - 1).coerceIn(0, doc.lineCount - 1) + val end = (endLine - 1).coerceIn(start, doc.lineCount - 1) + val from = doc.getLineStartOffset(start) + val to = doc.getLineEndOffset(end) + editor.selectionModel.setSelection(from, to) + editor.caretModel.moveToOffset(from) + editor.scrollingModel.scrollToCaret(ScrollType.CENTER) + } + if (cont.isActive) cont.resume(Unit) + return@invokeLater + } val descriptor = if (line == null) { OpenFileDescriptor(project, file) } else { diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/app/KiloWorkspaceService.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/app/KiloWorkspaceService.kt index 66c3625bd6..33d628d2a2 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/app/KiloWorkspaceService.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/app/KiloWorkspaceService.kt @@ -179,19 +179,19 @@ class KiloWorkspaceService internal constructor( } } - suspend fun openPath(directory: String, path: String, line: Int? = null, column: Int? = null): Boolean { + suspend fun openPath(directory: String, path: String, line: Int? = null, column: Int? = null, endLine: Int? = null): Boolean { val match = files(directory, path).firstOrNull() ?: return false return try { - call { openFile(match.path, line, column) } + call { openFile(match.path, line, column, endLine) } } catch (e: Exception) { LOG.warn("workspace file open failed for path=${match.path}", e) false } } - suspend fun openFile(path: String, line: Int? = null, column: Int? = null): Boolean { + suspend fun openFile(path: String, line: Int? = null, column: Int? = null, endLine: Int? = null): Boolean { return try { - call { openFile(path, line, column) } + call { openFile(path, line, column, endLine) } } catch (e: Exception) { LOG.warn("workspace file open failed for path=$path", e) false diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionFileLinks.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionFileLinks.kt index daee52fea8..88f3804098 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionFileLinks.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionFileLinks.kt @@ -59,7 +59,7 @@ class SessionFileLinks( } val target = parse(href) scope.launch { - val ok = service.openPath(dir, target.path, target.line, target.column) + val ok = service.openPath(dir, target.path, target.line, target.column, target.endLine) if (ok) { track(target, "direct") return@launch @@ -72,7 +72,7 @@ class SessionFileLinks( when (val result = decide(false, found)) { Resolution.Opened -> Unit is Resolution.OpenDirect -> { - val opened = service.openPath(dir, result.file.path, target.line, target.column) + val opened = service.openPath(dir, result.file.path, target.line, target.column, target.endLine) track(target, if (opened) "search_direct" else "missing") } is Resolution.Choose -> { @@ -104,7 +104,7 @@ class SessionFileLinks( .createPopupChooserBuilder(files) .setRenderer(FileRenderer()) .setItemChosenCallback { file -> - scope.launch { service.openPath(dir, file.path, target.line, target.column) } + scope.launch { service.openPath(dir, file.path, target.line, target.column, target.endLine) } } .createPopup() popup.show(anchor ?: RelativePoint.getCenterOf(root)) @@ -143,11 +143,11 @@ class SessionFileLinks( data object Missing : Resolution } - data class Target(val path: String, val line: Int? = null, val column: Int? = null) + data class Target(val path: String, val line: Int? = null, val column: Int? = null, val endLine: Int? = null) companion object { private const val FILE_SEARCH_LIMIT = 50 - private val LINE = Regex(":(\\d+)(?:-\\d+)?(?::(\\d+))?$") + private val LINE = Regex(":(\\d+)(?:-(\\d+))?(?::(\\d+))?$") private val SCHEME = Regex("^([A-Za-z][A-Za-z0-9+.-]*):") fun parse(href: String): Target { @@ -155,6 +155,7 @@ class SessionFileLinks( return Target( href.substring(0, match.range.first), match.groupValues[1].toIntOrNull(), + match.groupValues.getOrNull(3)?.takeIf { it.isNotBlank() }?.toIntOrNull(), match.groupValues.getOrNull(2)?.takeIf { it.isNotBlank() }?.toIntOrNull(), ) } diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt index 7635c282e9..4c7df90e8a 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt @@ -921,12 +921,13 @@ class SessionUi( return } if (uri.scheme == "file") { - val path = runCatching { Path.of(uri).toString() }.getOrNull() ?: run { + val path = runCatching { Path.of(cleanAttachmentUri(uri)).toString() }.getOrNull() ?: run { LOG.info("kind=attachment-open skipped=true reason=invalid-file-uri message=$messageId part=${item.id} url=${attachmentUrl(url)}") return } - LOG.info("kind=attachment-open route=file session=${controller.id ?: "none"} message=$messageId part=${item.id} path=$path") - fileLinks.open(path, null) + val target = attachmentHref(path, item) + LOG.info("kind=attachment-open route=file session=${controller.id ?: "none"} message=$messageId part=${item.id} path=$target") + fileLinks.open(target, null) return } LOG.info("kind=attachment-open route=browser session=${controller.id ?: "none"} message=$messageId part=${item.id} url=${attachmentUrl(url)}") @@ -937,6 +938,14 @@ class SessionUi( ?: item.url.substringBefore(',').substringAfterLast('/').takeIf { it.isNotBlank() } ?: "attachment" + private fun attachmentHref(path: String, item: FileAttachment): String { + val start = item.startLine ?: return path + val end = item.endLine ?: start + return "$path:$start-$end" + } + + private fun cleanAttachmentUri(uri: URI): URI = URI(uri.scheme, uri.authority, uri.path, null, null) + private fun attachmentUrl(url: String): String { val scheme = url.substringBefore(':', missingDelimiterValue = "none") return "scheme=$scheme chars=${url.length} embedded=${isEmbeddedAttachment(url)}" diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/model/Message.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/model/Message.kt index beee188bd7..225b88c6bb 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/model/Message.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/model/Message.kt @@ -73,6 +73,8 @@ class FileAttachment(id: String) : Content(id) { var url: String = "" var filename: String? = null var source: PartSourceDto? = null + var startLine: Int? = null + var endLine: Int? = null } /** Tool invocation with lifecycle state. */ diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/model/SessionModel.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/model/SessionModel.kt index 1d918c70bc..53878cd75f 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/model/SessionModel.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/model/SessionModel.kt @@ -498,6 +498,9 @@ class SessionModel { existing.url = dto.url ?: "" existing.filename = dto.filename existing.source = dto.source + val range = range(existing.url) + existing.startLine = range?.first + existing.endLine = range?.last } is Tool -> { val old = existing.childSessionId @@ -552,6 +555,9 @@ class SessionModel { url = dto.url ?: "" filename = dto.filename source = dto.source + val range = range(url) + startLine = range?.first + endLine = range?.last } "tool" -> Tool(dto.id, dto.tool ?: "unknown", toolKind(dto.tool)).apply { messageID = dto.messageID @@ -581,6 +587,20 @@ class SessionModel { for (l in listeners) l.onEvent(event) } + private fun range(url: String): IntRange? { + val query = runCatching { java.net.URI.create(url).rawQuery }.getOrNull() ?: return null + val args = query.split('&') + .mapNotNull { + val index = it.indexOf('=') + if (index < 0) return@mapNotNull null + it.substring(0, index) to it.substring(index + 1) + } + .toMap() + val start = args["start"]?.toIntOrNull()?.takeIf { it > 0 } ?: return null + val end = args["end"]?.toIntOrNull()?.takeIf { it >= start } ?: start + return start..end + } + private fun trackChild(messageId: String, content: Content) { val tool = content as? Tool ?: return val child = tool.childSessionId ?: return diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/attachment/AttachmentCard.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/attachment/AttachmentCard.kt index 2ae5cdc759..f433862985 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/attachment/AttachmentCard.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/attachment/AttachmentCard.kt @@ -47,6 +47,78 @@ data class AttachmentCardItem( val path: Path? = null, ) +class AttachmentChip( + private val item: AttachmentCardItem, + private val file: Boolean, + private val startLine: Int? = null, + private val endLine: Int? = null, + open: (() -> Unit)? = null, +) : JPanel(BorderLayout()) { + private val tip = tooltip(item) + private val open = open?.let { callback -> + object : MouseAdapter() { + override fun mouseClicked(e: MouseEvent) { + callback() + } + } + } + + init { + isOpaque = false + border = JBUI.Borders.empty(0, JBUI.scale(SessionUiStyle.View.Attachment.CHIP_HORIZONTAL_PADDING)) + cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR) + toolTipText = tip + accessibleContext?.accessibleName = KiloBundle.message("prompt.attachment.open", item.name) + val label = JBLabel(label()).apply { + icon = attachmentIcon(item.mime, item.name) + iconTextGap = JBUI.scale(SessionUiStyle.View.Attachment.CHIP_ICON_GAP) + toolTipText = tip + } + add(label, BorderLayout.CENTER) + watch(this) + } + + override fun getPreferredSize(): Dimension { + val size = super.getPreferredSize() + return Dimension(size.width, JBUI.scale(SessionUiStyle.View.Attachment.CHIP_HEIGHT)) + } + + override fun getMinimumSize(): Dimension = preferredSize + + override fun paintComponent(g: Graphics) { + val g2 = g.create() as Graphics2D + try { + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON) + val arc = JBUI.scale(SessionUiStyle.View.Attachment.CORNER_ARC) + g2.color = SessionUiStyle.View.Surface.bgColor() + g2.fillRoundRect(0, 0, width, height, arc, arc) + g2.color = SessionUiStyle.View.Outline.color() + g2.drawRoundRect(0, 0, width - 1, height - 1, arc, arc) + } finally { + g2.dispose() + } + super.paintComponent(g) + } + + private fun label(): String { + val start = startLine + val end = endLine + if (file && start != null && end != null) return KiloBundle.message("session.attachment.file.range", item.name, start, end) + if (file) return item.name + return KiloBundle.message("session.attachment.unknown", item.mime.ifBlank { "unknown" }) + } + + private fun watch(node: Component) { + if (node is JComponent) node.toolTipText = tip + open?.let { + node.removeMouseListener(it) + node.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR) + node.addMouseListener(it) + } + if (node is Container) node.components.forEach(::watch) + } +} + open class AttachmentCard( private val item: AttachmentCardItem, remove: (() -> Unit)? = null, diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/style/SessionUiStyle.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/style/SessionUiStyle.kt index 82cdcd4032..aa8ce97a96 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/style/SessionUiStyle.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/style/SessionUiStyle.kt @@ -121,6 +121,9 @@ object SessionUiStyle { const val CARD_HEIGHT = 59 const val CLOSE_SIZE = 18 const val CORNER_ARC = 8 + const val CHIP_HEIGHT = 28 + const val CHIP_HORIZONTAL_PADDING = 8 + const val CHIP_ICON_GAP = 6 } /** Full-session file drop overlay geometry and colors. */ diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/AttachmentView.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/AttachmentView.kt index 95de423700..7f21a7acf2 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/AttachmentView.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/AttachmentView.kt @@ -6,12 +6,14 @@ import ai.kilocode.client.session.model.Content import ai.kilocode.client.session.model.FileAttachment import ai.kilocode.client.session.ui.attachment.AttachmentCard import ai.kilocode.client.session.ui.attachment.AttachmentCardItem +import ai.kilocode.client.session.ui.attachment.AttachmentChip import ai.kilocode.client.session.views.base.PartView import ai.kilocode.client.ui.UiStyle import com.intellij.util.ui.JBUI import java.awt.FlowLayout import java.net.URI import java.nio.file.Path +import javax.swing.JComponent class AttachmentView( private var item: FileAttachment, @@ -48,20 +50,31 @@ class AttachmentView( override fun dumpLabel(): String = "AttachmentView#${item.id}:${name(item)}" - private fun chip(item: FileAttachment) = AttachmentCard( - AttachmentCardItem(name(item), item.mime, item.url), - open = { openAttachment(item) }, - ) + private fun chip(item: FileAttachment): JComponent { + val card = AttachmentCardItem(name(item), item.mime, item.url) + if (item.mime.startsWith("image/")) return AttachmentCard(card, open = { openAttachment(item) }) + return AttachmentChip(card, file = file(item), startLine = item.startLine, endLine = item.endLine, open = { openAttachment(item) }) + } - private fun same(next: FileAttachment) = item.mime == next.mime && item.url == next.url && item.filename == next.filename + private fun same(next: FileAttachment) = item.mime == next.mime && + item.url == next.url && + item.filename == next.filename && + item.startLine == next.startLine && + item.endLine == next.endLine + + private fun file(item: FileAttachment): Boolean { + if (item.source?.path?.isNotBlank() == true) return true + val uri = runCatching { URI.create(item.url) }.getOrNull() ?: return false + return uri.scheme == "file" + } companion object { fun openDefault(item: FileAttachment, openFile: SessionFileOpener, openUrl: (String) -> Unit) { val url = item.url.takeIf { it.isNotBlank() } ?: return val uri = runCatching { URI.create(url) }.getOrNull() ?: return if (uri.scheme == "file") { - val path = runCatching { Path.of(uri).toString() }.getOrNull() ?: return - openFile(path, null) + val path = runCatching { Path.of(clean(uri)).toString() }.getOrNull() ?: return + openFile(href(path, item), null) return } if (SessionFileLinks.isFileHref(url)) { @@ -70,6 +83,14 @@ class AttachmentView( } openUrl(url) } + + private fun href(path: String, item: FileAttachment): String { + val start = item.startLine ?: return path + val end = item.endLine ?: start + return "$path:$start-$end" + } + + private fun clean(uri: URI): URI = URI(uri.scheme, uri.authority, uri.path, null, null) } private fun name(item: FileAttachment) = item.filename?.takeIf { it.isNotBlank() } diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/MessageView.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/MessageView.kt index c4ae7dc75b..3aa839f805 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/MessageView.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/MessageView.kt @@ -212,12 +212,11 @@ class MessageView( } } val view = view(content) - val item = wrapPrompt(view) view.resize = resize view.hover = hover view.applyStyle(style) parts[content.id] = view - add(item) + wrapPrompt(view)?.let { add(it) } } @RequiresEdt @@ -227,7 +226,9 @@ class MessageView( it.hover = hover it.applyStyle(style) attachments = it - add(it) + val node = ensurePromptWrap() + promptBox?.add(it, BorderLayout.SOUTH) + if (node.parent == null) add(node) } view.upsert(content) parts[content.id] = view @@ -253,20 +254,19 @@ class MessageView( @RequiresEdt private fun replacePart(content: Content, existing: PartView) { - val at = components.indexOfFirst { it === existing }.takeIf { it >= 0 } ?: componentCount + val at = components.indexOfFirst { it === existing || it === wrap }.takeIf { it >= 0 } ?: componentCount parts.remove(content.id) aliases.values.removeAll { it == content.id } sources.keys.removeAll { it !in aliases } - detach(existing) - remove(existing) + removeView(existing) + if (existing === prompt) prompt = null Disposer.dispose(existing) val view = view(content) - val item = wrapPrompt(view) view.resize = resize view.hover = hover view.applyStyle(style) parts[content.id] = view - add(item, at) + wrapPrompt(view)?.let { add(it, at) } syncBorder() refresh() } @@ -294,10 +294,11 @@ class MessageView( } aliases.values.removeAll { it == contentId } sources.keys.removeAll { it !in aliases } - detach(view) - remove(view) + removeView(view) Disposer.dispose(view) + if (view === prompt) prompt = null syncBorder() + syncPromptWrap() refresh() return true } @@ -325,10 +326,10 @@ class MessageView( @RequiresEdt private fun rebuildParts() { parts.values.distinct().forEach { - detach(it) - remove(it) + removeView(it) Disposer.dispose(it) } + wrap?.let { remove(it) } parts.clear() aliases.clear() sources.clear() @@ -442,10 +443,10 @@ class MessageView( @RequiresEdt override fun dispose() { parts.values.forEach { - detach(it) - remove(it) + removeView(it) Disposer.dispose(it) } + wrap?.let { remove(it) } parts.clear() aliases.clear() sources.clear() @@ -502,19 +503,42 @@ class MessageView( } @RequiresEdt - private fun wrapPrompt(view: PartView): JComponent { + private fun removeView(view: PartView) { + detach(view) + view.parent?.remove(view) + } + + @RequiresEdt + private fun wrapPrompt(view: PartView): JComponent? { if (role != SessionUiStyle.View.Message.USER_ROLE) return view if (view !is PromptView) return view prompt = view + val node = ensurePromptWrap() + val box = promptBox ?: return node + if (view.parent !== box) box.add(view, BorderLayout.CENTER) + node.bar.setActive(true) + return node.takeIf { it.parent == null } + } + + @RequiresEdt + private fun ensurePromptWrap(): PromptWrap { + val existing = wrap + if (existing != null) return existing val box = JPanel(BorderLayout()).also { it.isOpaque = false - it.add(view, BorderLayout.CENTER) promptBox = it } - val node = PromptWrap(box) - wrap = node - node.bar.setActive(true) - return node + return PromptWrap(box).also { wrap = it } + } + + @RequiresEdt + private fun syncPromptWrap() { + val node = wrap ?: return + val box = promptBox ?: return + if (box.componentCount > 0) return + node.parent?.remove(node) + wrap = null + promptBox = null } private inner class PromptWrap( diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/PromptAttachmentView.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/PromptAttachmentView.kt index a01148a25f..fb15e0e1d2 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/PromptAttachmentView.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/PromptAttachmentView.kt @@ -4,6 +4,7 @@ import ai.kilocode.client.session.model.Content import ai.kilocode.client.session.model.FileAttachment import ai.kilocode.client.session.ui.attachment.AttachmentCard import ai.kilocode.client.session.ui.attachment.AttachmentCardItem +import ai.kilocode.client.session.ui.attachment.AttachmentChip import ai.kilocode.client.session.ui.style.SessionUiStyle import ai.kilocode.client.session.views.base.PartView import ai.kilocode.client.ui.UiStyle @@ -12,6 +13,8 @@ import com.intellij.ui.components.JBScrollPane import com.intellij.util.concurrency.annotations.RequiresEdt import com.intellij.util.ui.JBUI import java.awt.Dimension +import java.net.URI +import javax.swing.JComponent import javax.swing.ScrollPaneConstants class PromptAttachmentView( @@ -21,7 +24,7 @@ class PromptAttachmentView( override val contentId: String = "attachments:$messageId" private val items = LinkedHashMap() - private val cards = LinkedHashMap() + private val cards = LinkedHashMap() private val row = Stack.horizontal(gap = UiStyle.Gap.sm()) private val scroll = JBScrollPane(row).apply { border = null @@ -35,9 +38,9 @@ class PromptAttachmentView( isOpaque = false border = JBUI.Borders.empty( 0, - JBUI.scale(SessionUiStyle.View.Prompt.SHELL_HORIZONTAL_PADDING), + 0, JBUI.scale(SessionUiStyle.View.Prompt.SHELL_VERTICAL_PADDING), - JBUI.scale(SessionUiStyle.View.Prompt.SHELL_HORIZONTAL_PADDING), + 0, ) add(scroll) } @@ -111,12 +114,23 @@ class PromptAttachmentView( repaint() } - private fun card(item: FileAttachment) = AttachmentCard( - AttachmentCardItem(name(item), item.mime, item.url), - open = { openAttachment(item) }, - ) + private fun card(item: FileAttachment): JComponent { + val card = AttachmentCardItem(name(item), item.mime, item.url) + if (item.mime.startsWith("image/")) return AttachmentCard(card, open = { openAttachment(item) }) + return AttachmentChip(card, file = file(item), startLine = item.startLine, endLine = item.endLine, open = { openAttachment(item) }) + } - private fun same(a: FileAttachment, b: FileAttachment) = a.mime == b.mime && a.url == b.url && a.filename == b.filename + private fun same(a: FileAttachment, b: FileAttachment) = a.mime == b.mime && + a.url == b.url && + a.filename == b.filename && + a.startLine == b.startLine && + a.endLine == b.endLine + + private fun file(item: FileAttachment): Boolean { + if (item.source?.path?.isNotBlank() == true) return true + val uri = runCatching { URI.create(item.url) }.getOrNull() ?: return false + return uri.scheme == "file" + } private fun bar() = scroll.horizontalScrollBar.preferredSize.height diff --git a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle.properties b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle.properties index f03000a254..2c1c665a0a 100644 --- a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle.properties +++ b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle.properties @@ -227,6 +227,8 @@ session.attachment.unsupported=Cannot preview {0} session.attachment.mime=Type: {0} session.attachment.size=Size: {0} bytes session.attachment.error=Failed to load attachment: {0} +session.attachment.file.range={0}:{1}-{2} +session.attachment.unknown=Attached content ({0}) prompt.action.enhance=Enhance prompt prompt.action.enhance.loading=Enhancing prompt... prompt.action.enhance.description=The 'Enhance Prompt' button helps improve your prompt by providing additional context, clarification, or rephrasing. Try typing a prompt in here and clicking the button again to see how it works. diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/SessionFileLinksTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/SessionFileLinksTest.kt index 13c56f27f0..1294933f4a 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/SessionFileLinksTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/SessionFileLinksTest.kt @@ -41,7 +41,7 @@ class SessionFileLinksTest : BasePlatformTestCase() { fun `test parse strips line range and column suffixes`() { assertEquals(SessionFileLinks.Target("src/Foo.kt", line = 12), SessionFileLinks.parse("src/Foo.kt:12")) - assertEquals(SessionFileLinks.Target("src/Foo.kt", line = 12), SessionFileLinks.parse("src/Foo.kt:12-20")) + assertEquals(SessionFileLinks.Target("src/Foo.kt", line = 12, endLine = 20), SessionFileLinks.parse("src/Foo.kt:12-20")) assertEquals(SessionFileLinks.Target("src/Foo.kt", line = 12, column = 3), SessionFileLinks.parse("src/Foo.kt:12:3")) } @@ -96,6 +96,18 @@ class SessionFileLinksTest : BasePlatformTestCase() { assertEquals("true", events.single().second["hasLine"]) } + fun `test open forwards line range to workspace service`() = runBlocking { + val file = WorkspaceFileDto("/test/src/Foo.kt", "Foo.kt") + val done = CompletableDeferred() + rpc.fileResolver = { path -> if (path == "src/Foo.kt") listOf(file) else emptyList() } + val links = SessionFileLinks("/test", service, scope, JPanel(), openUrl = {}) { _, _ -> done.complete(Unit) } + + links.open("src/Foo.kt:12-20", null) + withTimeout(OPEN_TIMEOUT_MS) { done.await() } + + assertEquals(listOf(FakeWorkspaceRpcApi.Opened("/test/src/Foo.kt", 12, null, 20)), rpc.openedFiles) + } + private companion object { const val OPEN_TIMEOUT_MS = 5_000L } diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/SessionMessageListPanelTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/SessionMessageListPanelTest.kt index 95ff55bf94..8f36919989 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/SessionMessageListPanelTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/SessionMessageListPanelTest.kt @@ -22,6 +22,7 @@ import ai.kilocode.client.session.views.question.QuestionView import ai.kilocode.client.session.ui.selection.SessionCopyTarget import ai.kilocode.client.session.views.MessageToolbar import ai.kilocode.client.session.views.MessageView +import ai.kilocode.client.session.views.PromptAttachmentView import ai.kilocode.client.session.views.TextView import ai.kilocode.client.session.views.TurnView import ai.kilocode.client.session.views.base.PartView @@ -809,7 +810,9 @@ class SessionMessageListPanelTest : BasePlatformTestCase() { layout(message) val box = promptBox(message) val point = SwingUtilities.convertPoint(box, Point(), message) - assertTrue("prompt box should be below attachment", point.y > 0) + val attachment = components(message).filterIsInstance().single() + val attachmentPoint = SwingUtilities.convertPoint(attachment, Point(), box) + assertTrue("attachment should be inside prompt box below prompt text", attachmentPoint.y > 0) val image = BufferedImage(message.width, message.height, BufferedImage.TYPE_INT_ARGB) val graphics = image.createGraphics() @@ -818,7 +821,7 @@ class SessionMessageListPanelTest : BasePlatformTestCase() { val line = SessionUiStyle.View.Outline.color().rgb assertEquals(line, Color(image.getRGB(point.x + box.width / 2, point.y), true).rgb) - assertFalse(line == Color(image.getRGB(point.x + box.width / 2, 0), true).rgb) + assertEquals(line, Color(image.getRGB(point.x + box.width / 2, point.y + box.height - 1), true).rgb) } fun `test created ContentDelta is not double applied`() { @@ -1635,7 +1638,7 @@ class SessionMessageListPanelTest : BasePlatformTestCase() { } private fun promptBox(root: MessageView): Component { - return components(root).first { it.parent != root && it is JPanel && it.componentCount == 1 && it.components.single() is TextView } + return components(root).first { it.parent != root && it is JPanel && it.components.any { child -> child is TextView } } } private fun components(root: Component): List { diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/SessionUiUpdateTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/SessionUiUpdateTest.kt index dee8f91c9f..188ef09d37 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/SessionUiUpdateTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/SessionUiUpdateTest.kt @@ -3,6 +3,7 @@ package ai.kilocode.client.session.ui import ai.kilocode.client.session.model.SessionModel import ai.kilocode.client.session.model.SessionState import ai.kilocode.client.session.ui.attachment.AttachmentCard +import ai.kilocode.client.session.ui.attachment.AttachmentChip import ai.kilocode.client.session.ui.style.SessionUiStyle import ai.kilocode.client.session.views.AttachmentView import ai.kilocode.client.session.views.PromptAttachmentView @@ -20,6 +21,7 @@ import ai.kilocode.rpc.dto.PartSourceTextDto import com.intellij.openapi.Disposable import com.intellij.openapi.util.Disposer import com.intellij.testFramework.fixtures.BasePlatformTestCase +import com.intellij.ui.components.JBLabel import com.intellij.util.ui.JBUI import java.awt.Container import java.awt.event.MouseEvent @@ -270,16 +272,21 @@ class SessionUiUpdateTest : BasePlatformTestCase() { val attachment = msg.part("f1")!! val other = msg.part("f2")!! - assertSame(msg, attachment.parent) + assertNotSame(msg, attachment.parent) assertSame(attachment, other) assertEquals(listOf("p1", "f1", "f2"), msg.partIds()) - assertEquals(1, msg.components.filterIsInstance().size) - assertEquals(2, findAll(attachment, AttachmentCard::class.java).size) + assertEquals(1, findAll(msg, PromptAttachmentView::class.java).size) + assertEquals(1, findAll(attachment, AttachmentCard::class.java).size) + assertEquals(1, findAll(attachment, AttachmentChip::class.java).size) val cards = findAll(attachment, AttachmentCard::class.java) for (card in cards) { card.dispatchEvent(MouseEvent(card, MouseEvent.MOUSE_CLICKED, System.currentTimeMillis(), 0, 1, 1, 1, false)) } + val chips = findAll(attachment, AttachmentChip::class.java) + for (chip in chips) { + chip.dispatchEvent(MouseEvent(chip, MouseEvent.MOUSE_CLICKED, System.currentTimeMillis(), 0, 1, 1, 1, false)) + } assertEquals(listOf("data:image/png;base64,aGVsbG8=", "data:text/plain;base64,aGVsbG8="), opened) } @@ -356,7 +363,26 @@ class SessionUiUpdateTest : BasePlatformTestCase() { val view = panel.findMessage("u1")!!.part("f1") assertTrue(view is PromptAttachmentView) - assertNotNull(find(view!!, AttachmentCard::class.java)) + assertNotNull(find(view!!, AttachmentChip::class.java)) + } + + fun `test source less file selection renders filename range chip`() { + model.upsertMessage(msg("u1", "user")) + model.updateContent("u1", PartDto( + id = "f1", + sessionID = "ses", + messageID = "u1", + type = "file", + mime = "text/plain", + url = "file:///tmp/HvJwtFilter.java?start=12&end=40", + filename = "HvJwtFilter.java", + )) + + val view = panel.findMessage("u1")!!.part("f1")!! + val chip = find(view, AttachmentChip::class.java) + + assertNotNull(chip) + assertTrue(findAll(chip!!, JBLabel::class.java).any { it.text == "HvJwtFilter.java:12-40" }) } fun `test source backed image attachment still renders in prompt strip`() { @@ -400,7 +426,7 @@ class SessionUiUpdateTest : BasePlatformTestCase() { assertNull(msg.part("p2")) assertEquals(listOf("p1", "f1"), msg.partIds()) assertTrue(msg.part("p1") is TextView) - assertEquals(1, msg.components.filterIsInstance().size) + assertEquals(1, findAll(msg, PromptAttachmentView::class.java).size) } fun `test prompt text panel is removed when content becomes empty`() { @@ -497,8 +523,8 @@ class SessionUiUpdateTest : BasePlatformTestCase() { ), ) - val card = find(item.findMessage("u1")!!.part("f1")!!, AttachmentCard::class.java)!! - card.dispatchEvent(MouseEvent(card, MouseEvent.MOUSE_CLICKED, System.currentTimeMillis(), 0, 1, 1, 1, false)) + val chip = find(item.findMessage("u1")!!.part("f1")!!, AttachmentChip::class.java)!! + chip.dispatchEvent(MouseEvent(chip, MouseEvent.MOUSE_CLICKED, System.currentTimeMillis(), 0, 1, 1, 1, false)) assertEquals(listOf("u1" to "data:text/plain;base64,aGVsbG8="), opened) } diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/testing/FakeWorkspaceRpcApi.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/testing/FakeWorkspaceRpcApi.kt index 327566bf9c..da98aa58b5 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/testing/FakeWorkspaceRpcApi.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/testing/FakeWorkspaceRpcApi.kt @@ -111,10 +111,10 @@ class FakeWorkspaceRpcApi : KiloWorkspaceRpcApi { return branchName } - override suspend fun openFile(path: String, line: Int?, column: Int?): Boolean { + override suspend fun openFile(path: String, line: Int?, column: Int?, endLine: Int?): Boolean { assertNotEdt("openFile") opened.add(path) - openedFiles.add(Opened(path, line, column)) + openedFiles.add(Opened(path, line, column, endLine)) return openResult } @@ -150,5 +150,5 @@ class FakeWorkspaceRpcApi : KiloWorkspaceRpcApi { return openResult } - data class Opened(val path: String, val line: Int?, val column: Int?) + data class Opened(val path: String, val line: Int?, val column: Int?, val endLine: Int? = null) } diff --git a/packages/kilo-jetbrains/shared/src/main/kotlin/ai/kilocode/rpc/KiloWorkspaceRpcApi.kt b/packages/kilo-jetbrains/shared/src/main/kotlin/ai/kilocode/rpc/KiloWorkspaceRpcApi.kt index f609a796da..bebd2c3869 100644 --- a/packages/kilo-jetbrains/shared/src/main/kotlin/ai/kilocode/rpc/KiloWorkspaceRpcApi.kt +++ b/packages/kilo-jetbrains/shared/src/main/kotlin/ai/kilocode/rpc/KiloWorkspaceRpcApi.kt @@ -67,7 +67,7 @@ interface KiloWorkspaceRpcApi : RemoteApi { suspend fun branchName(directory: String): String? /** Open an absolute backend file path in the IDE. */ - suspend fun openFile(path: String, line: Int? = null, column: Int? = null): Boolean + suspend fun openFile(path: String, line: Int? = null, column: Int? = null, endLine: Int? = null): Boolean /** Resolve the editable local config target. */ suspend fun localConfigTarget(directory: String): ConfigTargetDto From f66b7efc6c2f0192205fdc2b9f4019850e47cd50 Mon Sep 17 00:00:00 2001 From: kirillk Date: Sun, 9 Aug 2026 15:27:19 -0400 Subject: [PATCH 03/14] fix(jetbrains): harden editor context and fix tool-view placement Address review feedback on the automatic editor-context PR: - Skip malformed .kilocodeignore/.gitignore globs instead of throwing PatternSyntaxException that broke every prompt send. - Add KiloIgnoreCache so the compiled matcher is cached per root and invalidated via a VFS listener, keeping the blocking (remote cwm) read off the prompt-send path after the first prompt. - Gather editor context only on the prompt path so slash commands and client actions no longer pay its cost or hit its failure modes. - Guard Path.of(file.path) against cross-OS invalid filenames, matching rootDir(). - Keep the local Auto-Include Editor Context toggle interactive regardless of backend readiness; it is a per-IDE preference, not CLI config. - Fix a replaced tool view (e.g. completed question) jumping above the prompt bubble on user messages. --- .../ai/kilocode/client/session/SessionUi.kt | 24 +++++----- .../session/context/EditorContextGatherer.kt | 7 ++- .../client/session/context/KiloIgnore.kt | 9 ++-- .../client/session/context/KiloIgnoreCache.kt | 46 ++++++++++++++++++ .../client/session/views/MessageView.kt | 6 ++- .../settings/context/ContextSettingsUi.kt | 8 +++- .../session/context/KiloIgnoreCacheTest.kt | 34 +++++++++++++ .../client/session/context/KiloIgnoreTest.kt | 6 +++ .../client/session/views/MessageViewTest.kt | 48 +++++++++++++++++++ .../settings/context/ContextSettingsUiTest.kt | 33 ++++++++++++- 10 files changed, 201 insertions(+), 20 deletions(-) create mode 100644 packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/context/KiloIgnoreCache.kt create mode 100644 packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/context/KiloIgnoreCacheTest.kt create mode 100644 packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/MessageViewTest.kt diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt index 4c7df90e8a..5068db342e 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt @@ -683,17 +683,6 @@ class SessionUi( private fun sendPrompt(text: String, files: List) { if (text.isBlank() && files.isEmpty()) return - val editor = EditorContextGatherer.gather(project, workspace.directory) - val allFiles = files + listOfNotNull(editor.selection) - val parts = buildList { - text.takeIf { it.isNotBlank() }?.let { add(PromptPartDto(type = "text", text = it)) } - addAll(allFiles) - } - LOG.debug { - val agent = controller.model.agent ?: "none" - val model = controller.model.model ?: "none" - "${ChatLogSummary.prompt(PromptDto(parts = parts, editorContext = editor.context))} agent=$agent model=$model ready=${controller.ready}" - } prompt.clear() val follow = scroll.atBottom() val action = completion.clientAction(text) @@ -708,6 +697,19 @@ class SessionUi( scroll.followBottom(follow) return } + // Only the prompt path uses editor context; gather after the command branches so slash + // commands and client actions don't pay the editor-context cost or hit its failure modes. + val editor = EditorContextGatherer.gather(project, workspace.directory) + val allFiles = files + listOfNotNull(editor.selection) + LOG.debug { + val parts = buildList { + text.takeIf { it.isNotBlank() }?.let { add(PromptPartDto(type = "text", text = it)) } + addAll(allFiles) + } + val agent = controller.model.agent ?: "none" + val model = controller.model.model ?: "none" + "${ChatLogSummary.prompt(PromptDto(parts = parts, editorContext = editor.context))} agent=$agent model=$model ready=${controller.ready}" + } controller.prompt(text, allFiles, editor.context) scroll.followBottom(follow) } diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/context/EditorContextGatherer.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/context/EditorContextGatherer.kt index 747a663c21..ecf163c35f 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/context/EditorContextGatherer.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/context/EditorContextGatherer.kt @@ -6,6 +6,7 @@ import ai.kilocode.log.KiloLog import ai.kilocode.rpc.dto.EditorContextDto import ai.kilocode.rpc.dto.PromptPartDto import com.intellij.codeWithMe.ClientId +import com.intellij.openapi.components.service import com.intellij.openapi.editor.Editor import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.fileEditor.FileEditorManager @@ -49,7 +50,7 @@ internal object EditorContextGatherer { val openFiles = manager.openFilesWithRemotes val editor = manager.selectedTextEditorWithRemotes.firstOrNull() val activeFile = editor?.let { file(it) } ?: lastOpen(project, openFiles) ?: openFiles.firstOrNull() - val ignore = KiloIgnore.load(rootDir(listOfNotNull(activeFile) + openFiles, base)) + val ignore = project.service().matcher(rootDir(listOfNotNull(activeFile) + openFiles, base)) fun keep(file: VirtualFile?): String? = rel(file, base)?.takeUnless { ignore.ignored(it) } @@ -129,7 +130,9 @@ internal object EditorContextGatherer { private fun local(file: VirtualFile, root: Path): Path? { if (file.fileSystem.protocol == KiloVirtualFileSystem.PROTOCOL) return null - val path = Path.of(file.path).toAbsolutePath().normalize() + // A host filename that is invalid on the client OS (e.g. `?`/`*` from a Linux host on + // a Windows frontend) throws InvalidPathException; drop the file instead of failing the send. + val path = runCatching { Path.of(file.path).toAbsolutePath().normalize() }.getOrNull() ?: return null if (!path.startsWith(root)) return null return path } diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/context/KiloIgnore.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/context/KiloIgnore.kt index bef689e556..dedc4666f5 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/context/KiloIgnore.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/context/KiloIgnore.kt @@ -36,8 +36,8 @@ internal class KiloIgnore private constructor(private val rules: List) { companion object { val EMPTY = KiloIgnore(emptyList()) - private const val KILO = ".kilocodeignore" - private const val GIT = ".gitignore" + const val KILO = ".kilocodeignore" + const val GIT = ".gitignore" private val SENSITIVE = listOf(".env", ".env.*") /** @@ -80,7 +80,10 @@ internal class KiloIgnore private constructor(private val rules: List) { val anchored = leading || line.contains('/') val prefix = if (anchored) "" else "(?:.*/)?" val suffix = if (dirOnly) "/.*" else "(?:/.*)?" - return Rule(Regex("^$prefix${glob(line)}$suffix$"), negate) + // A malformed character class (e.g. `[]`, `[z-a]`) yields an invalid Java regex. + // Skip the bad rule instead of letting PatternSyntaxException break every prompt send. + val regex = runCatching { Regex("^$prefix${glob(line)}$suffix$") }.getOrNull() ?: return null + return Rule(regex, negate) } private fun glob(glob: String): String { diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/context/KiloIgnoreCache.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/context/KiloIgnoreCache.kt new file mode 100644 index 0000000000..170b144cb8 --- /dev/null +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/context/KiloIgnoreCache.kt @@ -0,0 +1,46 @@ +package ai.kilocode.client.session.context + +import com.intellij.openapi.Disposable +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.components.Service +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.vfs.VirtualFileManager +import com.intellij.openapi.vfs.newvfs.BulkFileListener +import com.intellij.openapi.vfs.newvfs.events.VFileEvent +import java.util.concurrent.ConcurrentHashMap + +/** + * Caches the compiled [KiloIgnore] per workspace-root directory so editor-context + * gathering does not re-read and re-compile the ignore files on every prompt. + * + * The compiled matcher is reused until a `.kilocodeignore` or `.gitignore` change + * invalidates it via a VFS listener. This keeps the blocking VFS read (a remote `cwm` + * round-trip in split mode) off the prompt-send path after the first prompt, instead of + * repeating it for every message the user sends. + */ +@Service(Service.Level.PROJECT) +internal class KiloIgnoreCache : Disposable { + private val cache = ConcurrentHashMap() + + init { + ApplicationManager.getApplication().messageBus.connect(this) + .subscribe(VirtualFileManager.VFS_CHANGES, object : BulkFileListener { + override fun after(events: List) { + if (events.any { relevant(it) }) cache.clear() + } + }) + } + + /** Returns the cached matcher for [root], compiling and caching it on first use. */ + fun matcher(root: VirtualFile?): KiloIgnore { + if (root == null) return KiloIgnore.EMPTY + return cache.getOrPut(root.url) { KiloIgnore.load(root) } + } + + override fun dispose() = cache.clear() + + private fun relevant(event: VFileEvent): Boolean { + val name = event.path.substringAfterLast('/') + return name == KiloIgnore.KILO || name == KiloIgnore.GIT + } +} diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/MessageView.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/MessageView.kt index 3aa839f805..7a55bc0d08 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/MessageView.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/MessageView.kt @@ -254,7 +254,11 @@ class MessageView( @RequiresEdt private fun replacePart(content: Content, existing: PartView) { - val at = components.indexOfFirst { it === existing || it === wrap }.takeIf { it >= 0 } ?: componentCount + // A replaced tool view is a direct child, so re-insert at its own slot. Only fall back to + // the prompt wrap's index when the replaced view is nested inside it, otherwise the wrap's + // lower index would push the replacement above the prompt bubble on user messages. + val at = (if (existing.parent !== this) components.indexOf(wrap) else components.indexOfFirst { it === existing }) + .takeIf { it >= 0 } ?: componentCount parts.remove(content.id) aliases.values.removeAll { it == content.id } sources.keys.removeAll { it !in aliases } diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/settings/context/ContextSettingsUi.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/settings/context/ContextSettingsUi.kt index 0118542137..d6e4a8a433 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/settings/context/ContextSettingsUi.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/settings/context/ContextSettingsUi.kt @@ -135,6 +135,9 @@ internal class ContextSettingsContent( private val update: (ContextDraft.() -> ContextDraft) -> Unit, ) : BaseContentPanel() { private val auto = SettingsToggle { value -> update { copy(auto = value) } } + // Editor-context auto-include is a local per-IDE preference in PropertiesComponent (like + // autoApprove), applied immediately on toggle rather than through the CLI-backed draft/apply/ + // reset flow used by the other rows. It stays interactive even when the backend isn't READY. private val editor = SettingsToggle(KiloPluginSettings.getAutoEditorContext()) { value -> KiloPluginSettings.setAutoEditorContext(value) } @@ -183,11 +186,14 @@ internal class ContextSettingsContent( @RequiresEdt fun sync(draft: ContextDraft, enabled: Boolean) { auto.isSelected = draft.auto + // Local preference: reflects PropertiesComponent and stays enabled regardless of the + // CLI-backed [enabled] gating that applies to the draft-driven rows below. editor.isSelected = KiloPluginSettings.getAutoEditorContext() + editor.isEnabled = true prune.isSelected = draft.prune threshold.sync(draft.threshold) patterns.sync(draft.ignore) - listOf(auto, editor, prune, threshold, patterns).forEach { it.isEnabled = enabled } + listOf(auto, prune, threshold, patterns).forEach { it.isEnabled = enabled } } } diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/context/KiloIgnoreCacheTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/context/KiloIgnoreCacheTest.kt new file mode 100644 index 0000000000..aca6828d29 --- /dev/null +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/context/KiloIgnoreCacheTest.kt @@ -0,0 +1,34 @@ +package ai.kilocode.client.session.context + +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.components.service +import com.intellij.openapi.vfs.VfsUtil +import com.intellij.testFramework.fixtures.BasePlatformTestCase +import com.intellij.util.ui.UIUtil + +class KiloIgnoreCacheTest : BasePlatformTestCase() { + fun `test matcher caches until ignore file changes`() { + val file = myFixture.addFileToProject(".kilocodeignore", "ignored/\n").virtualFile + val root = file.parent + val cache = project.service() + + val first = cache.matcher(root) + assertTrue(first.ignored("ignored/Secret.kt")) + assertFalse(first.ignored("src/App.kt")) + assertSame(first, cache.matcher(root)) + + ApplicationManager.getApplication().runWriteAction { + VfsUtil.saveText(file, "src/\n") + } + UIUtil.dispatchAllInvocationEvents() + + val second = cache.matcher(root) + assertNotSame(first, second) + assertFalse(second.ignored("ignored/Secret.kt")) + assertTrue(second.ignored("src/App.kt")) + } + + fun `test null root allows everything`() { + assertSame(KiloIgnore.EMPTY, project.service().matcher(null)) + } +} diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/context/KiloIgnoreTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/context/KiloIgnoreTest.kt index fbe71fa5a8..1dedb3b4ae 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/context/KiloIgnoreTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/context/KiloIgnoreTest.kt @@ -86,4 +86,10 @@ class KiloIgnoreTest : TestCase() { val ignore = KiloIgnore.of("node_modules/") assertTrue(ignore.ignored("a\\node_modules\\pkg.js")) } + + fun `test malformed char class is skipped without throwing`() { + val ignore = KiloIgnore.of("[z-a]\n[]\n[!]\n*.log") + assertTrue(ignore.ignored("debug.log")) + assertFalse(ignore.ignored("src/App.kt")) + } } diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/MessageViewTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/MessageViewTest.kt new file mode 100644 index 0000000000..cf1e3408db --- /dev/null +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/MessageViewTest.kt @@ -0,0 +1,48 @@ +package ai.kilocode.client.session.views + +import ai.kilocode.client.session.model.Message +import ai.kilocode.client.session.model.Text +import ai.kilocode.client.session.model.Tool +import ai.kilocode.client.session.model.ToolExecState +import ai.kilocode.client.session.model.ToolKind +import ai.kilocode.client.session.views.question.QuestionResultView +import ai.kilocode.rpc.dto.MessageDto +import ai.kilocode.rpc.dto.MessageTimeDto +import com.intellij.testFramework.fixtures.BasePlatformTestCase +import javax.swing.SwingUtilities + +class MessageViewTest : BasePlatformTestCase() { + // A user message can carry both a prompt bubble (wrapped, lower component index) and a tool + // view added after it. Replacing that tool (e.g. a completed question) must reuse the tool's + // own slot, not the prompt wrap's lower index, or the replacement jumps above the bubble. + fun `test replacing a tool view keeps it below the prompt bubble`() { + val msg = Message(MessageDto("m1", "ses", "user", MessageTimeDto(0.0))) + val view = MessageView(msg, openFile = { _, _ -> }) + + val text = Text("p1").also { it.content.append("do the thing") } + msg.parts["p1"] = text + view.upsertPart(text) + + val tool = Tool("t1", "question", ToolKind.GENERIC).also { + it.state = ToolExecState.RUNNING + it.input = mapOf("questions" to """[{"question":"Proceed?"}]""") + } + msg.parts["t1"] = tool + view.upsertPart(tool) + + tool.state = ToolExecState.COMPLETED + tool.metadata = mapOf("answers" to """[["Yes"]]""") + view.upsertPart(tool) + + val result = view.part("t1") + val prompt = view.part("p1") + assertNotNull(result) + assertNotNull(prompt) + assertTrue(result is QuestionResultView) + val children = view.components.toList() + val wrapIndex = children.indexOfFirst { SwingUtilities.isDescendingFrom(prompt, it) } + val resultIndex = children.indexOf(result) + assertTrue("prompt bubble is a direct child", wrapIndex >= 0) + assertTrue("question result stays below the prompt bubble", resultIndex > wrapIndex) + } +} diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/settings/context/ContextSettingsUiTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/settings/context/ContextSettingsUiTest.kt index 80c5a2e1c4..ac29c470be 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/settings/context/ContextSettingsUiTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/settings/context/ContextSettingsUiTest.kt @@ -2,6 +2,8 @@ package ai.kilocode.client.settings.context import ai.kilocode.client.app.KiloAppService import ai.kilocode.client.app.KiloWorkspaceService +import ai.kilocode.client.plugin.KiloPluginSettings +import ai.kilocode.client.settings.base.SettingsRow import ai.kilocode.client.settings.base.SettingsToggle import ai.kilocode.client.ui.HoverIcon import ai.kilocode.client.testing.FakeAppRpcApi @@ -69,6 +71,7 @@ class ContextSettingsUiTest : BasePlatformTestCase() { ui = null uiScope.cancel() appScope.cancel() + KiloPluginSettings.unsetAutoEditorContext() } finally { super.tearDown() } @@ -253,21 +256,39 @@ class ContextSettingsUiTest : BasePlatformTestCase() { } } - fun `test controls are disabled during pending save`() { + fun `test controls are disabled during pending save except local editor toggle`() { val panel = requireUi() rpc.configUpdateGate = CompletableDeferred() edt { threshold(panel).text = "80" panel.applyDraft() - assertTrue(components(panel).filterIsInstance().all { !it.isEnabled }) + val editor = editorToggle(panel) + val cli = components(panel).filterIsInstance().filter { it !== editor } + assertTrue(cli.all { !it.isEnabled }) assertFalse(threshold(panel).isEnabled) + assertTrue(editor.isEnabled) } rpc.configUpdateGate?.complete(Unit) flushUntil { rpc.configPatches.isNotEmpty() } } + fun `test editor context toggle persists immediately without a config patch`() { + val panel = requireUi() + assertTrue(KiloPluginSettings.getAutoEditorContext()) + + edt { + val editor = editorToggle(panel) + assertTrue(editor.isEnabled) + editor.doClick() + } + + assertFalse(KiloPluginSettings.getAutoEditorContext()) + edt { UIUtil.dispatchAllInvocationEvents() } + assertTrue(rpc.configPatches.isEmpty()) + } + private fun requireUi(): ContextSettingsUi = requireNotNull(ui) private fun threshold(panel: ContextSettingsUi): JBTextField = components(panel) @@ -284,6 +305,14 @@ class ContextSettingsUiTest : BasePlatformTestCase() { .filterIsInstance() .single { it.toolTipText == tip } + private fun editorToggle(panel: ContextSettingsUi): SettingsToggle { + val label = components(panel).filterIsInstance() + .first { it.text == "Auto-Include Editor Context" } + var row: Container? = label.parent + while (row != null && row !is SettingsRow) row = row.parent + return components(requireNotNull(row)).filterIsInstance().single() + } + private fun edt(block: () -> T): T { var result: T? = null ApplicationManager.getApplication().invokeAndWait { result = block() } From 9ebe1f6bd4e0a8537e59e1b51641860d7cb21edb Mon Sep 17 00:00:00 2001 From: kirillk Date: Mon, 10 Aug 2026 09:21:40 -0400 Subject: [PATCH 04/14] fix(jetbrains): align prompt attachment chips Remove the rounded outline from compact prompt attachment chips and let the prompt attachment container own left, right, and bottom padding so attached selections align with the prompt text. --- .../session/ui/attachment/AttachmentCard.kt | 16 --------- .../client/session/ui/style/SessionUiStyle.kt | 1 - .../session/views/PromptAttachmentView.kt | 5 +-- .../session/views/PromptAttachmentViewTest.kt | 35 +++++++++++++++++++ 4 files changed, 38 insertions(+), 19 deletions(-) create mode 100644 packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/PromptAttachmentViewTest.kt diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/attachment/AttachmentCard.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/attachment/AttachmentCard.kt index f433862985..a54e683355 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/attachment/AttachmentCard.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/attachment/AttachmentCard.kt @@ -65,7 +65,6 @@ class AttachmentChip( init { isOpaque = false - border = JBUI.Borders.empty(0, JBUI.scale(SessionUiStyle.View.Attachment.CHIP_HORIZONTAL_PADDING)) cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR) toolTipText = tip accessibleContext?.accessibleName = KiloBundle.message("prompt.attachment.open", item.name) @@ -85,21 +84,6 @@ class AttachmentChip( override fun getMinimumSize(): Dimension = preferredSize - override fun paintComponent(g: Graphics) { - val g2 = g.create() as Graphics2D - try { - g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON) - val arc = JBUI.scale(SessionUiStyle.View.Attachment.CORNER_ARC) - g2.color = SessionUiStyle.View.Surface.bgColor() - g2.fillRoundRect(0, 0, width, height, arc, arc) - g2.color = SessionUiStyle.View.Outline.color() - g2.drawRoundRect(0, 0, width - 1, height - 1, arc, arc) - } finally { - g2.dispose() - } - super.paintComponent(g) - } - private fun label(): String { val start = startLine val end = endLine diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/style/SessionUiStyle.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/style/SessionUiStyle.kt index aa8ce97a96..f610298b9b 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/style/SessionUiStyle.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/style/SessionUiStyle.kt @@ -122,7 +122,6 @@ object SessionUiStyle { const val CLOSE_SIZE = 18 const val CORNER_ARC = 8 const val CHIP_HEIGHT = 28 - const val CHIP_HORIZONTAL_PADDING = 8 const val CHIP_ICON_GAP = 6 } diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/PromptAttachmentView.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/PromptAttachmentView.kt index fb15e0e1d2..b828c659da 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/PromptAttachmentView.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/PromptAttachmentView.kt @@ -36,11 +36,12 @@ class PromptAttachmentView( init { isOpaque = false + // Align the attachment chips with the prompt text: same left/right/bottom padding as PromptView. border = JBUI.Borders.empty( 0, - 0, + JBUI.scale(SessionUiStyle.View.Prompt.SHELL_HORIZONTAL_PADDING), JBUI.scale(SessionUiStyle.View.Prompt.SHELL_VERTICAL_PADDING), - 0, + JBUI.scale(SessionUiStyle.View.Prompt.SHELL_HORIZONTAL_PADDING), ) add(scroll) } diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/PromptAttachmentViewTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/PromptAttachmentViewTest.kt new file mode 100644 index 0000000000..9e07af5dda --- /dev/null +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/PromptAttachmentViewTest.kt @@ -0,0 +1,35 @@ +package ai.kilocode.client.session.views + +import ai.kilocode.client.session.model.Text +import ai.kilocode.client.session.ui.attachment.AttachmentCardItem +import ai.kilocode.client.session.ui.attachment.AttachmentChip +import com.intellij.testFramework.fixtures.BasePlatformTestCase + +class PromptAttachmentViewTest : BasePlatformTestCase() { + // The attachment strip should line up with the prompt text: same left, right, and bottom + // padding as PromptView so the selection reference reads as part of the prompt. + fun `test attachment padding matches prompt text`() { + val prompt = PromptView(Text("p1")).insets + val attach = PromptAttachmentView("m1") {}.insets + + assertEquals(prompt.left, attach.left) + assertEquals(prompt.right, attach.right) + assertEquals(prompt.bottom, attach.bottom) + } + + // With the outline removed, the chip owns no internal padding; alignment comes from the + // container so the chip content sits flush against the prompt-matching insets. + fun `test attachment chip has no outline padding`() { + val chip = AttachmentChip( + AttachmentCardItem("HvJwtFilter.java", "text/plain", "file:///HvJwtFilter.java"), + file = true, + startLine = 40, + endLine = 42, + ).insets + + assertEquals(0, chip.left) + assertEquals(0, chip.right) + assertEquals(0, chip.top) + assertEquals(0, chip.bottom) + } +} From 029391950696373acba5dfdf0f7b58711e0bc218 Mon Sep 17 00:00:00 2001 From: kirillk Date: Mon, 10 Aug 2026 09:36:07 -0400 Subject: [PATCH 05/14] fix(jetbrains): trim prompt attachment panel chrome Remove the remaining border line from the prompt attachment scroll pane and keep only a small standard bottom inset below attached selections. --- .../session/views/PromptAttachmentView.kt | 9 ++++----- .../client/session/ui/SessionUiUpdateTest.kt | 7 +++---- .../session/views/PromptAttachmentViewTest.kt | 17 +++++++++++++---- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/PromptAttachmentView.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/PromptAttachmentView.kt index b828c659da..919b114fe5 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/PromptAttachmentView.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/PromptAttachmentView.kt @@ -28,6 +28,7 @@ class PromptAttachmentView( private val row = Stack.horizontal(gap = UiStyle.Gap.sm()) private val scroll = JBScrollPane(row).apply { border = null + viewportBorder = null isOpaque = false viewport.isOpaque = false horizontalScrollBarPolicy = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED @@ -36,11 +37,11 @@ class PromptAttachmentView( init { isOpaque = false - // Align the attachment chips with the prompt text: same left/right/bottom padding as PromptView. + // Align the attachment chips with the prompt text horizontally, with only a small bottom inset. border = JBUI.Borders.empty( 0, JBUI.scale(SessionUiStyle.View.Prompt.SHELL_HORIZONTAL_PADDING), - JBUI.scale(SessionUiStyle.View.Prompt.SHELL_VERTICAL_PADDING), + UiStyle.Gap.sm(), JBUI.scale(SessionUiStyle.View.Prompt.SHELL_HORIZONTAL_PADDING), ) add(scroll) @@ -87,7 +88,7 @@ class PromptAttachmentView( override fun getPreferredSize(): Dimension { val ins = insets val pref = scroll.preferredSize - return Dimension(0, pref.height + bar() + ins.top + ins.bottom) + return Dimension(0, pref.height + ins.top + ins.bottom) } override fun getMinimumSize() = preferredSize @@ -133,8 +134,6 @@ class PromptAttachmentView( return uri.scheme == "file" } - private fun bar() = scroll.horizontalScrollBar.preferredSize.height - private fun name(item: FileAttachment) = item.filename?.takeIf { it.isNotBlank() } ?: tail(item.url).takeIf { it.isNotBlank() } ?: "attachment" diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/SessionUiUpdateTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/SessionUiUpdateTest.kt index 188ef09d37..49ff27f583 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/SessionUiUpdateTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/SessionUiUpdateTest.kt @@ -5,6 +5,7 @@ import ai.kilocode.client.session.model.SessionState import ai.kilocode.client.session.ui.attachment.AttachmentCard import ai.kilocode.client.session.ui.attachment.AttachmentChip import ai.kilocode.client.session.ui.style.SessionUiStyle +import ai.kilocode.client.ui.UiStyle import ai.kilocode.client.session.views.AttachmentView import ai.kilocode.client.session.views.PromptAttachmentView import ai.kilocode.client.session.views.tool.ReadToolView @@ -467,11 +468,9 @@ class SessionUiUpdateTest : BasePlatformTestCase() { assertEquals(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED, pane.horizontalScrollBarPolicy) assertEquals(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER, pane.verticalScrollBarPolicy) assertEquals(0, view.insets.top) - assertEquals(JBUI.scale(SessionUiStyle.View.Prompt.SHELL_VERTICAL_PADDING), view.insets.bottom) + assertEquals(UiStyle.Gap.sm(), view.insets.bottom) assertEquals( - JBUI.scale(SessionUiStyle.View.Attachment.CARD_HEIGHT) + - pane.horizontalScrollBar.preferredSize.height + - JBUI.scale(SessionUiStyle.View.Prompt.SHELL_VERTICAL_PADDING), + JBUI.scale(SessionUiStyle.View.Attachment.CARD_HEIGHT) + UiStyle.Gap.sm(), height, ) diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/PromptAttachmentViewTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/PromptAttachmentViewTest.kt index 9e07af5dda..457a8cdfd8 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/PromptAttachmentViewTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/PromptAttachmentViewTest.kt @@ -3,18 +3,27 @@ package ai.kilocode.client.session.views import ai.kilocode.client.session.model.Text import ai.kilocode.client.session.ui.attachment.AttachmentCardItem import ai.kilocode.client.session.ui.attachment.AttachmentChip +import ai.kilocode.client.ui.UiStyle import com.intellij.testFramework.fixtures.BasePlatformTestCase class PromptAttachmentViewTest : BasePlatformTestCase() { - // The attachment strip should line up with the prompt text: same left, right, and bottom - // padding as PromptView so the selection reference reads as part of the prompt. - fun `test attachment padding matches prompt text`() { + // The attachment strip should line up with the prompt text horizontally and keep only a + // small standard bottom inset below the selection reference. + fun `test attachment padding matches prompt text with small bottom inset`() { val prompt = PromptView(Text("p1")).insets val attach = PromptAttachmentView("m1") {}.insets assertEquals(prompt.left, attach.left) assertEquals(prompt.right, attach.right) - assertEquals(prompt.bottom, attach.bottom) + assertEquals(0, attach.top) + assertEquals(UiStyle.Gap.sm(), attach.bottom) + } + + fun `test attachment scroll pane has no border line`() { + val scroll = PromptAttachmentView("m1") {}.scrollPane() + + assertNull(scroll.border) + assertNull(scroll.viewportBorder) } // With the outline removed, the chip owns no internal padding; alignment comes from the From aaf2820162363a14f19bd43c8e8b1606f6cafe83 Mon Sep 17 00:00:00 2001 From: kirillk Date: Mon, 10 Aug 2026 09:48:35 -0400 Subject: [PATCH 06/14] fix(jetbrains): handle prompt bulk updates and context apply Skip prompt editor height and highlight recalculation while the editor document is in bulk update, then schedule one refresh after bulk mode exits so undo/redo cannot trigger UnexpectedBulkUpdateStateException. Also include the local Auto-Include Editor Context setting in the Context page draft state so toggling it marks the configurable modified and Apply persists it without sending a CLI config patch. --- .../client/session/ui/prompt/PromptPanel.kt | 29 +++++++++++++++++ .../settings/context/ContextSettingsState.kt | 6 ++++ .../settings/context/ContextSettingsUi.kt | 31 +++++++++++++------ .../client/session/ui/PromptPanelTest.kt | 17 ++++++++++ .../settings/context/ContextSettingsUiTest.kt | 6 +++- 5 files changed, 78 insertions(+), 11 deletions(-) diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/prompt/PromptPanel.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/prompt/PromptPanel.kt index dbea737449..1b5b656b85 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/prompt/PromptPanel.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/prompt/PromptPanel.kt @@ -42,6 +42,7 @@ import com.intellij.openapi.actionSystem.ex.ActionUtil import com.intellij.openapi.actionSystem.IdeActions import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.editor.DefaultLanguageHighlighterColors +import com.intellij.openapi.editor.Document import com.intellij.openapi.editor.SpellCheckingEditorCustomizationProvider import com.intellij.openapi.editor.colors.CodeInsightColors import com.intellij.openapi.editor.colors.TextAttributesKey @@ -256,6 +257,7 @@ class PromptPanel( private var ready = false private var enhancing = false private var request = 0L + private var deferred = false override val isSendEnabled: Boolean get() = ready && !submitting && (text().isNotEmpty() || attachments.isNotEmpty()) @@ -270,12 +272,22 @@ class PromptPanel( editor.addDocumentListener(object : DocumentListener { override fun documentChanged(e: DocumentEvent) { invalidateEnhancement() + if (e.document.isInBulkUpdate) { + deferEditorSync() + syncButton() + onChange() + return + } syncEditorHeight() triggerCompletion(e) syncHighlights() syncButton() onChange() } + + override fun bulkUpdateFinished(document: Document) { + deferEditorSync() + } }) shell.add(strip, BorderLayout.NORTH) shell.add(editor, BorderLayout.CENTER) @@ -937,6 +949,10 @@ class PromptPanel( @RequiresEdt private fun syncEditorHeight() { + if (editor.document.isInBulkUpdate) { + deferEditorSync() + return + } val before = editor.preferredSize.height val lower = editor.minimumSize.height editor.setPreferredSize(null) @@ -968,6 +984,19 @@ class PromptPanel( repaint() } + @RequiresEdt + private fun deferEditorSync() { + if (deferred) return + deferred = true + ApplicationManager.getApplication().invokeLater { + deferred = false + if (project.isDisposed || editor.document.isInBulkUpdate) return@invokeLater + syncEditorHeight() + syncHighlights() + syncButton() + } + } + @RequiresEdt private fun syncEditorScroll(ed: EditorEx?, overflow: Boolean) { // AS_NEEDED keeps the standard auto-hiding editor scrollbar (appears on diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/settings/context/ContextSettingsState.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/settings/context/ContextSettingsState.kt index 3a5989a332..6de6c44133 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/settings/context/ContextSettingsState.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/settings/context/ContextSettingsState.kt @@ -1,5 +1,6 @@ package ai.kilocode.client.settings.context +import ai.kilocode.client.plugin.KiloPluginSettings import ai.kilocode.rpc.dto.CompactionPatchDto import ai.kilocode.rpc.dto.ConfigDto import ai.kilocode.rpc.dto.ConfigPatchDto @@ -9,6 +10,7 @@ internal data class ContextDraft( val auto: Boolean = false, val threshold: String = "", val prune: Boolean = false, + val editor: Boolean = KiloPluginSettings.getAutoEditorContext(), val ignore: List = emptyList(), ) @@ -21,6 +23,7 @@ internal fun contextDraft(config: ConfigDto?): ContextDraft = ContextDraft( auto = config?.compaction?.auto ?: false, threshold = config?.compaction?.threshold_percent?.let(::formatThreshold).orEmpty(), prune = config?.compaction?.prune ?: false, + editor = KiloPluginSettings.getAutoEditorContext(), ignore = config?.watcher?.ignore ?: emptyList(), ) @@ -38,8 +41,11 @@ internal fun savedMatches(base: ContextDraft, draft: ContextDraft): Boolean = base.auto == draft.auto && normalizeThreshold(base.threshold) == normalizeThreshold(draft.threshold) && base.prune == draft.prune && + base.editor == draft.editor && base.ignore == draft.ignore +internal fun localChanged(base: ContextDraft, draft: ContextDraft): Boolean = base.editor != draft.editor + internal fun thresholdStatus(value: String): ThresholdStatus { val text = value.trim() if (text.isBlank()) return ThresholdStatus.VALID diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/settings/context/ContextSettingsUi.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/settings/context/ContextSettingsUi.kt index d6e4a8a433..8b156065b8 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/settings/context/ContextSettingsUi.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/settings/context/ContextSettingsUi.kt @@ -63,10 +63,23 @@ internal class ContextSettingsUi( startSettings(ContextSettingsContent { updateDraft(it) }) } - override fun change(from: ContextDraft, to: ContextDraft): ConfigPatchDto? = patch(from, to)?.takeIf(::changed) + override fun change(from: ContextDraft, to: ContextDraft): ConfigPatchDto? { + val patch = patch(from, to) ?: return null + if (changed(patch)) return patch + return ConfigPatchDto().takeIf { localChanged(from, to) } + } override fun save(change: ConfigPatchDto, done: (KiloAppStateDto?) -> Unit) { - app.updateConfigAsync(change, done) + val value = draft.editor + if (!changed(change)) { + KiloPluginSettings.setAutoEditorContext(value) + done(appState) + return + } + app.updateConfigAsync(change) { result -> + if (result != null) KiloPluginSettings.setAutoEditorContext(value) + done(result) + } } override fun base(result: KiloAppStateDto): ContextDraft = contextDraft(result.config) @@ -136,11 +149,9 @@ internal class ContextSettingsContent( ) : BaseContentPanel() { private val auto = SettingsToggle { value -> update { copy(auto = value) } } // Editor-context auto-include is a local per-IDE preference in PropertiesComponent (like - // autoApprove), applied immediately on toggle rather than through the CLI-backed draft/apply/ - // reset flow used by the other rows. It stays interactive even when the backend isn't READY. - private val editor = SettingsToggle(KiloPluginSettings.getAutoEditorContext()) { value -> - KiloPluginSettings.setAutoEditorContext(value) - } + // autoApprove). It participates in this page's draft/apply/reset state so the Configurable + // Apply button reflects unsaved local changes, but it is never sent as CLI config. + private val editor = SettingsToggle { value -> update { copy(editor = value) } } private val prune = SettingsToggle { value -> update { copy(prune = value) } } private val threshold = ThresholdField( KiloBundle.message("settings.context.compaction.threshold.placeholder"), @@ -186,9 +197,9 @@ internal class ContextSettingsContent( @RequiresEdt fun sync(draft: ContextDraft, enabled: Boolean) { auto.isSelected = draft.auto - // Local preference: reflects PropertiesComponent and stays enabled regardless of the - // CLI-backed [enabled] gating that applies to the draft-driven rows below. - editor.isSelected = KiloPluginSettings.getAutoEditorContext() + // Local preference: draft-driven, but still enabled regardless of the CLI-backed [enabled] + // gating that applies to the remote config rows below. + editor.isSelected = draft.editor editor.isEnabled = true prune.isSelected = draft.prune threshold.sync(draft.threshold) diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/PromptPanelTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/PromptPanelTest.kt index cb787b2b77..63d41c27f2 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/PromptPanelTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/PromptPanelTest.kt @@ -70,6 +70,7 @@ import com.intellij.ui.components.JBLabel import com.intellij.util.Producer import com.intellij.util.ui.EmptyIcon import com.intellij.ui.scale.JBUIScale +import com.intellij.util.DocumentUtil import com.intellij.util.ui.JBUI import com.intellij.util.ui.UIUtil import kotlinx.coroutines.CompletableDeferred @@ -501,6 +502,22 @@ class PromptPanelTest : BasePlatformTestCase() { assertEquals("hello", editor.document.text) } + fun `test prompt editor height sync skips bulk document updates`() { + val panel = PromptPanel(project = project, onSend = { _, _ -> }, onAbort = {}, onEnhance = { _, _ -> }, completion = completion()) + val field = panel.defaultFocusedComponent as EditorTextField + + realize(panel, 260, 400) + val editor = field.getEditor(false)!! + WriteCommandAction.runWriteCommandAction(project) { + DocumentUtil.executeInBulk(editor.document, true) { + editor.document.insertString(0, "hello") + } + } + UIUtil.dispatchAllInvocationEvents() + + assertEquals("hello", editor.document.text) + } + fun `test prompt editor highlights missing mention as wrong reference`() { val panel = PromptPanel(project = project, onSend = { _, _ -> }, onAbort = {}, onEnhance = { _, _ -> }, completion = completion()) val field = panel.defaultFocusedComponent as EditorTextField diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/settings/context/ContextSettingsUiTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/settings/context/ContextSettingsUiTest.kt index ac29c470be..44337dd149 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/settings/context/ContextSettingsUiTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/settings/context/ContextSettingsUiTest.kt @@ -274,7 +274,7 @@ class ContextSettingsUiTest : BasePlatformTestCase() { flushUntil { rpc.configPatches.isNotEmpty() } } - fun `test editor context toggle persists immediately without a config patch`() { + fun `test editor context toggle marks modified and applies without a config patch`() { val panel = requireUi() assertTrue(KiloPluginSettings.getAutoEditorContext()) @@ -282,10 +282,14 @@ class ContextSettingsUiTest : BasePlatformTestCase() { val editor = editorToggle(panel) assertTrue(editor.isEnabled) editor.doClick() + assertTrue(panel.modified()) } + assertTrue(KiloPluginSettings.getAutoEditorContext()) + edt { panel.applyDraft() } assertFalse(KiloPluginSettings.getAutoEditorContext()) edt { UIUtil.dispatchAllInvocationEvents() } + assertFalse(edt { panel.modified() }) assertTrue(rpc.configPatches.isEmpty()) } From e48c534978e5864662f9f155815c029cfe549f30 Mon Sep 17 00:00:00 2001 From: Josh Lambert Date: Mon, 10 Aug 2026 10:32:19 -0400 Subject: [PATCH 07/14] fix(tui): separate autocomplete descriptions from labels --- .changeset/tidy-autocomplete-labels.md | 5 +++++ packages/tui/src/component/prompt/autocomplete.tsx | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 .changeset/tidy-autocomplete-labels.md diff --git a/.changeset/tidy-autocomplete-labels.md b/.changeset/tidy-autocomplete-labels.md new file mode 100644 index 0000000000..df7c9a8c34 --- /dev/null +++ b/.changeset/tidy-autocomplete-labels.md @@ -0,0 +1,5 @@ +--- +"@kilocode/cli": patch +--- + +Separate autocomplete item names from their descriptions in the TUI. diff --git a/packages/tui/src/component/prompt/autocomplete.tsx b/packages/tui/src/component/prompt/autocomplete.tsx index 8f9f983c7b..489e6ae76d 100644 --- a/packages/tui/src/component/prompt/autocomplete.tsx +++ b/packages/tui/src/component/prompt/autocomplete.tsx @@ -831,13 +831,14 @@ export function Autocomplete(props: { moveTo(index) }} onMouseUp={() => select()} + gap={1} // kilocode_change - keep descriptions separated from labels in flex layout > {option().display} - {" " + option().description?.trimStart()} + {option().description?.trimStart()}{/* kilocode_change */} From 5c08bda580eb34b2f7fdb4ba009a0d2e7bb79e74 Mon Sep 17 00:00:00 2001 From: kirillk Date: Mon, 10 Aug 2026 12:07:16 -0400 Subject: [PATCH 08/14] fix(jetbrains): underline prompt attachment file links Remove the remaining prompt attachment strip border and reuse the shared underlined file-link styling for attachment chip labels so clickable files look consistent in the transcript. --- .../client/session/ui/FileLinkText.kt | 14 ++++++++ .../session/ui/attachment/AttachmentCard.kt | 3 +- .../session/views/PromptAttachmentView.kt | 5 +-- .../client/session/views/tool/ToolSupport.kt | 15 ++++----- .../client/session/ui/SessionUiUpdateTest.kt | 2 +- .../session/views/PromptAttachmentViewTest.kt | 32 +++++++++++++++++-- 6 files changed, 56 insertions(+), 15 deletions(-) create mode 100644 packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/FileLinkText.kt diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/FileLinkText.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/FileLinkText.kt new file mode 100644 index 0000000000..bcc402ef85 --- /dev/null +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/FileLinkText.kt @@ -0,0 +1,14 @@ +package ai.kilocode.client.session.ui + +import com.intellij.xml.util.XmlStringUtil + +internal fun fileLinkText(value: String): String = value.lineSequence() + .map { it.trim() } + .filter { it.isNotEmpty() } + .joinToString(" ") + +internal fun fileLinkHtml(value: String): String { + val text = fileLinkText(value) + if (text.isBlank()) return "" + return XmlStringUtil.wrapInHtml("${XmlStringUtil.escapeString(text)}") +} diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/attachment/AttachmentCard.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/attachment/AttachmentCard.kt index a54e683355..b5b3d3ebc3 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/attachment/AttachmentCard.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/attachment/AttachmentCard.kt @@ -1,6 +1,7 @@ package ai.kilocode.client.session.ui.attachment import ai.kilocode.client.plugin.KiloBundle +import ai.kilocode.client.session.ui.fileLinkHtml import ai.kilocode.client.session.ui.style.SessionUiStyle import ai.kilocode.client.ui.UiStyle import ai.kilocode.client.ui.iconButton @@ -68,7 +69,7 @@ class AttachmentChip( cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR) toolTipText = tip accessibleContext?.accessibleName = KiloBundle.message("prompt.attachment.open", item.name) - val label = JBLabel(label()).apply { + val label = JBLabel(fileLinkHtml(label())).apply { icon = attachmentIcon(item.mime, item.name) iconTextGap = JBUI.scale(SessionUiStyle.View.Attachment.CHIP_ICON_GAP) toolTipText = tip diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/PromptAttachmentView.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/PromptAttachmentView.kt index 919b114fe5..331499655e 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/PromptAttachmentView.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/PromptAttachmentView.kt @@ -27,8 +27,9 @@ class PromptAttachmentView( private val cards = LinkedHashMap() private val row = Stack.horizontal(gap = UiStyle.Gap.sm()) private val scroll = JBScrollPane(row).apply { - border = null - viewportBorder = null + // Empty borders remove the visible scroll pane frame; null can be replaced by the current UI. + border = JBUI.Borders.empty() + viewportBorder = JBUI.Borders.empty() isOpaque = false viewport.isOpaque = false horizontalScrollBarPolicy = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/ToolSupport.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/ToolSupport.kt index a17b164e31..3eabce5da9 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/ToolSupport.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/ToolSupport.kt @@ -7,6 +7,8 @@ import ai.kilocode.client.session.SessionFileOpener import ai.kilocode.client.session.model.Tool import ai.kilocode.client.session.model.ToolExecState import ai.kilocode.client.session.model.ToolKind +import ai.kilocode.client.session.ui.fileLinkHtml +import ai.kilocode.client.session.ui.fileLinkText import ai.kilocode.client.session.ui.selection.SessionSelection import ai.kilocode.client.session.ui.selection.SessionCopyTarget import ai.kilocode.client.session.ui.style.SessionEditorStyle @@ -135,8 +137,8 @@ class FileLinkLabel( @RequiresEdt fun setTarget(path: String?, text: String): Boolean { - val next = single(text.ifBlank { path.orEmpty() }) - val value = if (next.isBlank()) "" else XmlStringUtil.wrapInHtml("${XmlStringUtil.escapeString(next)}") + val next = fileLinkText(text.ifBlank { path.orEmpty() }) + val value = fileLinkHtml(next) var changed = false if (href != path) { href = path @@ -489,7 +491,7 @@ internal fun setText(label: JBLabel, text: String): Boolean { @RequiresEdt internal fun setTargetText(label: JBLabel, text: String): Boolean { - val value = single(text) + val value = fileLinkText(text) if (label.text == value) return false label.text = value return true @@ -511,16 +513,11 @@ private fun clip(label: T): T = label.apply { } private fun html(text: String): String { - val value = single(text) + val value = fileLinkText(text) if (value.isBlank()) return "" return XmlStringUtil.wrapInHtml("${XmlStringUtil.escapeString(value)}") } -private fun single(text: String): String = text.lineSequence() - .map { it.trim() } - .filter { it.isNotEmpty() } - .joinToString(" ") - @RequiresEdt internal fun show(parts: ToolParts, link: Boolean): Boolean { var changed = false diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/SessionUiUpdateTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/SessionUiUpdateTest.kt index 49ff27f583..e85eb69e4c 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/SessionUiUpdateTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/SessionUiUpdateTest.kt @@ -383,7 +383,7 @@ class SessionUiUpdateTest : BasePlatformTestCase() { val chip = find(view, AttachmentChip::class.java) assertNotNull(chip) - assertTrue(findAll(chip!!, JBLabel::class.java).any { it.text == "HvJwtFilter.java:12-40" }) + assertTrue(findAll(chip!!, JBLabel::class.java).any { it.text.contains("HvJwtFilter.java:12-40") }) } fun `test source backed image attachment still renders in prompt strip`() { diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/PromptAttachmentViewTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/PromptAttachmentViewTest.kt index 457a8cdfd8..27ad1470af 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/PromptAttachmentViewTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/PromptAttachmentViewTest.kt @@ -5,6 +5,8 @@ import ai.kilocode.client.session.ui.attachment.AttachmentCardItem import ai.kilocode.client.session.ui.attachment.AttachmentChip import ai.kilocode.client.ui.UiStyle import com.intellij.testFramework.fixtures.BasePlatformTestCase +import com.intellij.ui.components.JBLabel +import java.awt.Container class PromptAttachmentViewTest : BasePlatformTestCase() { // The attachment strip should line up with the prompt text horizontally and keep only a @@ -22,8 +24,14 @@ class PromptAttachmentViewTest : BasePlatformTestCase() { fun `test attachment scroll pane has no border line`() { val scroll = PromptAttachmentView("m1") {}.scrollPane() - assertNull(scroll.border) - assertNull(scroll.viewportBorder) + assertEquals(0, scroll.border.getBorderInsets(scroll).top) + assertEquals(0, scroll.border.getBorderInsets(scroll).left) + assertEquals(0, scroll.border.getBorderInsets(scroll).bottom) + assertEquals(0, scroll.border.getBorderInsets(scroll).right) + assertEquals(0, scroll.viewportBorder.getBorderInsets(scroll).top) + assertEquals(0, scroll.viewportBorder.getBorderInsets(scroll).left) + assertEquals(0, scroll.viewportBorder.getBorderInsets(scroll).bottom) + assertEquals(0, scroll.viewportBorder.getBorderInsets(scroll).right) } // With the outline removed, the chip owns no internal padding; alignment comes from the @@ -41,4 +49,24 @@ class PromptAttachmentViewTest : BasePlatformTestCase() { assertEquals(0, chip.top) assertEquals(0, chip.bottom) } + + fun `test attachment chip uses file link underline style`() { + val chip = AttachmentChip( + AttachmentCardItem("HvJwtFilter.java", "text/plain", "file:///HvJwtFilter.java"), + file = true, + startLine = 40, + endLine = 42, + ) + val label = components(chip).filterIsInstance().single() + + assertTrue(label.text.contains("HvJwtFilter.java:40-42")) + } + + private fun components(root: Container): List = buildList { + fun visit(comp: java.awt.Component) { + add(comp) + if (comp is Container) comp.components.forEach { visit(it) } + } + visit(root) + } } From ad933a31dfa9c71a714e3675ba513118accc900a Mon Sep 17 00:00:00 2001 From: kirillk Date: Mon, 10 Aug 2026 13:34:46 -0400 Subject: [PATCH 09/14] feat(jetbrains): align prompt background with code fragments Give the prompt input and the transcript user-prompt bubble a dedicated background that defaults to the editor's code-fragment (code block) color, via a new Kilo.Session.Prompt.Background theme key so it can be restyled independently. Attachment strip is transparent so the prompt surface shows through, and the prompt-bar mode/model/effort pickers now blend into that surface when idle while keeping the standard hover fill. The transcript bubble drops its outline unless the prompt shares the session background, in which case it keeps the original outline for contrast. --- .../ui/prompt/PromptAttachmentStrip.kt | 3 +++ .../client/session/ui/prompt/PromptPanel.kt | 16 ++++++----- .../session/ui/style/SessionEditorStyle.kt | 12 ++++----- .../client/session/ui/style/SessionUiStyle.kt | 10 +++++++ .../client/session/views/MessageView.kt | 23 +++++++++------- .../client/session/views/PromptView.kt | 2 +- .../ai/kilocode/client/ui/PickerButton.kt | 27 +++++++++++++------ .../kotlin/ai/kilocode/client/ui/UiStyle.kt | 10 +++++++ .../ai/kilocode/client/ui/md/MdCommon.kt | 2 +- .../client/session/ui/PromptPanelTest.kt | 19 ++++++++----- .../client/session/views/TextViewTest.kt | 4 +-- 11 files changed, 87 insertions(+), 41 deletions(-) diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/prompt/PromptAttachmentStrip.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/prompt/PromptAttachmentStrip.kt index 9efa44aabf..f4d850bdc6 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/prompt/PromptAttachmentStrip.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/prompt/PromptAttachmentStrip.kt @@ -23,6 +23,9 @@ class PromptAttachmentStrip( private val chips = LinkedHashMap() init { + // Transparent so the prompt shell background shows through instead of the strip + // painting its own panel background above the input surface. + isOpaque = false border = JBUI.Borders.emptyBottom(UiStyle.Gap.sm()) isVisible = false } diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/prompt/PromptPanel.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/prompt/PromptPanel.kt index 1b5b656b85..7a4fcd3297 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/prompt/PromptPanel.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/prompt/PromptPanel.kt @@ -127,11 +127,14 @@ class PromptPanel( private val INVALID_KEY = CodeInsightColors.WRONG_REFERENCES_ATTRIBUTES } - val mode = ModePicker() + // Prompt-bar pickers blend into the prompt background when idle and only show the standard + // hover fill on pointer-over (idleFill = null paints nothing behind the label). + val mode = ModePicker().apply { idleFill = null } val model = ModelPicker().apply { placement = ModelPicker.Placement.ABOVE + idleFill = null } - val reasoning = ReasoningPicker() + val reasoning = ReasoningPicker().apply { idleFill = null } var onReset: () -> Unit = {} var onChange: () -> Unit = {} var onAutoApproveToggle: (Boolean) -> Unit = {} @@ -411,7 +414,7 @@ class PromptPanel( @RequiresEdt private fun chrome(ed: EditorEx) { if (ed.isDisposed) return - style.applyPromptToEditor(ed) + style.applyPromptToEditor(ed, SessionUiStyle.View.Prompt.bgColor(style)) if (ed.isDisposed) return } @@ -497,11 +500,12 @@ class PromptPanel( @RequiresEdt override fun applyStyle(style: SessionEditorStyle) { this.style = style - background = style.editorScheme.defaultBackground - shell.background = style.editorScheme.defaultBackground + val bg = SessionUiStyle.View.Prompt.bgColor(style) + background = bg + shell.background = bg style.applyTranscriptToField(editor) editor.getEditor(false)?.let(::chrome) - editor.background = style.editorBackground + editor.background = bg syncEditorHeight() syncAutoApprove() syncHighlights() diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/style/SessionEditorStyle.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/style/SessionEditorStyle.kt index 1a71b24c65..66d4c18fef 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/style/SessionEditorStyle.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/style/SessionEditorStyle.kt @@ -80,7 +80,7 @@ data class SessionEditorStyle( } /** Apply the visible prompt-input text styling to embedded session editor components. */ - fun applyPromptToEditor(editor: EditorEx) { + fun applyPromptToEditor(editor: EditorEx, background: Color = editorBackground) { if (editor.isDisposed) return applyTranscriptToEditor(editor) if (editor.isDisposed) return @@ -92,11 +92,11 @@ data class SessionEditorStyle( 0, JBUI.scale(SessionUiStyle.View.Prompt.EDITOR_HORIZONTAL_INSET), ) - editor.backgroundColor = editorBackground - editor.component.background = editorBackground - editor.contentComponent.background = editorBackground - editor.scrollPane.background = editorBackground - editor.scrollPane.viewport.background = editorBackground + editor.backgroundColor = background + editor.component.background = background + editor.contentComponent.background = background + editor.scrollPane.background = background + editor.scrollPane.viewport.background = background editor.scrollPane.horizontalScrollBarPolicy = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER editor.scrollPane.revalidate() editor.scrollPane.repaint() diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/style/SessionUiStyle.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/style/SessionUiStyle.kt index f610298b9b..9a3af597cf 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/style/SessionUiStyle.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/style/SessionUiStyle.kt @@ -96,6 +96,16 @@ object SessionUiStyle { /** Prompt input dimensions and chrome inside the session view. */ object Prompt { + /** + * Background of the prompt input and the transcript user-prompt bubble. Uses a dedicated + * theme key so the prompt surface can be restyled independently, defaulting to the + * code-fragment background so the prompt matches rendered code blocks. + */ + fun bgColor(style: SessionEditorStyle): Color = JBColor.namedColor( + "Kilo.Session.Prompt.Background", + UiStyle.Colors.codeBlockBackground(style.editorScheme), + ) + const val EDITOR_LINES = 1 const val EDITOR_CHROME = 16 const val SEND_BUTTON_SIZE = 24 diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/MessageView.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/MessageView.kt index 7a55bc0d08..710c0f9335 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/MessageView.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/MessageView.kt @@ -99,7 +99,7 @@ class MessageView( init { isOpaque = false - if (msg.info.role == SessionUiStyle.View.Message.USER_ROLE) background = style.editorScheme.defaultBackground + if (msg.info.role == SessionUiStyle.View.Message.USER_ROLE) background = SessionUiStyle.View.Prompt.bgColor(style) border = assistantBorder() // Populate content that already exists (e.g. after loadHistory) @@ -439,7 +439,7 @@ class MessageView( @RequiresEdt override fun applyStyle(style: SessionEditorStyle) { this.style = style - if (msg.info.role == SessionUiStyle.View.Message.USER_ROLE) background = style.editorScheme.defaultBackground + if (msg.info.role == SessionUiStyle.View.Message.USER_ROLE) background = SessionUiStyle.View.Prompt.bgColor(style) for (view in parts.values) view.applyStyle(style) refresh() } @@ -481,14 +481,17 @@ class MessageView( g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON) val arc = JBUI.scale(JBUI.getInt("Button.arc", SessionUiStyle.View.Prompt.CORNER_ARC)) val pt = if (box === this) Point() else SwingUtilities.convertPoint(box, Point(), this) - val x = pt.x - val y = pt.y - val w = box.width - 1 - val h = box.height - 1 - g2.color = style.editorScheme.defaultBackground - g2.fillRoundRect(x, y, box.width, box.height, arc, arc) - g2.color = SessionUiStyle.View.Outline.color() - if (w > 0 && h > 0) g2.drawRoundRect(x, y, w, h, arc, arc) + val bg = SessionUiStyle.View.Prompt.bgColor(style) + g2.color = bg + g2.fillRoundRect(pt.x, pt.y, box.width, box.height, arc, arc) + // When the prompt shares the session background there is no fill contrast, so draw the + // outline to keep the bubble visible. + if (bg.rgb == style.editorBackground.rgb) { + val w = box.width - 1 + val h = box.height - 1 + g2.color = SessionUiStyle.View.Outline.color() + if (w > 0 && h > 0) g2.drawRoundRect(pt.x, pt.y, w, h, arc, arc) + } } finally { g2.dispose() } diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/PromptView.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/PromptView.kt index 7d3f7e267e..32ddf7c21e 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/PromptView.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/PromptView.kt @@ -74,7 +74,7 @@ class PromptView( override fun styleFont(style: SessionEditorStyle) = style.transcriptFont - override fun styleBackground(style: SessionEditorStyle) = style.editorBackground + override fun styleBackground(style: SessionEditorStyle) = SessionUiStyle.View.Prompt.bgColor(style) private fun sync() { md.set(linkifyMentions(buffer.toString(), mentions)) diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/PickerButton.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/PickerButton.kt index 86b1b6763e..f2ccbdd856 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/PickerButton.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/PickerButton.kt @@ -2,6 +2,7 @@ package ai.kilocode.client.ui import com.intellij.ui.components.JBLabel import com.intellij.util.ui.JBUI +import java.awt.Color import java.awt.Graphics import java.awt.Graphics2D import java.awt.RenderingHints @@ -11,6 +12,13 @@ import java.awt.event.MouseEvent open class PickerButton : JBLabel() { private var over = false + /** + * Idle (unhovered) fill. Defaults to the standard picker surface; set to `null` to paint + * nothing so the picker blends into its container (e.g. the prompt background). The hover + * fill is unaffected. + */ + var idleFill: Color? = UiStyle.Colors.picker() + init { border = pickerBorder() background = UiStyle.Colors.picker() @@ -34,14 +42,17 @@ open class PickerButton : JBLabel() { } override fun paintComponent(g: Graphics) { - val g2 = g.create() as Graphics2D - try { - g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON) - g2.color = if (isEnabled && over) JBUI.CurrentTheme.ActionButton.hoverBackground() else UiStyle.Colors.picker() - val arc = JBUI.scale(JBUI.getInt("Button.arc", 6)) - g2.fillRoundRect(0, 0, width, height, arc, arc) - } finally { - g2.dispose() + val fill = if (isEnabled && over) JBUI.CurrentTheme.ActionButton.hoverBackground() else idleFill + if (fill != null) { + val g2 = g.create() as Graphics2D + try { + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON) + g2.color = fill + val arc = JBUI.scale(JBUI.getInt("Button.arc", 6)) + g2.fillRoundRect(0, 0, width, height, arc, arc) + } finally { + g2.dispose() + } } super.paintComponent(g) } diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/UiStyle.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/UiStyle.kt index 7e71120466..b874ed7491 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/UiStyle.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/UiStyle.kt @@ -1,6 +1,8 @@ package ai.kilocode.client.ui +import com.intellij.openapi.editor.DefaultLanguageHighlighterColors import com.intellij.openapi.editor.colors.EditorColorsManager +import com.intellij.openapi.editor.colors.EditorColorsScheme import com.intellij.ui.JBColor import com.intellij.util.ui.JBFont import com.intellij.util.ui.JBUI @@ -118,6 +120,14 @@ object UiStyle { /** Uses the editor background so chat cards feel native beside editor content. */ fun editorBackground(): Color = JBColor.lazy { EditorColorsManager.getInstance().globalScheme.defaultBackground } + /** + * Background for rendered code fragments (markdown code blocks). Uses the editor's doc + * code-block attribute background and falls back to the editor background when the theme + * leaves it unset. + */ + fun codeBlockBackground(scheme: EditorColorsScheme): Color = + scheme.getAttributes(DefaultLanguageHighlighterColors.DOC_CODE_BLOCK)?.backgroundColor ?: scheme.defaultBackground + /** * Contained panel background: follows the active theme's text-field/input surface. * Falls back to the panel background when unavailable. diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/md/MdCommon.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/md/MdCommon.kt index ac73c1400a..d95db32ee0 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/md/MdCommon.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/md/MdCommon.kt @@ -94,7 +94,7 @@ internal object MdCommon { ?: fg(style, DefaultLanguageHighlighterColors.DOC_COMMENT) ?: UIUtil.getContextHelpForeground() val border = color(style, EditorColors.PREVIEW_BORDER_COLOR) ?: UiStyle.Colors.contentBorder() - val blockBg = bg(style, DefaultLanguageHighlighterColors.DOC_CODE_BLOCK) ?: style.editorBackground + val blockBg = UiStyle.Colors.codeBlockBackground(style.editorScheme) return MdStyle( font = style.transcriptFont, foreground = style.editorForeground, diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/PromptPanelTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/PromptPanelTest.kt index 63d41c27f2..3f9c0e22fa 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/PromptPanelTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/PromptPanelTest.kt @@ -138,11 +138,11 @@ class PromptPanelTest : BasePlatformTestCase() { assertEquals(style.transcriptFont.size, font.size) } - fun `test prompt input uses editor background`() { + fun `test prompt input uses prompt background`() { val style = SessionEditorStyle.current() val panel = PromptPanel(project = project, onSend = { _, _ -> }, onAbort = {}, onEnhance = { _, _ -> }) - assertEquals(style.editorScheme.defaultBackground, panel.defaultFocusedComponent.background) + assertEquals(SessionUiStyle.View.Prompt.bgColor(style), panel.defaultFocusedComponent.background) } fun `test prompt editor hides floating toolbar`() { @@ -249,7 +249,12 @@ class PromptPanelTest : BasePlatformTestCase() { HighlighterColors.TEXT, TextAttributes(Color(0xEA, 0xEA, 0xEA), bg, null, null, Font.PLAIN), ) + scheme.setAttributes( + DefaultLanguageHighlighterColors.DOC_CODE_BLOCK, + TextAttributes(null, bg, null, null, Font.PLAIN), + ) val style = SessionEditorStyle.create(scheme = scheme) + val promptBg = SessionUiStyle.View.Prompt.bgColor(style) realize(panel, 260, 400) val editor = (panel.defaultFocusedComponent as EditorTextField).getEditor(false)!! @@ -259,11 +264,11 @@ class PromptPanelTest : BasePlatformTestCase() { panel.applyStyle(style) - assertEquals(bg, panel.defaultFocusedComponent.background) - assertEquals(bg, editor.backgroundColor) - assertEquals(bg, editor.scrollPane.background) - assertEquals(bg, editor.scrollPane.viewport.background) - assertEquals(bg, editor.contentComponent.background) + assertEquals(promptBg, panel.defaultFocusedComponent.background) + assertEquals(promptBg, editor.backgroundColor) + assertEquals(promptBg, editor.scrollPane.background) + assertEquals(promptBg, editor.scrollPane.viewport.background) + assertEquals(promptBg, editor.contentComponent.background) } fun `test prompt editor grows when lines are added`() { diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/TextViewTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/TextViewTest.kt index 14bfaea526..4f6d118dfe 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/TextViewTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/TextViewTest.kt @@ -229,14 +229,14 @@ class TextViewTest : BasePlatformTestCase() { assertEquals(style.editorForeground, view.md.foreground) } - fun `test prompt view uses transcript font and editor background`() { + fun `test prompt view uses transcript font and prompt background`() { val style = SessionEditorStyle.create(family = "Courier New", size = 23) val view = PromptView(Text("p1")) view.applyStyle(style) assertEquals(style.transcriptFont, view.md.font) - assertEquals(style.editorBackground, view.md.background) + assertEquals(SessionUiStyle.View.Prompt.bgColor(style), view.md.background) assertFalse(view.contentOpaque()) } From 9f32194345f707f94ac17da953833604e1a8d16f Mon Sep 17 00:00:00 2001 From: kirillk Date: Mon, 10 Aug 2026 14:11:34 -0400 Subject: [PATCH 10/14] test(jetbrains): stabilize ContextSettingsStateTest against platform app ContextDraft's default editor value reads a PropertiesComponent app service, so constructing it requires an initialized IntelliJ Application. As a plain unit test this only passed when another BasePlatformTestCase initialized the app first in the same fork, making it order-dependent. Extend BasePlatformTestCase so the Application is always available. --- .../context/ContextSettingsStateTest.kt | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/settings/context/ContextSettingsStateTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/settings/context/ContextSettingsStateTest.kt index 8fc6dd6179..d7c285627f 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/settings/context/ContextSettingsStateTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/settings/context/ContextSettingsStateTest.kt @@ -3,15 +3,16 @@ package ai.kilocode.client.settings.context import ai.kilocode.rpc.dto.CompactionConfigDto import ai.kilocode.rpc.dto.ConfigDto import ai.kilocode.rpc.dto.WatcherConfigDto -import kotlin.test.Test +import com.intellij.testFramework.fixtures.BasePlatformTestCase import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertNull import kotlin.test.assertTrue -class ContextSettingsStateTest { - @Test - fun `draft reads context config`() { +// Extends BasePlatformTestCase so the IntelliJ Application is initialized: ContextDraft's default +// editor value reads a PropertiesComponent app service, which is null in a plain unit test. +class ContextSettingsStateTest : BasePlatformTestCase() { + fun `test draft reads context config`() { val draft = contextDraft(ConfigDto( watcher = WatcherConfigDto(ignore = listOf("**/dist/**")), compaction = CompactionConfigDto(auto = true, threshold_percent = 75.0, prune = true), @@ -23,15 +24,13 @@ class ContextSettingsStateTest { assertEquals(listOf("**/dist/**"), draft.ignore) } - @Test - fun `unchanged draft emits no patch`() { + fun `test unchanged draft emits no patch`() { val draft = ContextDraft(auto = true, threshold = "75", prune = false, ignore = listOf("tmp/**")) assertEquals(false, patch(draft, draft)?.let(::changed)) } - @Test - fun `boolean false values are emitted`() { + fun `test boolean false values are emitted`() { val from = ContextDraft(auto = true, prune = true) val to = ContextDraft(auto = false, prune = false) val patch = patch(from, to) @@ -40,8 +39,7 @@ class ContextSettingsStateTest { assertEquals(false, patch?.compaction?.prune) } - @Test - fun `threshold set and clear use explicit semantics`() { + fun `test threshold set and clear use explicit semantics`() { val from = ContextDraft(threshold = "") val set = ContextDraft(threshold = "80") val clear = ContextDraft(threshold = "") @@ -51,16 +49,14 @@ class ContextSettingsStateTest { assertNull(patch(set, clear)?.compaction?.threshold_percent) } - @Test - fun `watcher empty list is emitted`() { + fun `test watcher empty list is emitted`() { val from = ContextDraft(ignore = listOf("**/dist/**")) val to = ContextDraft(ignore = emptyList()) - assertEquals(emptyList(), patch(from, to)?.watcher?.ignore) + assertEquals(emptyList(), patch(from, to)?.watcher?.ignore) } - @Test - fun `invalid threshold prevents patch without looking like no changes`() { + fun `test invalid threshold prevents patch without looking like no changes`() { val from = ContextDraft(threshold = "50") val to = ContextDraft(auto = true, threshold = "101", prune = true, ignore = listOf("tmp/**")) @@ -68,8 +64,7 @@ class ContextSettingsStateTest { assertNull(patch(from, to)) } - @Test - fun `saved match normalizes threshold formatting`() { + fun `test saved match normalizes threshold formatting`() { assertTrue(savedMatches(ContextDraft(threshold = "75"), ContextDraft(threshold = "75.0"))) assertFalse(savedMatches(ContextDraft(threshold = "75"), ContextDraft(threshold = "76"))) } From 025bbdfe93f2f146ce83c12db390ed05ca638179 Mon Sep 17 00:00:00 2001 From: kirillk Date: Mon, 10 Aug 2026 15:27:26 -0400 Subject: [PATCH 11/14] fix(jetbrains): preserve session scroll follow intent Use the sticky tail-follow intent instead of momentary viewport position when deciding whether batched session updates and newly sent prompts should continue auto-scrolling. This prevents turn-close layout changes, such as modified-files cards plus progress footer removal, from cancelling an in-flight follow-to-bottom pass. --- .../ai/kilocode/client/session/SessionUi.kt | 4 +- .../client/session/SessionScrollTest.kt | 55 +++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt index 5068db342e..28c4cd1750 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt @@ -156,7 +156,7 @@ class SessionUi( condense = Registry.`is`("kilo.session.condense", true), displayMs = displayMs, open = { item -> manager?.openSession(item) }, - beforeUpdate = { if (opening) false else scroll.atBottom() }, + beforeUpdate = { if (opening) false else scroll.following() }, afterUpdate = { if (!opening) scroll.followBottom(it) }, loaded = ::onSessionLoaded, openProfileAction = ::openProfileSettings, @@ -684,7 +684,7 @@ class SessionUi( private fun sendPrompt(text: String, files: List) { if (text.isBlank() && files.isEmpty()) return prompt.clear() - val follow = scroll.atBottom() + val follow = scroll.following() val action = completion.clientAction(text) if (action != null) { action.action() diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/SessionScrollTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/SessionScrollTest.kt index 70d9ffeec8..f9685fd2c5 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/SessionScrollTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/SessionScrollTest.kt @@ -498,6 +498,61 @@ class SessionScrollTest : SessionUiTestBase() { assertFalse(jumpButton().isVisible) } + fun `test turn close after modified files keeps pending tail follow`() { + showMessages() + fillTranscript(24) + val bar = scrollBar() + setBottom(bar) + + emit(ChatEventDto.TurnOpen("ses_test")) + drainScroll() + assertBottom(bar) + assertTrue(ui.scroll.following()) + val id = "modified_close_tail" + val pid = "modified_close_part" + emit(ChatEventDto.MessageUpdated("ses_test", message(id).copy(summary = MessageSummaryDto(listOf(modifiedFile())))), flush = false) + emit(ChatEventDto.PartUpdated("ses_test", part(pid, id, "text", "tail line\n".repeat(160))), flush = false) + forceFlushWithoutDispatch() + + emit(ChatEventDto.TurnClose("ses_test", "completed")) + drainScroll() + + assertBottom(bar) + assertTrue(ui.scroll.following()) + assertFalse(jumpButton().isVisible) + + findAll(ui).first().text = "next prompt" + find(ui).send() + settleShort(100) + val text = rpc.prompts.last().third.parts.single().text + val next = "modified_close_next" + emit(ChatEventDto.MessageUpdated("ses_test", message(next)), flush = false) + emit(ChatEventDto.PartUpdated("ses_test", part("modified_close_next_part", next, "text", text)), flush = false) + forceFlush() + drainScroll() + + assertBottom(bar) + assertFalse(jumpButton().isVisible) + } + + fun `test turn close after modified files preserves user scroll position`() { + showMessages() + fillTranscript(24) + val bar = scrollBar() + setValue(bar, bottom(bar) / 2) + val value = bar.value + + emit(ChatEventDto.TurnOpen("ses_test"), flush = false) + emit(ChatEventDto.MessageUpdated("ses_test", message("modified_close_middle").copy(summary = MessageSummaryDto(listOf(modifiedFile())))), flush = false) + emit(ChatEventDto.TurnClose("ses_test", "completed"), flush = false) + forceFlush() + drainScroll() + + assertEquals(value, bar.value) + assertFalse(ui.scroll.following()) + assertTrue(jumpButton().isVisible) + } + fun `test prompt editor growth preserves middle scroll position`() { showMessages() fillTranscript(24) From 9f2e04cee31c7a16330a5980ff6d961c33f35617 Mon Sep 17 00:00:00 2001 From: "kilo-maintainer[bot]" Date: Mon, 10 Aug 2026 20:24:05 +0000 Subject: [PATCH 12/14] release(jetbrains): v7.0.15 --- packages/kilo-jetbrains/CHANGELOG.md | 50 +++++++++++++++++++++++ packages/kilo-jetbrains/gradle.properties | 2 +- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/packages/kilo-jetbrains/CHANGELOG.md b/packages/kilo-jetbrains/CHANGELOG.md index 168b62f5ea..17d8fb7d08 100644 --- a/packages/kilo-jetbrains/CHANGELOG.md +++ b/packages/kilo-jetbrains/CHANGELOG.md @@ -128,6 +128,56 @@ ## [Unreleased] +## [7.0.15] - 2026-08-10 + +### Added +- feat(vscode): sync model selector with slash command overrides by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12982 +- feat(review): add nested suggestions and subcommands for review by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12991 +- feat(docs): Add detailed instructions on how to install the Slack bot by @RSO in https://github.com/Kilo-Org/kilocode/pull/13011 +- feat(vscode): show approval reason outside workspace reads and writes by @bagatao-anaconda in https://github.com/Kilo-Org/kilocode/pull/13001 +- feat(cli): set prompt cache breakpoints on stable prefix and user query before environment details by @chrarnoldus in https://github.com/Kilo-Org/kilocode/pull/13022 +- feat(vscode): show Auto model routes for every kilo-auto model by @chrarnoldus in https://github.com/Kilo-Org/kilocode/pull/13034 +- feat(cli): answer sessionless model catalog requests by @iscekic in https://github.com/Kilo-Org/kilocode/pull/13014 +- feat(cli): exclude ChatGPT subscriptions from explicit promptCacheBreakpoint treatment by @chrarnoldus in https://github.com/Kilo-Org/kilocode/pull/13044 +- feat(vscode): improve custom provider dialog layout and model toggles by @chrarnoldus in https://github.com/Kilo-Org/kilocode/pull/13046 +- feat(vscode): Agent Manager PR View Panel by @cosi-conda in https://github.com/Kilo-Org/kilocode/pull/12961 +- feat(jetbrains): add editor context and prompt attachments by @kirillk in https://github.com/Kilo-Org/kilocode/pull/13015 + +### Fixed +- fix(agent-manager): align multi-project header actions by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12975 +- fix(cli): remove unsupported kilo web command by @chrarnoldus in https://github.com/Kilo-Org/kilocode/pull/12978 +- fix(vscode): compress speech-to-text audio to AAC to prevent payload errors by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12983 +- fix(vscode): toggle Agent Manager terminal from toolbar button by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12985 +- fix(vscode): sidebar top bar regression by @bagatao-anaconda in https://github.com/Kilo-Org/kilocode/pull/12988 +- fix(cli): apply saved sandbox settings to existing sessions by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12600 +- fix(vscode): improve narrow recent sessions layout by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12819 +- fix(vscode): align xterm terminal on narrow sidebar width by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12989 +- fix(vscode): tool approval source display by @bagatao-anaconda in https://github.com/Kilo-Org/kilocode/pull/12995 +- fix(vscode): surface macOS speech capture errors by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12821 +- fix(agent-manager): route tool requests by project directory and handle busy sessions by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12993 +- fix(vscode): surface exit signals and spawn errors in server startup diagnostics by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/13008 +- fix(ui): prevent snap-to-bottom and flickering during upward session scroll by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/13009 +- fix(cli): disable suggest tool in non-interactive runs by @chrarnoldus in https://github.com/Kilo-Org/kilocode/pull/13007 +- fix(vscode): infer custom provider reasoning efforts by @chrarnoldus in https://github.com/Kilo-Org/kilocode/pull/12941 +- fix(gateway): ignore negative prices in model catalog by @chrarnoldus in https://github.com/Kilo-Org/kilocode/pull/13040 +- fix(memory): quiet transient capture timeouts and stop same-turn retries by @johnnyeric in https://github.com/Kilo-Org/kilocode/pull/12958 +- fix(ui): Mermaid Copy PNG/SVG should copy images, not fail or copy markup by @fxnie in https://github.com/Kilo-Org/kilocode/pull/12981 +- fix(vscode): keep session errors visible by @chrarnoldus in https://github.com/Kilo-Org/kilocode/pull/13045 +- fix(config): expand MCP header env refs without wiping MCP set by @arimu1 in https://github.com/Kilo-Org/kilocode/pull/12554 + +### Changed +- release(jetbrains): v7.0.14 by @kilo-maintainer[bot] in https://github.com/Kilo-Org/kilocode/pull/12966 +- refactor(agent-manager): extract keybinding defaults into keybind-defaults.ts by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12954 +- refactor(vscode): keep large files below line caps by @lambertjosh in https://github.com/Kilo-Org/kilocode/pull/12963 +- docs(vscode): update multi-project Agent Manager guidance by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12973 +- perf(agent-manager): speed up worktree session startup by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12979 +- ci: add domain architecture and state ratchet guards by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12990 +- perf(build): optimize cli and extension compile and test times by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12987 +- refactor(vscode): wrap long skill path tooltips to prevent viewport overflow by @rakshith1928 in https://github.com/Kilo-Org/kilocode/pull/12971 +- docs(kilo-docs): document local session search by @lambertjosh in https://github.com/Kilo-Org/kilocode/pull/13032 +- docs(kilo-docs): correct session paths and scope by @lambertjosh in https://github.com/Kilo-Org/kilocode/pull/13036 + + ## [7.0.14] - 2026-08-06 ### Fixed diff --git a/packages/kilo-jetbrains/gradle.properties b/packages/kilo-jetbrains/gradle.properties index 21e1e5e45b..57b5c1fbb5 100644 --- a/packages/kilo-jetbrains/gradle.properties +++ b/packages/kilo-jetbrains/gradle.properties @@ -1,5 +1,5 @@ kotlin.stdlib.default.dependency=false -kilo.jetbrains.version=7.0.14 +kilo.jetbrains.version=7.0.15 # When true (default) the JetBrains plugin uses the pinned CLI release from package.json. # Set to false ONLY for local dev: generate the client from local source + bundle the local binary. # false is NOT releasable -- production builds fail unless this is true. From 96b28c064e90a8ebabc5ad71abff8d513b6d95a3 Mon Sep 17 00:00:00 2001 From: Kirill Kalishev Date: Mon, 10 Aug 2026 16:54:53 -0400 Subject: [PATCH 13/14] docs(jetbrains): edit changelog for v7.0.15 --- packages/kilo-jetbrains/CHANGELOG.md | 50 ++++------------------------ 1 file changed, 6 insertions(+), 44 deletions(-) diff --git a/packages/kilo-jetbrains/CHANGELOG.md b/packages/kilo-jetbrains/CHANGELOG.md index 17d8fb7d08..8fba3e7e42 100644 --- a/packages/kilo-jetbrains/CHANGELOG.md +++ b/packages/kilo-jetbrains/CHANGELOG.md @@ -131,52 +131,14 @@ ## [7.0.15] - 2026-08-10 ### Added -- feat(vscode): sync model selector with slash command overrides by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12982 -- feat(review): add nested suggestions and subcommands for review by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12991 -- feat(docs): Add detailed instructions on how to install the Slack bot by @RSO in https://github.com/Kilo-Org/kilocode/pull/13011 -- feat(vscode): show approval reason outside workspace reads and writes by @bagatao-anaconda in https://github.com/Kilo-Org/kilocode/pull/13001 -- feat(cli): set prompt cache breakpoints on stable prefix and user query before environment details by @chrarnoldus in https://github.com/Kilo-Org/kilocode/pull/13022 -- feat(vscode): show Auto model routes for every kilo-auto model by @chrarnoldus in https://github.com/Kilo-Org/kilocode/pull/13034 -- feat(cli): answer sessionless model catalog requests by @iscekic in https://github.com/Kilo-Org/kilocode/pull/13014 -- feat(cli): exclude ChatGPT subscriptions from explicit promptCacheBreakpoint treatment by @chrarnoldus in https://github.com/Kilo-Org/kilocode/pull/13044 -- feat(vscode): improve custom provider dialog layout and model toggles by @chrarnoldus in https://github.com/Kilo-Org/kilocode/pull/13046 -- feat(vscode): Agent Manager PR View Panel by @cosi-conda in https://github.com/Kilo-Org/kilocode/pull/12961 -- feat(jetbrains): add editor context and prompt attachments by @kirillk in https://github.com/Kilo-Org/kilocode/pull/13015 +- Include editor context in JetBrains prompts, including the active file, open and visible files, selected text, and shell context when available. +- Show selected text and attached files as prompt attachments in user messages, with clickable links back to source files and selections. +- Add a JetBrains Context setting to enable or disable automatic editor context. ### Fixed -- fix(agent-manager): align multi-project header actions by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12975 -- fix(cli): remove unsupported kilo web command by @chrarnoldus in https://github.com/Kilo-Org/kilocode/pull/12978 -- fix(vscode): compress speech-to-text audio to AAC to prevent payload errors by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12983 -- fix(vscode): toggle Agent Manager terminal from toolbar button by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12985 -- fix(vscode): sidebar top bar regression by @bagatao-anaconda in https://github.com/Kilo-Org/kilocode/pull/12988 -- fix(cli): apply saved sandbox settings to existing sessions by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12600 -- fix(vscode): improve narrow recent sessions layout by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12819 -- fix(vscode): align xterm terminal on narrow sidebar width by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12989 -- fix(vscode): tool approval source display by @bagatao-anaconda in https://github.com/Kilo-Org/kilocode/pull/12995 -- fix(vscode): surface macOS speech capture errors by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12821 -- fix(agent-manager): route tool requests by project directory and handle busy sessions by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12993 -- fix(vscode): surface exit signals and spawn errors in server startup diagnostics by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/13008 -- fix(ui): prevent snap-to-bottom and flickering during upward session scroll by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/13009 -- fix(cli): disable suggest tool in non-interactive runs by @chrarnoldus in https://github.com/Kilo-Org/kilocode/pull/13007 -- fix(vscode): infer custom provider reasoning efforts by @chrarnoldus in https://github.com/Kilo-Org/kilocode/pull/12941 -- fix(gateway): ignore negative prices in model catalog by @chrarnoldus in https://github.com/Kilo-Org/kilocode/pull/13040 -- fix(memory): quiet transient capture timeouts and stop same-turn retries by @johnnyeric in https://github.com/Kilo-Org/kilocode/pull/12958 -- fix(ui): Mermaid Copy PNG/SVG should copy images, not fail or copy markup by @fxnie in https://github.com/Kilo-Org/kilocode/pull/12981 -- fix(vscode): keep session errors visible by @chrarnoldus in https://github.com/Kilo-Org/kilocode/pull/13045 -- fix(config): expand MCP header env refs without wiping MCP set by @arimu1 in https://github.com/Kilo-Org/kilocode/pull/12554 - -### Changed -- release(jetbrains): v7.0.14 by @kilo-maintainer[bot] in https://github.com/Kilo-Org/kilocode/pull/12966 -- refactor(agent-manager): extract keybinding defaults into keybind-defaults.ts by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12954 -- refactor(vscode): keep large files below line caps by @lambertjosh in https://github.com/Kilo-Org/kilocode/pull/12963 -- docs(vscode): update multi-project Agent Manager guidance by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12973 -- perf(agent-manager): speed up worktree session startup by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12979 -- ci: add domain architecture and state ratchet guards by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12990 -- perf(build): optimize cli and extension compile and test times by @marius-kilocode in https://github.com/Kilo-Org/kilocode/pull/12987 -- refactor(vscode): wrap long skill path tooltips to prevent viewport overflow by @rakshith1928 in https://github.com/Kilo-Org/kilocode/pull/12971 -- docs(kilo-docs): document local session search by @lambertjosh in https://github.com/Kilo-Org/kilocode/pull/13032 -- docs(kilo-docs): correct session paths and scope by @lambertjosh in https://github.com/Kilo-Org/kilocode/pull/13036 - +- Avoid JetBrains prompt editor crashes during undo/redo bulk updates. +- Keep completed question and tool views in the correct JetBrains transcript position. +- Keep JetBrains chat pinned to the bottom when a turn finishes after modified-file updates. ## [7.0.14] - 2026-08-06 From a5aaef74a81edaa9b5dac9b6b459d7700b973b62 Mon Sep 17 00:00:00 2001 From: "kilo-maintainer[bot]" Date: Tue, 11 Aug 2026 03:07:28 +0000 Subject: [PATCH 14/14] release: v7.4.21 --- .changeset/agent-manager-sidebar-density.md | 5 - .changeset/agent-manager-terminal-focus.md | 5 - .../align-agent-manager-project-actions.md | 5 - .changeset/auto-model-route-choices.md | 5 - .changeset/bright-model-search.md | 5 - .changeset/bright-project-picker.md | 5 - .changeset/broad-custom-provider-efforts.md | 6 - .../cache-worktree-dialog-selections.md | 5 - .changeset/calm-agent-manager-git-polling.md | 5 - .changeset/calm-location-watchers.md | 6 - .changeset/calm-skill-examples.md | 5 - .changeset/command-model-selector-sync.md | 5 - .changeset/commands-settings-jetbrains.md | 5 - .changeset/compressed-speech-to-text-audio.md | 5 - ...ustom-provider-edit-screen-improvements.md | 6 - ...disable-suggest-in-non-interactive-runs.md | 5 - ...de-chatgpt-from-prompt-cache-breakpoint.md | 5 - .changeset/faster-agent-manager-worktrees.md | 5 - .../fix-agent-manager-terminal-toggle.md | 5 - .changeset/fix-gh-windows-console.md | 5 - .changeset/fix-interactive-terminal-input.md | 5 - .changeset/fix-mcp-env-header-expansion.md | 5 - .../fix-memory-model-timeout-warnings.md | 7 - .../fix-multi-project-agent-manager-tool.md | 6 - .changeset/fix-multi-project-navigation.md | 5 - .changeset/fix-multi-project-progress.md | 5 - .changeset/fix-multi-project-session-scope.md | 5 - .changeset/fix-secondary-sidebar-nav-bar.md | 5 - .changeset/fix-session-scroll-flicker.md | 6 - .../fix-tool-approval-source-display.md | 5 - .changeset/forked-subagent-resume.md | 5 - .changeset/friendly-llamas-manage.md | 5 - .changeset/green-greps-settle.md | 5 - .changeset/ignore-negative-model-prices.md | 8 -- .changeset/instant-agent-manager-terminal.md | 5 - .changeset/jetbrains-auto-editor-context.md | 5 - .changeset/jetbrains-cli-checksums.md | 5 - .changeset/jetbrains-cli-mode-visibility.md | 5 - .changeset/jetbrains-file-drop-references.md | 5 - .changeset/jetbrains-prompt-attachments.md | 5 - .changeset/jetbrains-revert-diff-card.md | 5 - .changeset/jetbrains-slash-completion.md | 5 - .changeset/mermaid-copy-clipboard-images.md | 5 - .changeset/narrow-agent-manager-terminal.md | 5 - .changeset/narrow-sidebar-recent.md | 5 - ...penai-explicit-prompt-cache-breakpoints.md | 5 - .changeset/opencode-v1-17-9-to-v1-17-13.md | 21 --- .changeset/optimize-review-slash-commands.md | 6 - .changeset/persist-project-accordion.md | 5 - .changeset/preserve-model-variants.md | 5 - .changeset/privacy-mode-tui.md | 5 - .changeset/project-local-navigation-hints.md | 5 - .changeset/project-row-isolation.md | 5 - .changeset/prompt-rail-panel-edge.md | 5 - .changeset/quick-git-launches.md | 5 - .changeset/quiet-embedded-logo.md | 5 - .changeset/quiet-jetbrains-watchers.md | 5 - .changeset/quiet-sqlite-lock-errors.md | 5 - .changeset/quiet-tui-config-reloads.md | 5 - .changeset/quiet-tui-disposal-aborts.md | 5 - .changeset/quiet-worktrees-list.md | 5 - .changeset/remote-instance-catalog.md | 5 - .../remove-project-selection-indicator.md | 5 - .changeset/remove-web-command.md | 5 - .changeset/safe-credential-reconciliation.md | 5 - .changeset/sandbox-live-settings.md | 6 - .changeset/session-resume-import.md | 5 - .../show-outside-workspace-approval-reason.md | 5 - .changeset/show-vscode-session-errors.md | 5 - .../single-agent-manager-empty-state.md | 5 - .../surface-backend-exit-diagnostics.md | 5 - .changeset/sync-inspector-width.md | 5 - .changeset/terminal-shortcut-translations.md | 5 - .changeset/tidy-autocomplete-labels.md | 5 - .changeset/vscode-skills-tooltip-wrap.md | 5 - .changeset/worktree-rename-focus.md | 5 - .changeset/worktree-slash-commands.md | 5 - bun.lock | 80 +++++------ package.json | 2 +- packages/client/package.json | 2 +- packages/core/package.json | 2 +- packages/effect-drizzle-sqlite/package.json | 2 +- packages/effect-sqlite-node/package.json | 2 +- packages/extensions/zed/extension.toml | 12 +- packages/http-recorder/package.json | 2 +- packages/httpapi-codegen/package.json | 2 +- packages/kilo-console/package.json | 2 +- packages/kilo-docs/package.json | 2 +- packages/kilo-gateway/package.json | 2 +- packages/kilo-i18n/package.json | 2 +- packages/kilo-indexing/package.json | 2 +- packages/kilo-jetbrains/CHANGELOG.md | 25 ++++ packages/kilo-memory/package.json | 2 +- packages/kilo-sandbox/package.json | 2 +- packages/kilo-telemetry/package.json | 2 +- packages/kilo-ui/package.json | 2 +- packages/kilo-vscode/CHANGELOG.md | 130 ++++++++++++++++++ packages/kilo-vscode/package.json | 2 +- packages/kilo-vscode/tests/package.json | 2 +- packages/kilo-web-ui/package.json | 2 +- packages/llm/package.json | 2 +- packages/opencode/CHANGELOG.md | 83 +++++++++++ packages/opencode/package.json | 2 +- packages/plugin-atomic-chat/package.json | 2 +- packages/plugin/package.json | 2 +- packages/protocol/package.json | 2 +- packages/schema/package.json | 2 +- packages/script/package.json | 2 +- packages/sdk-next/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/server/package.json | 2 +- packages/session-ui/package.json | 2 +- packages/storybook/package.json | 2 +- packages/tui/package.json | 2 +- packages/ui/package.json | 2 +- script/upstream/package.json | 2 +- 116 files changed, 318 insertions(+), 493 deletions(-) delete mode 100644 .changeset/agent-manager-sidebar-density.md delete mode 100644 .changeset/agent-manager-terminal-focus.md delete mode 100644 .changeset/align-agent-manager-project-actions.md delete mode 100644 .changeset/auto-model-route-choices.md delete mode 100644 .changeset/bright-model-search.md delete mode 100644 .changeset/bright-project-picker.md delete mode 100644 .changeset/broad-custom-provider-efforts.md delete mode 100644 .changeset/cache-worktree-dialog-selections.md delete mode 100644 .changeset/calm-agent-manager-git-polling.md delete mode 100644 .changeset/calm-location-watchers.md delete mode 100644 .changeset/calm-skill-examples.md delete mode 100644 .changeset/command-model-selector-sync.md delete mode 100644 .changeset/commands-settings-jetbrains.md delete mode 100644 .changeset/compressed-speech-to-text-audio.md delete mode 100644 .changeset/custom-provider-edit-screen-improvements.md delete mode 100644 .changeset/disable-suggest-in-non-interactive-runs.md delete mode 100644 .changeset/exclude-chatgpt-from-prompt-cache-breakpoint.md delete mode 100644 .changeset/faster-agent-manager-worktrees.md delete mode 100644 .changeset/fix-agent-manager-terminal-toggle.md delete mode 100644 .changeset/fix-gh-windows-console.md delete mode 100644 .changeset/fix-interactive-terminal-input.md delete mode 100644 .changeset/fix-mcp-env-header-expansion.md delete mode 100644 .changeset/fix-memory-model-timeout-warnings.md delete mode 100644 .changeset/fix-multi-project-agent-manager-tool.md delete mode 100644 .changeset/fix-multi-project-navigation.md delete mode 100644 .changeset/fix-multi-project-progress.md delete mode 100644 .changeset/fix-multi-project-session-scope.md delete mode 100644 .changeset/fix-secondary-sidebar-nav-bar.md delete mode 100644 .changeset/fix-session-scroll-flicker.md delete mode 100644 .changeset/fix-tool-approval-source-display.md delete mode 100644 .changeset/forked-subagent-resume.md delete mode 100644 .changeset/friendly-llamas-manage.md delete mode 100644 .changeset/green-greps-settle.md delete mode 100644 .changeset/ignore-negative-model-prices.md delete mode 100644 .changeset/instant-agent-manager-terminal.md delete mode 100644 .changeset/jetbrains-auto-editor-context.md delete mode 100644 .changeset/jetbrains-cli-checksums.md delete mode 100644 .changeset/jetbrains-cli-mode-visibility.md delete mode 100644 .changeset/jetbrains-file-drop-references.md delete mode 100644 .changeset/jetbrains-prompt-attachments.md delete mode 100644 .changeset/jetbrains-revert-diff-card.md delete mode 100644 .changeset/jetbrains-slash-completion.md delete mode 100644 .changeset/mermaid-copy-clipboard-images.md delete mode 100644 .changeset/narrow-agent-manager-terminal.md delete mode 100644 .changeset/narrow-sidebar-recent.md delete mode 100644 .changeset/openai-explicit-prompt-cache-breakpoints.md delete mode 100644 .changeset/opencode-v1-17-9-to-v1-17-13.md delete mode 100644 .changeset/optimize-review-slash-commands.md delete mode 100644 .changeset/persist-project-accordion.md delete mode 100644 .changeset/preserve-model-variants.md delete mode 100644 .changeset/privacy-mode-tui.md delete mode 100644 .changeset/project-local-navigation-hints.md delete mode 100644 .changeset/project-row-isolation.md delete mode 100644 .changeset/prompt-rail-panel-edge.md delete mode 100644 .changeset/quick-git-launches.md delete mode 100644 .changeset/quiet-embedded-logo.md delete mode 100644 .changeset/quiet-jetbrains-watchers.md delete mode 100644 .changeset/quiet-sqlite-lock-errors.md delete mode 100644 .changeset/quiet-tui-config-reloads.md delete mode 100644 .changeset/quiet-tui-disposal-aborts.md delete mode 100644 .changeset/quiet-worktrees-list.md delete mode 100644 .changeset/remote-instance-catalog.md delete mode 100644 .changeset/remove-project-selection-indicator.md delete mode 100644 .changeset/remove-web-command.md delete mode 100644 .changeset/safe-credential-reconciliation.md delete mode 100644 .changeset/sandbox-live-settings.md delete mode 100644 .changeset/session-resume-import.md delete mode 100644 .changeset/show-outside-workspace-approval-reason.md delete mode 100644 .changeset/show-vscode-session-errors.md delete mode 100644 .changeset/single-agent-manager-empty-state.md delete mode 100644 .changeset/surface-backend-exit-diagnostics.md delete mode 100644 .changeset/sync-inspector-width.md delete mode 100644 .changeset/terminal-shortcut-translations.md delete mode 100644 .changeset/tidy-autocomplete-labels.md delete mode 100644 .changeset/vscode-skills-tooltip-wrap.md delete mode 100644 .changeset/worktree-rename-focus.md delete mode 100644 .changeset/worktree-slash-commands.md diff --git a/.changeset/agent-manager-sidebar-density.md b/.changeset/agent-manager-sidebar-density.md deleted file mode 100644 index 08eb1cf9c1..0000000000 --- a/.changeset/agent-manager-sidebar-density.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Fix the Agent Manager sidebar keyboard shortcut badge so it appears on the right edge of local rows and only while hovered or holding the jump modifier, give worktree titles more room by no longer reserving space for hidden row actions, align project names and section headings with the row icons below them, and show which worktree a session belongs to in the search palette. diff --git a/.changeset/agent-manager-terminal-focus.md b/.changeset/agent-manager-terminal-focus.md deleted file mode 100644 index d88b418a29..0000000000 --- a/.changeset/agent-manager-terminal-focus.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": minor ---- - -Improve Agent Manager terminal focus and keyboard navigation. `Cmd+/` now focuses a visible embedded terminal before hiding it, `Cmd+Shift+T` creates a side terminal only while that terminal area has focus, and `Cmd+Shift+[` / `]` switch terminal tabs. `Cmd+Shift+M` focuses the Agent Manager prompt instead of opening VS Code Problems. `Cmd+W` hides the last side terminal instead of stopping its shell. diff --git a/.changeset/align-agent-manager-project-actions.md b/.changeset/align-agent-manager-project-actions.md deleted file mode 100644 index b2d36e56ce..0000000000 --- a/.changeset/align-agent-manager-project-actions.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Align multi-project Agent Manager header actions with the worktree controls. diff --git a/.changeset/auto-model-route-choices.md b/.changeset/auto-model-route-choices.md deleted file mode 100644 index 6edfcabe14..0000000000 --- a/.changeset/auto-model-route-choices.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Show underlying models for every Auto model in the model selector when the catalog includes them. diff --git a/.changeset/bright-model-search.md b/.changeset/bright-model-search.md deleted file mode 100644 index 6bdf91a54d..0000000000 --- a/.changeset/bright-model-search.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": minor ---- - -Improve model search relevance with provider-aware results and personalized usage suggestions. diff --git a/.changeset/bright-project-picker.md b/.changeset/bright-project-picker.md deleted file mode 100644 index 18458aaa85..0000000000 --- a/.changeset/bright-project-picker.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": minor ---- - -Let Agent Manager users choose the repository when creating or importing a worktree in multi-project mode. diff --git a/.changeset/broad-custom-provider-efforts.md b/.changeset/broad-custom-provider-efforts.md deleted file mode 100644 index c074695617..0000000000 --- a/.changeset/broad-custom-provider-efforts.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@kilocode/cli": patch -"kilo-code": patch ---- - -Automatically expose broad reasoning effort options for custom provider models and link saved providers to advanced JSON configuration. diff --git a/.changeset/cache-worktree-dialog-selections.md b/.changeset/cache-worktree-dialog-selections.md deleted file mode 100644 index f13128f7b9..0000000000 --- a/.changeset/cache-worktree-dialog-selections.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Remember Agent Manager worktree dialog model, variant, mode, and sandbox selections when reopened. diff --git a/.changeset/calm-agent-manager-git-polling.md b/.changeset/calm-agent-manager-git-polling.md deleted file mode 100644 index 5f3314267a..0000000000 --- a/.changeset/calm-agent-manager-git-polling.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Reduce Agent Manager background Git polling load across large worktree sets. diff --git a/.changeset/calm-location-watchers.md b/.changeset/calm-location-watchers.md deleted file mode 100644 index cb9c384b6e..0000000000 --- a/.changeset/calm-location-watchers.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@kilocode/cli": patch -"kilo-code": patch ---- - -Prevent VS Code sessions and Agent Manager worktrees from starting unused file watchers and defer file indexing until search is used. diff --git a/.changeset/calm-skill-examples.md b/.changeset/calm-skill-examples.md deleted file mode 100644 index a1ac64ae30..0000000000 --- a/.changeset/calm-skill-examples.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@kilocode/cli": patch ---- - -Prevent built-in skill documentation examples from triggering shell permission prompts. diff --git a/.changeset/command-model-selector-sync.md b/.changeset/command-model-selector-sync.md deleted file mode 100644 index cd64ab6f90..0000000000 --- a/.changeset/command-model-selector-sync.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Update the active model, mode, and thinking selectors when executing a slash command with configured overrides. diff --git a/.changeset/commands-settings-jetbrains.md b/.changeset/commands-settings-jetbrains.md deleted file mode 100644 index 0c451ea9f3..0000000000 --- a/.changeset/commands-settings-jetbrains.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@kilocode/cli": minor ---- - -Add kilocode command-file endpoints so clients can list editable command/workflow files, inspect model and reasoning variant metadata, and remove them. diff --git a/.changeset/compressed-speech-to-text-audio.md b/.changeset/compressed-speech-to-text-audio.md deleted file mode 100644 index fee2a9166d..0000000000 --- a/.changeset/compressed-speech-to-text-audio.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Compress speech-to-text audio input to AAC format across macOS, Linux, and Windows to prevent payload size errors on long recordings. diff --git a/.changeset/custom-provider-edit-screen-improvements.md b/.changeset/custom-provider-edit-screen-improvements.md deleted file mode 100644 index b4b383ff25..0000000000 --- a/.changeset/custom-provider-edit-screen-improvements.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"kilo-code": patch -"@kilocode/kilo-ui": patch ---- - -Improve custom provider edit dialog layout, make advanced configuration action prominent, and add bulk toggle buttons for reasoning and image modalities across all models. diff --git a/.changeset/disable-suggest-in-non-interactive-runs.md b/.changeset/disable-suggest-in-non-interactive-runs.md deleted file mode 100644 index d75f2b540e..0000000000 --- a/.changeset/disable-suggest-in-non-interactive-runs.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@kilocode/cli": patch ---- - -Disable the suggest tool and auto-dismiss pending suggestions in non-interactive CLI runs to prevent hanging on benchmarks and automated pipelines. diff --git a/.changeset/exclude-chatgpt-from-prompt-cache-breakpoint.md b/.changeset/exclude-chatgpt-from-prompt-cache-breakpoint.md deleted file mode 100644 index 6362bdd7ff..0000000000 --- a/.changeset/exclude-chatgpt-from-prompt-cache-breakpoint.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@kilocode/cli": patch ---- - -Exclude ChatGPT subscriptions from explicit prompt cache breakpoints. diff --git a/.changeset/faster-agent-manager-worktrees.md b/.changeset/faster-agent-manager-worktrees.md deleted file mode 100644 index 259a5413f7..0000000000 --- a/.changeset/faster-agent-manager-worktrees.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Start Agent Manager worktree sessions faster by prefetching base branches, reducing workspace file-watcher load, and overlapping independent multi-session setup. diff --git a/.changeset/fix-agent-manager-terminal-toggle.md b/.changeset/fix-agent-manager-terminal-toggle.md deleted file mode 100644 index a93825d595..0000000000 --- a/.changeset/fix-agent-manager-terminal-toggle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Fix the Agent Manager terminal toolbar button to toggle panel visibility directly. diff --git a/.changeset/fix-gh-windows-console.md b/.changeset/fix-gh-windows-console.md deleted file mode 100644 index f9fc3a1e5c..0000000000 --- a/.changeset/fix-gh-windows-console.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Prevent extension-managed GitHub CLI commands from opening transient Windows Terminal windows. diff --git a/.changeset/fix-interactive-terminal-input.md b/.changeset/fix-interactive-terminal-input.md deleted file mode 100644 index b9d5391548..0000000000 --- a/.changeset/fix-interactive-terminal-input.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@kilocode/cli": patch ---- - -Restore keyboard input for interactive terminal prompts when the CLI session uses a workspace. diff --git a/.changeset/fix-mcp-env-header-expansion.md b/.changeset/fix-mcp-env-header-expansion.md deleted file mode 100644 index 051924f1df..0000000000 --- a/.changeset/fix-mcp-env-header-expansion.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@kilocode/cli": patch ---- - -Prevent project MCP configs from resolving variable-backed headers or inheriting trusted headers when changing endpoints, while preserving unaffected servers. diff --git a/.changeset/fix-memory-model-timeout-warnings.md b/.changeset/fix-memory-model-timeout-warnings.md deleted file mode 100644 index 5a0c870016..0000000000 --- a/.changeset/fix-memory-model-timeout-warnings.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@kilocode/cli": patch -"@kilocode/kilo-memory": patch -"kilo-code": patch ---- - -Reduce noisy memory timeout warnings and retry transient background consolidation failures once. diff --git a/.changeset/fix-multi-project-agent-manager-tool.md b/.changeset/fix-multi-project-agent-manager-tool.md deleted file mode 100644 index bb4e6304f6..0000000000 --- a/.changeset/fix-multi-project-agent-manager-tool.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"kilo-code": patch -"@kilocode/cli": patch ---- - -Route Agent Manager tool-launched sessions to the project that owns the tool event directory, keep sandboxed worktree sessions inside their active worktree, and wait for busy managed sessions before prompting them. diff --git a/.changeset/fix-multi-project-navigation.md b/.changeset/fix-multi-project-navigation.md deleted file mode 100644 index 4eddfc5eb1..0000000000 --- a/.changeset/fix-multi-project-navigation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Fix multi-project Agent Manager keyboard navigation and Cmd/Ctrl shortcut selection when worktrees are grouped in sections. diff --git a/.changeset/fix-multi-project-progress.md b/.changeset/fix-multi-project-progress.md deleted file mode 100644 index 53ccaf5730..0000000000 --- a/.changeset/fix-multi-project-progress.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Fix Agent Manager progress indicators when multiple projects are expanded. diff --git a/.changeset/fix-multi-project-session-scope.md b/.changeset/fix-multi-project-session-scope.md deleted file mode 100644 index 0f32e3327a..0000000000 --- a/.changeset/fix-multi-project-session-scope.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Speed up local VS Code snapshot installs and scope Agent Manager session events and Git status to the active project, including edits inside nested repositories. diff --git a/.changeset/fix-secondary-sidebar-nav-bar.md b/.changeset/fix-secondary-sidebar-nav-bar.md deleted file mode 100644 index 7905945a1e..0000000000 --- a/.changeset/fix-secondary-sidebar-nav-bar.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Fix the sidebar navigation bar (New Task, History, Agent Manager, KiloClaw, Marketplace, Profile, Settings) disappearing in Cursor when the Kilo Code view is docked in the Secondary Side Bar. Cursor now renders the navigation inside the webview itself so it stays visible regardless of dock location. VS Code is unaffected — it continues to use its native title bar toolbar, which already worked correctly everywhere. diff --git a/.changeset/fix-session-scroll-flicker.md b/.changeset/fix-session-scroll-flicker.md deleted file mode 100644 index 7fe8232c1e..0000000000 --- a/.changeset/fix-session-scroll-flicker.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@kilocode/kilo-ui": patch -"kilo-code": patch ---- - -Fix flickering and sticky scrolling when scrolling up in Agent Manager and chat sessions. diff --git a/.changeset/fix-tool-approval-source-display.md b/.changeset/fix-tool-approval-source-display.md deleted file mode 100644 index 3308ed0827..0000000000 --- a/.changeset/fix-tool-approval-source-display.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": minor ---- - -Move the "why was this tool call approved" line to after the tool output instead of between the header and body, add an icon to it, and add a Display setting to hide it. diff --git a/.changeset/forked-subagent-resume.md b/.changeset/forked-subagent-resume.md deleted file mode 100644 index 2a4f8cd483..0000000000 --- a/.changeset/forked-subagent-resume.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@kilocode/cli": patch ---- - -Allow subagent tasks to be resumed after their parent session is forked. diff --git a/.changeset/friendly-llamas-manage.md b/.changeset/friendly-llamas-manage.md deleted file mode 100644 index 494e5cc065..0000000000 --- a/.changeset/friendly-llamas-manage.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@kilocode/cli": patch ---- - -Support the Agent Manager tool with llama.cpp servers that reject prefix-only JSON Schema patterns. diff --git a/.changeset/green-greps-settle.md b/.changeset/green-greps-settle.md deleted file mode 100644 index 9f7cacabf7..0000000000 --- a/.changeset/green-greps-settle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@kilocode/cli": patch ---- - -Add bounded, context-aware grep controls without leaving agents waiting on completed searches. diff --git a/.changeset/ignore-negative-model-prices.md b/.changeset/ignore-negative-model-prices.md deleted file mode 100644 index 66bcf01a6d..0000000000 --- a/.changeset/ignore-negative-model-prices.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"@kilocode/kilo-gateway": patch -"@kilocode/cli": patch -"kilo-code": patch -"@kilocode/kilo-jetbrains": patch ---- - -Ignore negative pricing entries from model catalogs and handle unpriced models gracefully in UI price formatting. diff --git a/.changeset/instant-agent-manager-terminal.md b/.changeset/instant-agent-manager-terminal.md deleted file mode 100644 index 4893a22b6d..0000000000 --- a/.changeset/instant-agent-manager-terminal.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Accept terminal input immediately while the Agent Manager shell starts. diff --git a/.changeset/jetbrains-auto-editor-context.md b/.changeset/jetbrains-auto-editor-context.md deleted file mode 100644 index 3a6b91d758..0000000000 --- a/.changeset/jetbrains-auto-editor-context.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@kilocode/kilo-jetbrains": minor ---- - -Include the active editor file, open files, visible files, and selected text in JetBrains chat context by default, with a Context settings toggle to disable it. Files matched by `.kilocodeignore` (or `.gitignore` plus `.env` files) are excluded, and the default shell is reported to the agent. diff --git a/.changeset/jetbrains-cli-checksums.md b/.changeset/jetbrains-cli-checksums.md deleted file mode 100644 index 186ae55675..0000000000 --- a/.changeset/jetbrains-cli-checksums.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@kilocode/kilo-jetbrains": patch ---- - -Avoid GitHub API rate-limit failures when the JetBrains plugin downloads the pinned Kilo CLI. diff --git a/.changeset/jetbrains-cli-mode-visibility.md b/.changeset/jetbrains-cli-mode-visibility.md deleted file mode 100644 index bc9cf774d1..0000000000 --- a/.changeset/jetbrains-cli-mode-visibility.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@kilocode/kilo-jetbrains": minor ---- - -Log whether the JetBrains plugin downloads Core or uses the bundled/cached version, and mark the Core version shown in the popup as "Bundled" when it wasn't downloaded. diff --git a/.changeset/jetbrains-file-drop-references.md b/.changeset/jetbrains-file-drop-references.md deleted file mode 100644 index e801c8ba49..0000000000 --- a/.changeset/jetbrains-file-drop-references.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@kilocode/kilo-jetbrains": patch ---- - -Fix dropping files into the JetBrains prompt so code files are added as readable file references and drops anywhere in the session panel feed the prompt attachments. diff --git a/.changeset/jetbrains-prompt-attachments.md b/.changeset/jetbrains-prompt-attachments.md deleted file mode 100644 index ac1f1f117e..0000000000 --- a/.changeset/jetbrains-prompt-attachments.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@kilocode/kilo-jetbrains": patch ---- - -Render prompt attachments inside the sent message bubble with file chips, image previews, and selection-aware file opening. diff --git a/.changeset/jetbrains-revert-diff-card.md b/.changeset/jetbrains-revert-diff-card.md deleted file mode 100644 index fe8f75e3d3..0000000000 --- a/.changeset/jetbrains-revert-diff-card.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@kilocode/kilo-jetbrains": patch ---- - -Improve JetBrains session transcript layout, icons, reverted-change summaries, and multi-hunk diff rendering. diff --git a/.changeset/jetbrains-slash-completion.md b/.changeset/jetbrains-slash-completion.md deleted file mode 100644 index e2ec3c4996..0000000000 --- a/.changeset/jetbrains-slash-completion.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@kilocode/kilo-jetbrains": patch ---- - -Improve slash command completion to match separators, camel-case humps, and contained command names. diff --git a/.changeset/mermaid-copy-clipboard-images.md b/.changeset/mermaid-copy-clipboard-images.md deleted file mode 100644 index c9c0a405f9..0000000000 --- a/.changeset/mermaid-copy-clipboard-images.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Fix Mermaid Copy PNG/SVG in VS Code webviews so they put images on the clipboard instead of failing or copying SVG markup. diff --git a/.changeset/narrow-agent-manager-terminal.md b/.changeset/narrow-agent-manager-terminal.md deleted file mode 100644 index 5091714bdb..0000000000 --- a/.changeset/narrow-agent-manager-terminal.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Keep Agent Manager terminals aligned and correctly wrapped when the terminal panel is narrow. diff --git a/.changeset/narrow-sidebar-recent.md b/.changeset/narrow-sidebar-recent.md deleted file mode 100644 index fcd56b0f73..0000000000 --- a/.changeset/narrow-sidebar-recent.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Keep recent sessions and the Show History action inset and usable in narrow VS Code sidebars. diff --git a/.changeset/openai-explicit-prompt-cache-breakpoints.md b/.changeset/openai-explicit-prompt-cache-breakpoints.md deleted file mode 100644 index 4ba3d97b45..0000000000 --- a/.changeset/openai-explicit-prompt-cache-breakpoints.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@kilocode/cli": patch ---- - -Set explicit prompt cache breakpoints on stable prefixes for OpenAI GPT-5.6+ models. diff --git a/.changeset/opencode-v1-17-9-to-v1-17-13.md b/.changeset/opencode-v1-17-9-to-v1-17-13.md deleted file mode 100644 index 529fce727e..0000000000 --- a/.changeset/opencode-v1-17-9-to-v1-17-13.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -"@kilocode/cli": patch -"kilo-code": patch ---- - -Changes from opencode v1.17.9 to v1.17.13 upstream: - -- Core Improvements: MCP servers can append their instructions to the model context, and MCP resources are available as tools with template listing. -- Core Improvements: Model variants are generated from models.dev data, including modes exposed as models. -- Core Improvements: Tool definitions pass `strict` through for Codex parity, and Gemini requests support video and audio media. -- Core Bugfixes: Interrupted assistant steps settle instead of leaving sessions stuck busy. -- Core Bugfixes: MCP OAuth reconnects after authorization even when the server is disabled, refreshes credentials on reauthentication, requests refresh token scope, surfaces completion errors, and binds its callback to the IPv4 loopback. -- Core Bugfixes: MCP tool results prefer content over structured output, and denied resource template tools stay hidden. -- Core Bugfixes: Stale GitHub Copilot Responses item IDs are no longer replayed, and OpenAI reasoning variants are forced where required. -- Core Bugfixes: Adaptive thinking is enabled for Claude Sonnet 5, and expired promos were removed from the zen catalog. -- Core Bugfixes: Preserve released prompt history during database replay and keep native event streams connected for all supported Kilo events. -- Core Bugfixes: Remote skill manifests support optional per-skill versions; changing a version refreshes the cached skill atomically, and skill base directories are emitted as filesystem paths. -- CLI Improvements: Ports increment from the default when busy. -- CLI Improvements: Use `--auto` to start the TUI in a run-scoped auto-approve mode, and leave the mode mid-session from the command palette. -- TUI Improvements: Redesigned crash screen, model picker sorted by release date, bindable diff viewer and Move Session commands, main-branch diff source, and inline skill load errors. -- TUI Bugfixes: File autocomplete is scoped to the session, multi-day durations format correctly, and root sessions load in the session switcher. diff --git a/.changeset/optimize-review-slash-commands.md b/.changeset/optimize-review-slash-commands.md deleted file mode 100644 index 8e104765c2..0000000000 --- a/.changeset/optimize-review-slash-commands.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"kilo-code": minor -"@kilocode/cli": minor ---- - -Add nested slash command suggestions for `/review` in VS Code and support `staged`, `unpushed`, and `quick` review modes. diff --git a/.changeset/persist-project-accordion.md b/.changeset/persist-project-accordion.md deleted file mode 100644 index 6fda8a714a..0000000000 --- a/.changeset/persist-project-accordion.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Allow Agent Manager project headers to collapse or expand their project body, and persist that state across panel opens and VS Code restarts. diff --git a/.changeset/preserve-model-variants.md b/.changeset/preserve-model-variants.md deleted file mode 100644 index 10941827bb..0000000000 --- a/.changeset/preserve-model-variants.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Preserve the selected reasoning effort when switching to a model that supports the same or nearest available variant. diff --git a/.changeset/privacy-mode-tui.md b/.changeset/privacy-mode-tui.md deleted file mode 100644 index 9131915a7e..0000000000 --- a/.changeset/privacy-mode-tui.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@kilocode/cli": patch ---- - -Add a privacy mode that blurs PII in the TUI (personal balance, Kilo Pass usage, etc.) and requires confirmation before `/profile` reveals email, name, balance, and team. Toggle with the new `/privacy` command or by setting `privacy_mode` in `kilo.json`. The `kilo profile` CLI command is unaffected. \ No newline at end of file diff --git a/.changeset/project-local-navigation-hints.md b/.changeset/project-local-navigation-hints.md deleted file mode 100644 index f6acca80ec..0000000000 --- a/.changeset/project-local-navigation-hints.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Show previous and next navigation hints using each project's own Agent Manager sidebar order. diff --git a/.changeset/project-row-isolation.md b/.changeset/project-row-isolation.md deleted file mode 100644 index fe1129c2cc..0000000000 --- a/.changeset/project-row-isolation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Keep Agent Manager worktree rows isolated when projects contain identical raw worktree IDs. diff --git a/.changeset/prompt-rail-panel-edge.md b/.changeset/prompt-rail-panel-edge.md deleted file mode 100644 index e6de692cc3..0000000000 --- a/.changeset/prompt-rail-panel-edge.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Move the prompt navigator rail to the panel edge so it is harder to open by accident, and keep it clear of the pane splitter while resizing. diff --git a/.changeset/quick-git-launches.md b/.changeset/quick-git-launches.md deleted file mode 100644 index 6c29101ce1..0000000000 --- a/.changeset/quick-git-launches.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Reduce Agent Manager Git polling overhead by reusing the validated Git executable and bypassing the macOS developer-tool launcher when safe. diff --git a/.changeset/quiet-embedded-logo.md b/.changeset/quiet-embedded-logo.md deleted file mode 100644 index 7623cf0e73..0000000000 --- a/.changeset/quiet-embedded-logo.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Use a compatible Kilo wordmark in embedded Agent Manager terminals. diff --git a/.changeset/quiet-jetbrains-watchers.md b/.changeset/quiet-jetbrains-watchers.md deleted file mode 100644 index 3e07e6c698..0000000000 --- a/.changeset/quiet-jetbrains-watchers.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@kilocode/cli": patch ---- - -Fix high CPU and runaway memory growth in the JetBrains background `kilo serve` process on macOS by no longer eagerly starting native file watchers, matching the VS Code backend. diff --git a/.changeset/quiet-sqlite-lock-errors.md b/.changeset/quiet-sqlite-lock-errors.md deleted file mode 100644 index db7cb238f9..0000000000 --- a/.changeset/quiet-sqlite-lock-errors.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@kilocode/cli": patch ---- - -Show a concise retryable message when concurrent Kilo processes temporarily lock the SQLite database instead of printing the full server error trace. diff --git a/.changeset/quiet-tui-config-reloads.md b/.changeset/quiet-tui-config-reloads.md deleted file mode 100644 index 8c21c818bb..0000000000 --- a/.changeset/quiet-tui-config-reloads.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@kilocode/cli": patch ---- - -Prevent TUI config reload logs from corrupting the interactive terminal. diff --git a/.changeset/quiet-tui-disposal-aborts.md b/.changeset/quiet-tui-disposal-aborts.md deleted file mode 100644 index 7431897d31..0000000000 --- a/.changeset/quiet-tui-disposal-aborts.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@kilocode/cli": patch ---- - -Avoid printing an error when closing the TUI cancels in-flight startup refreshes. diff --git a/.changeset/quiet-worktrees-list.md b/.changeset/quiet-worktrees-list.md deleted file mode 100644 index 71bd8445ec..0000000000 --- a/.changeset/quiet-worktrees-list.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Prevent Agent Manager overview requests from timing out while refreshing Git statistics across many worktrees. diff --git a/.changeset/remote-instance-catalog.md b/.changeset/remote-instance-catalog.md deleted file mode 100644 index bed43e8cd3..0000000000 --- a/.changeset/remote-instance-catalog.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": minor ---- - -Support browsing an instance's model catalog before a session starts. diff --git a/.changeset/remove-project-selection-indicator.md b/.changeset/remove-project-selection-indicator.md deleted file mode 100644 index 50aee2c720..0000000000 --- a/.changeset/remove-project-selection-indicator.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Remove the redundant selected-project indicator from Agent Manager. diff --git a/.changeset/remove-web-command.md b/.changeset/remove-web-command.md deleted file mode 100644 index 8dd97b02fe..0000000000 --- a/.changeset/remove-web-command.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@kilocode/cli": patch ---- - -Remove unsupported `kilo web` CLI command. diff --git a/.changeset/safe-credential-reconciliation.md b/.changeset/safe-credential-reconciliation.md deleted file mode 100644 index abb8dd5f36..0000000000 --- a/.changeset/safe-credential-reconciliation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@kilocode/cli": patch ---- - -Prevent concurrent Kilo startups from rewriting unchanged credentials, retry transient database locks, and redact bound values from database errors. diff --git a/.changeset/sandbox-live-settings.md b/.changeset/sandbox-live-settings.md deleted file mode 100644 index f356683b99..0000000000 --- a/.changeset/sandbox-live-settings.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"kilo-code": patch -"@kilocode/cli": patch ---- - -Apply saved sandbox settings to existing sessions and use the latest settings when enabling sandboxing diff --git a/.changeset/session-resume-import.md b/.changeset/session-resume-import.md deleted file mode 100644 index 5243f666e0..0000000000 --- a/.changeset/session-resume-import.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@kilocode/cli": minor ---- - -Import conversation history from Claude Code and OpenAI Codex sessions with the /resume-claude and /resume-codex slash commands. diff --git a/.changeset/show-outside-workspace-approval-reason.md b/.changeset/show-outside-workspace-approval-reason.md deleted file mode 100644 index 1475ae34cd..0000000000 --- a/.changeset/show-outside-workspace-approval-reason.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Show the permission approval reason for reads and writes outside the workspace, matching other tools diff --git a/.changeset/show-vscode-session-errors.md b/.changeset/show-vscode-session-errors.md deleted file mode 100644 index 5302a13a70..0000000000 --- a/.changeset/show-vscode-session-errors.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Keep model and provider errors visible in VS Code when chat history refreshes. diff --git a/.changeset/single-agent-manager-empty-state.md b/.changeset/single-agent-manager-empty-state.md deleted file mode 100644 index f3d9d0d35f..0000000000 --- a/.changeset/single-agent-manager-empty-state.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Show only one empty-state panel after removing the last Agent Manager worktree. diff --git a/.changeset/surface-backend-exit-diagnostics.md b/.changeset/surface-backend-exit-diagnostics.md deleted file mode 100644 index 146a00c742..0000000000 --- a/.changeset/surface-backend-exit-diagnostics.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Surface process exit signals and structured spawn failure details in server startup diagnostics diff --git a/.changeset/sync-inspector-width.md b/.changeset/sync-inspector-width.md deleted file mode 100644 index 9372f7ace4..0000000000 --- a/.changeset/sync-inspector-width.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Persist the Agent Manager inspector width, share it between the terminal and diff viewer, and keep resizing responsive. diff --git a/.changeset/terminal-shortcut-translations.md b/.changeset/terminal-shortcut-translations.md deleted file mode 100644 index c0b45c5c4c..0000000000 --- a/.changeset/terminal-shortcut-translations.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Translate the Agent Manager terminal focus shortcut label in all supported locales. diff --git a/.changeset/tidy-autocomplete-labels.md b/.changeset/tidy-autocomplete-labels.md deleted file mode 100644 index df7c9a8c34..0000000000 --- a/.changeset/tidy-autocomplete-labels.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@kilocode/cli": patch ---- - -Separate autocomplete item names from their descriptions in the TUI. diff --git a/.changeset/vscode-skills-tooltip-wrap.md b/.changeset/vscode-skills-tooltip-wrap.md deleted file mode 100644 index a4a5ecec03..0000000000 --- a/.changeset/vscode-skills-tooltip-wrap.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Long skill folder paths and URLs shown in the tooltip on the Skills settings page now wrap inside the viewport instead of overflowing on a single line. diff --git a/.changeset/worktree-rename-focus.md b/.changeset/worktree-rename-focus.md deleted file mode 100644 index fef1fe11e2..0000000000 --- a/.changeset/worktree-rename-focus.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Keep multi-project worktree rename inputs focused while selection updates are still settling. diff --git a/.changeset/worktree-slash-commands.md b/.changeset/worktree-slash-commands.md deleted file mode 100644 index bc008e5df7..0000000000 --- a/.changeset/worktree-slash-commands.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": minor ---- - -Support model, agent, variant, and sandbox slash commands in Agent Manager worktree prompts. diff --git a/bun.lock b/bun.lock index 50998be7c4..930feb8fd1 100644 --- a/bun.lock +++ b/bun.lock @@ -32,7 +32,7 @@ }, "packages/client": { "name": "@opencode-ai/client", - "version": "7.4.16", + "version": "7.4.21", "dependencies": { "@opencode-ai/protocol": "workspace:*", "@opencode-ai/schema": "workspace:*", @@ -56,7 +56,7 @@ }, "packages/core": { "name": "@opencode-ai/core", - "version": "7.4.20", + "version": "7.4.21", "bin": { "opencode": "./bin/opencode", }, @@ -154,7 +154,7 @@ }, "packages/effect-drizzle-sqlite": { "name": "@opencode-ai/effect-drizzle-sqlite", - "version": "7.4.20", + "version": "7.4.21", "dependencies": { "drizzle-orm": "catalog:", "effect": "catalog:", @@ -168,7 +168,7 @@ }, "packages/effect-sqlite-node": { "name": "@opencode-ai/effect-sqlite-node", - "version": "7.4.20", + "version": "7.4.21", "dependencies": { "effect": "catalog:", }, @@ -180,7 +180,7 @@ }, "packages/http-recorder": { "name": "@opencode-ai/http-recorder", - "version": "7.4.20", + "version": "7.4.21", "dependencies": { "@effect/platform-node": "4.0.0-beta.83", "@effect/platform-node-shared": "4.0.0-beta.83", @@ -201,7 +201,7 @@ }, "packages/httpapi-codegen": { "name": "@opencode-ai/httpapi-codegen", - "version": "7.4.16", + "version": "7.4.21", "dependencies": { "effect": "catalog:", "prettier": "3.6.2", @@ -214,7 +214,7 @@ }, "packages/kilo-console": { "name": "@kilocode/kilo-console", - "version": "7.4.20", + "version": "7.4.21", "dependencies": { "@kilocode/kilo-indexing": "workspace:*", "@kilocode/kilo-web-ui": "workspace:*", @@ -237,7 +237,7 @@ }, "packages/kilo-docs": { "name": "@kilocode/kilo-docs", - "version": "7.4.20", + "version": "7.4.21", "dependencies": { "@docsearch/css": "^4", "@docsearch/js": "^4", @@ -267,7 +267,7 @@ }, "packages/kilo-gateway": { "name": "@kilocode/kilo-gateway", - "version": "7.4.20", + "version": "7.4.21", "dependencies": { "@ai-sdk/alibaba": "1.0.17", "@ai-sdk/anthropic": "3.0.82", @@ -303,7 +303,7 @@ }, "packages/kilo-i18n": { "name": "@kilocode/kilo-i18n", - "version": "7.4.20", + "version": "7.4.21", "devDependencies": { "@tsconfig/node22": "catalog:", "@types/bun": "catalog:", @@ -313,7 +313,7 @@ }, "packages/kilo-indexing": { "name": "@kilocode/kilo-indexing", - "version": "7.4.20", + "version": "7.4.21", "dependencies": { "@aws-sdk/client-bedrock-runtime": "3.1005.0", "@aws-sdk/credential-provider-ini": "3.972.31", @@ -349,7 +349,7 @@ }, "packages/kilo-memory": { "name": "@kilocode/kilo-memory", - "version": "7.4.20", + "version": "7.4.21", "dependencies": { "effect": "catalog:", "zod": "catalog:", @@ -363,7 +363,7 @@ }, "packages/kilo-sandbox": { "name": "@kilocode/sandbox", - "version": "7.4.20", + "version": "7.4.21", "dependencies": { "@anthropic-ai/sandbox-runtime": "catalog:", "effect": "catalog:", @@ -378,7 +378,7 @@ }, "packages/kilo-telemetry": { "name": "@kilocode/kilo-telemetry", - "version": "7.4.20", + "version": "7.4.21", "dependencies": { "@kilocode/kilo-gateway": "workspace:*", "posthog-node": "4.4.0", @@ -392,7 +392,7 @@ }, "packages/kilo-ui": { "name": "@kilocode/kilo-ui", - "version": "7.4.20", + "version": "7.4.21", "dependencies": { "@kilocode/sdk": "workspace:*", "@kobalte/core": "0.13.11", @@ -430,7 +430,7 @@ }, "packages/kilo-vscode": { "name": "kilo-code", - "version": "7.4.20", + "version": "7.4.21", "dependencies": { "@anthropic-ai/sdk": "^0.39.0", "@kilocode/kilo-gateway": "workspace:*", @@ -503,7 +503,7 @@ }, "packages/kilo-web-ui": { "name": "@kilocode/kilo-web-ui", - "version": "7.4.20", + "version": "7.4.21", "dependencies": { "@kilocode/kilo-ui": "workspace:*", "@kobalte/core": "catalog:", @@ -520,7 +520,7 @@ }, "packages/llm": { "name": "@opencode-ai/llm", - "version": "7.4.20", + "version": "7.4.21", "dependencies": { "@opencode-ai/schema": "workspace:*", "@smithy/eventstream-codec": "4.2.14", @@ -539,7 +539,7 @@ }, "packages/opencode": { "name": "@kilocode/cli", - "version": "7.4.20", + "version": "7.4.21", "bin": { "kilo": "./bin/kilo", "kilocode": "./bin/kilo", @@ -708,7 +708,7 @@ }, "packages/plugin": { "name": "@kilocode/plugin", - "version": "7.4.20", + "version": "7.4.21", "dependencies": { "@ai-sdk/provider": "3.0.8", "@kilocode/sdk": "workspace:*", @@ -737,7 +737,7 @@ }, "packages/plugin-atomic-chat": { "name": "@kilocode/plugin-atomic-chat", - "version": "7.4.20", + "version": "7.4.21", "dependencies": { "@kilocode/plugin": "workspace:*", }, @@ -751,7 +751,7 @@ }, "packages/protocol": { "name": "@opencode-ai/protocol", - "version": "7.4.16", + "version": "7.4.21", "dependencies": { "@opencode-ai/schema": "workspace:*", "effect": "catalog:", @@ -764,7 +764,7 @@ }, "packages/schema": { "name": "@opencode-ai/schema", - "version": "7.4.16", + "version": "7.4.21", "dependencies": { "effect": "catalog:", }, @@ -776,7 +776,7 @@ }, "packages/script": { "name": "@opencode-ai/script", - "version": "7.4.20", + "version": "7.4.21", "dependencies": { "semver": "^7.6.3", }, @@ -787,7 +787,7 @@ }, "packages/sdk-next": { "name": "@opencode-ai/sdk-next", - "version": "7.4.16", + "version": "7.4.21", "dependencies": { "@opencode-ai/client": "workspace:*", "@opencode-ai/core": "workspace:*", @@ -802,7 +802,7 @@ }, "packages/sdk/js": { "name": "@kilocode/sdk", - "version": "7.4.20", + "version": "7.4.21", "dependencies": { "cross-spawn": "catalog:", }, @@ -817,7 +817,7 @@ }, "packages/server": { "name": "@opencode-ai/server", - "version": "7.4.20", + "version": "7.4.21", "dependencies": { "@opencode-ai/core": "workspace:*", "@opencode-ai/protocol": "workspace:*", @@ -832,7 +832,7 @@ }, "packages/session-ui": { "name": "@opencode-ai/session-ui", - "version": "7.4.16", + "version": "7.4.21", "dependencies": { "@kilocode/sdk": "workspace:*", "@kobalte/core": "catalog:", @@ -876,7 +876,7 @@ }, "packages/storybook": { "name": "@opencode-ai/storybook", - "version": "7.4.20", + "version": "7.4.21", "devDependencies": { "@opencode-ai/session-ui": "workspace:*", "@opencode-ai/ui": "workspace:*", @@ -900,7 +900,7 @@ }, "packages/tui": { "name": "@opencode-ai/tui", - "version": "7.4.20", + "version": "7.4.21", "dependencies": { "@kilocode/plugin": "workspace:*", "@kilocode/sdk": "workspace:*", @@ -927,7 +927,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "7.4.20", + "version": "7.4.21", "dependencies": { "@kilocode/sdk": "workspace:*", "@kobalte/core": "catalog:", @@ -987,23 +987,23 @@ }, }, "trustedDependencies": [ - "web-tree-sitter", "esbuild", - "tree-sitter-bash", "protobufjs", + "web-tree-sitter", + "tree-sitter-bash", ], "patchedDependencies": { - "virtua@0.49.1": "patches/virtua@0.49.1.patch", - "mammoth@1.12.0": "patches/mammoth@1.12.0.patch", - "@ff-labs/fff-bun@0.9.4": "patches/@ff-labs%2Ffff-bun@0.9.4.patch", - "@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch", + "@ai-sdk/xai@3.0.102": "patches/@ai-sdk%2Fxai@3.0.102.patch", "@modelcontextprotocol/sdk@1.29.0": "patches/@modelcontextprotocol%2Fsdk@1.29.0.patch", - "@ai-sdk/google@3.0.73": "patches/@ai-sdk%2Fgoogle@3.0.73.patch", + "@ff-labs/fff-bun@0.9.4": "patches/@ff-labs%2Ffff-bun@0.9.4.patch", "pacote@21.5.1": "patches/pacote@21.5.1.patch", - "@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch", + "@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch", "effect@4.0.0-beta.83": "patches/effect@4.0.0-beta.83.patch", "@npmcli/agent@4.0.2": "patches/@npmcli%2Fagent@4.0.2.patch", - "@ai-sdk/xai@3.0.102": "patches/@ai-sdk%2Fxai@3.0.102.patch", + "@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch", + "virtua@0.49.1": "patches/virtua@0.49.1.patch", + "@ai-sdk/google@3.0.73": "patches/@ai-sdk%2Fgoogle@3.0.73.patch", + "mammoth@1.12.0": "patches/mammoth@1.12.0.patch", }, "overrides": { "@effect/platform-node-shared": "4.0.0-beta.74", diff --git a/package.json b/package.json index ee3d5953d9..de307543ff 100644 --- a/package.json +++ b/package.json @@ -175,6 +175,6 @@ "pacote@21.5.1": "patches/pacote@21.5.1.patch", "mammoth@1.12.0": "patches/mammoth@1.12.0.patch" }, - "version": "7.4.20", + "version": "7.4.21", "peerDependencies": {} } diff --git a/packages/client/package.json b/packages/client/package.json index d3a4864d7f..67e14a097d 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -37,5 +37,5 @@ "@typescript/native-preview": "catalog:", "effect": "catalog:" }, - "version": "7.4.16" + "version": "7.4.21" } diff --git a/packages/core/package.json b/packages/core/package.json index 3e44b59e01..fb6f43057a 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "7.4.20", + "version": "7.4.21", "name": "@opencode-ai/core", "type": "module", "license": "MIT", diff --git a/packages/effect-drizzle-sqlite/package.json b/packages/effect-drizzle-sqlite/package.json index 907db1f9a3..cc31d9a215 100644 --- a/packages/effect-drizzle-sqlite/package.json +++ b/packages/effect-drizzle-sqlite/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "7.4.20", + "version": "7.4.21", "name": "@opencode-ai/effect-drizzle-sqlite", "type": "module", "license": "MIT", diff --git a/packages/effect-sqlite-node/package.json b/packages/effect-sqlite-node/package.json index 4af05664a0..24dcfc4a40 100644 --- a/packages/effect-sqlite-node/package.json +++ b/packages/effect-sqlite-node/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "7.4.20", + "version": "7.4.21", "name": "@opencode-ai/effect-sqlite-node", "type": "module", "license": "MIT", diff --git a/packages/extensions/zed/extension.toml b/packages/extensions/zed/extension.toml index 50fe224ed2..681146c5f9 100644 --- a/packages/extensions/zed/extension.toml +++ b/packages/extensions/zed/extension.toml @@ -1,7 +1,7 @@ id = "kilo" name = "Kilo" description = "The open source coding agent." -version = "7.4.20" +version = "7.4.21" schema_version = 1 authors = ["Anomaly"] repository = "https://github.com/Kilo-Org/kilocode" @@ -11,26 +11,26 @@ name = "Kilo" icon = "./icons/opencode.svg" [agent_servers.opencode.targets.darwin-aarch64] -archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.4.20/opencode-darwin-arm64.zip" +archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.4.21/opencode-darwin-arm64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.darwin-x86_64] -archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.4.20/opencode-darwin-x64.zip" +archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.4.21/opencode-darwin-x64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-aarch64] -archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.4.20/opencode-linux-arm64.tar.gz" +archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.4.21/opencode-linux-arm64.tar.gz" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-x86_64] -archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.4.20/opencode-linux-x64.tar.gz" +archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.4.21/opencode-linux-x64.tar.gz" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.windows-x86_64] -archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.4.20/opencode-windows-x64.zip" +archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.4.21/opencode-windows-x64.zip" cmd = "./opencode.exe" args = ["acp"] diff --git a/packages/http-recorder/package.json b/packages/http-recorder/package.json index 0b01f0809b..ae76fde11f 100644 --- a/packages/http-recorder/package.json +++ b/packages/http-recorder/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "7.4.20", + "version": "7.4.21", "name": "@opencode-ai/http-recorder", "description": "Record and replay Effect HTTP client traffic with deterministic cassettes", "type": "module", diff --git a/packages/httpapi-codegen/package.json b/packages/httpapi-codegen/package.json index aadefada25..bb64338cda 100644 --- a/packages/httpapi-codegen/package.json +++ b/packages/httpapi-codegen/package.json @@ -20,5 +20,5 @@ "@types/bun": "catalog:", "@typescript/native-preview": "catalog:" }, - "version": "7.4.16" + "version": "7.4.21" } diff --git a/packages/kilo-console/package.json b/packages/kilo-console/package.json index 2a024ace45..463b6c64d6 100755 --- a/packages/kilo-console/package.json +++ b/packages/kilo-console/package.json @@ -1,6 +1,6 @@ { "name": "@kilocode/kilo-console", - "version": "7.4.20", + "version": "7.4.21", "private": true, "type": "module", "scripts": { diff --git a/packages/kilo-docs/package.json b/packages/kilo-docs/package.json index f1c085a55f..6950c675c1 100644 --- a/packages/kilo-docs/package.json +++ b/packages/kilo-docs/package.json @@ -1,6 +1,6 @@ { "name": "@kilocode/kilo-docs", - "version": "7.4.20", + "version": "7.4.21", "private": true, "scripts": { "dev": "next dev --webpack --port 3002", diff --git a/packages/kilo-gateway/package.json b/packages/kilo-gateway/package.json index bb5e6796df..717fb18a2b 100644 --- a/packages/kilo-gateway/package.json +++ b/packages/kilo-gateway/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@kilocode/kilo-gateway", - "version": "7.4.20", + "version": "7.4.21", "type": "module", "license": "MIT", "description": "Unified Kilo Gateway package for OpenCode - authentication, provider, and API integration", diff --git a/packages/kilo-i18n/package.json b/packages/kilo-i18n/package.json index ec845e49b5..05796d3b6c 100644 --- a/packages/kilo-i18n/package.json +++ b/packages/kilo-i18n/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@kilocode/kilo-i18n", - "version": "7.4.20", + "version": "7.4.21", "type": "module", "license": "MIT", "description": "Kilo-specific i18n translations and overrides", diff --git a/packages/kilo-indexing/package.json b/packages/kilo-indexing/package.json index aab1178e92..eaaca71640 100644 --- a/packages/kilo-indexing/package.json +++ b/packages/kilo-indexing/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@kilocode/kilo-indexing", - "version": "7.4.20", + "version": "7.4.21", "type": "module", "license": "MIT", "description": "Standalone indexing engine and host helpers for Kilo Code", diff --git a/packages/kilo-jetbrains/CHANGELOG.md b/packages/kilo-jetbrains/CHANGELOG.md index 8fba3e7e42..fb12494613 100644 --- a/packages/kilo-jetbrains/CHANGELOG.md +++ b/packages/kilo-jetbrains/CHANGELOG.md @@ -1,5 +1,27 @@ # Changelog +## 7.5.0 + +### Minor Changes + +- [#13015](https://github.com/Kilo-Org/kilocode/pull/13015) [`62923ad`](https://github.com/Kilo-Org/kilocode/commit/62923adb518371d1659ea65e5519768e4abf231b) - Include the active editor file, open files, visible files, and selected text in JetBrains chat context by default, with a Context settings toggle to disable it. Files matched by `.kilocodeignore` (or `.gitignore` plus `.env` files) are excluded, and the default shell is reported to the agent. + +- [#12895](https://github.com/Kilo-Org/kilocode/pull/12895) [`a340d61`](https://github.com/Kilo-Org/kilocode/commit/a340d61716b6fdec89943bff438c151b513fd1f3) - Log whether the JetBrains plugin downloads Core or uses the bundled/cached version, and mark the Core version shown in the popup as "Bundled" when it wasn't downloaded. + +### Patch Changes + +- [#13040](https://github.com/Kilo-Org/kilocode/pull/13040) [`48c4a4a`](https://github.com/Kilo-Org/kilocode/commit/48c4a4af227572011bf44c172ab0ae86e0c2a429) - Ignore negative pricing entries from model catalogs and handle unpriced models gracefully in UI price formatting. + +- [#12861](https://github.com/Kilo-Org/kilocode/pull/12861) [`a957cc3`](https://github.com/Kilo-Org/kilocode/commit/a957cc38031823ae923d5bf7cc406543e19124c6) - Avoid GitHub API rate-limit failures when the JetBrains plugin downloads the pinned Kilo CLI. + +- [#12869](https://github.com/Kilo-Org/kilocode/pull/12869) [`cee2e36`](https://github.com/Kilo-Org/kilocode/commit/cee2e369f80ac5e8baa949ab7c789dcec831d886) - Fix dropping files into the JetBrains prompt so code files are added as readable file references and drops anywhere in the session panel feed the prompt attachments. + +- [#13015](https://github.com/Kilo-Org/kilocode/pull/13015) [`74470aa`](https://github.com/Kilo-Org/kilocode/commit/74470aa8611cdb48e3dc6c2e0deaa027b9af46f9) - Render prompt attachments inside the sent message bubble with file chips, image previews, and selection-aware file opening. + +- [#12862](https://github.com/Kilo-Org/kilocode/pull/12862) [`c47cfec`](https://github.com/Kilo-Org/kilocode/commit/c47cfeceebcd6b2ae5c0d416bde00f7e57449df8) - Improve JetBrains session transcript layout, icons, reverted-change summaries, and multi-hunk diff rendering. + +- [#12909](https://github.com/Kilo-Org/kilocode/pull/12909) [`5e60473`](https://github.com/Kilo-Org/kilocode/commit/5e60473e768325ce4109ef1c07106e392b49427f) - Improve slash command completion to match separators, camel-case humps, and contained command names. + ## 7.4.18 ### Patch Changes @@ -131,11 +153,13 @@ ## [7.0.15] - 2026-08-10 ### Added + - Include editor context in JetBrains prompts, including the active file, open and visible files, selected text, and shell context when available. - Show selected text and attached files as prompt attachments in user messages, with clickable links back to source files and selections. - Add a JetBrains Context setting to enable or disable automatic editor context. ### Fixed + - Avoid JetBrains prompt editor crashes during undo/redo bulk updates. - Keep completed question and tool views in the correct JetBrains transcript position. - Keep JetBrains chat pinned to the bottom when a turn finishes after modified-file updates. @@ -143,6 +167,7 @@ ## [7.0.14] - 2026-08-06 ### Fixed + - Improve slash command matching in the JetBrains plugin so typed commands resolve more reliably. - Avoid startup crashes when the Kilo CLI database is temporarily locked by another process. diff --git a/packages/kilo-memory/package.json b/packages/kilo-memory/package.json index 24b2e775f6..8406d64879 100644 --- a/packages/kilo-memory/package.json +++ b/packages/kilo-memory/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@kilocode/kilo-memory", - "version": "7.4.20", + "version": "7.4.21", "type": "module", "license": "MIT", "description": "Project memory storage, indexing, recall, and command helpers for Kilo Code", diff --git a/packages/kilo-sandbox/package.json b/packages/kilo-sandbox/package.json index 0f992f992a..fca4593950 100644 --- a/packages/kilo-sandbox/package.json +++ b/packages/kilo-sandbox/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@kilocode/sandbox", - "version": "7.4.20", + "version": "7.4.21", "type": "module", "license": "MIT", "private": true, diff --git a/packages/kilo-telemetry/package.json b/packages/kilo-telemetry/package.json index 4ce691dbb5..84cced9801 100644 --- a/packages/kilo-telemetry/package.json +++ b/packages/kilo-telemetry/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@kilocode/kilo-telemetry", - "version": "7.4.20", + "version": "7.4.21", "type": "module", "license": "MIT", "description": "Telemetry for Kilo CLI - PostHog analytics integration", diff --git a/packages/kilo-ui/package.json b/packages/kilo-ui/package.json index 6948cbadba..fbe3066d9e 100644 --- a/packages/kilo-ui/package.json +++ b/packages/kilo-ui/package.json @@ -1,6 +1,6 @@ { "name": "@kilocode/kilo-ui", - "version": "7.4.20", + "version": "7.4.21", "type": "module", "license": "MIT", "exports": { diff --git a/packages/kilo-vscode/CHANGELOG.md b/packages/kilo-vscode/CHANGELOG.md index 2a67fd8e94..17cc3dde71 100644 --- a/packages/kilo-vscode/CHANGELOG.md +++ b/packages/kilo-vscode/CHANGELOG.md @@ -1,5 +1,135 @@ # kilo-code +## 7.4.21 + +### Minor Changes + +- [#12945](https://github.com/Kilo-Org/kilocode/pull/12945) [`e0dfec0`](https://github.com/Kilo-Org/kilocode/commit/e0dfec020dd087c97a7b5644db3ab76105fab212) - Improve Agent Manager terminal focus and keyboard navigation. `Cmd+/` now focuses a visible embedded terminal before hiding it, `Cmd+Shift+T` creates a side terminal only while that terminal area has focus, and `Cmd+Shift+[` / `]` switch terminal tabs. `Cmd+Shift+M` focuses the Agent Manager prompt instead of opening VS Code Problems. `Cmd+W` hides the last side terminal instead of stopping its shell. + +- [#12943](https://github.com/Kilo-Org/kilocode/pull/12943) [`28ca073`](https://github.com/Kilo-Org/kilocode/commit/28ca0733bbe007c15b98eb28ddbf5a2bbb7a3fd8) - Improve model search relevance with provider-aware results and personalized usage suggestions. + +- [#12931](https://github.com/Kilo-Org/kilocode/pull/12931) [`fa99749`](https://github.com/Kilo-Org/kilocode/commit/fa9974996ac9f8d0bd37a62ecb339c0b524e4a26) - Let Agent Manager users choose the repository when creating or importing a worktree in multi-project mode. + +- [#12995](https://github.com/Kilo-Org/kilocode/pull/12995) [`729f791`](https://github.com/Kilo-Org/kilocode/commit/729f79133e60748a90a596fb3ec5f9566ec48a76) Thanks [@bagatao-anaconda](https://github.com/bagatao-anaconda)! - Move the "why was this tool call approved" line to after the tool output instead of between the header and body, add an icon to it, and add a Display setting to hide it. + +- [#12991](https://github.com/Kilo-Org/kilocode/pull/12991) [`0e1f11b`](https://github.com/Kilo-Org/kilocode/commit/0e1f11bed6b243f5f9379ecf05f68577e666e87a) - Add nested slash command suggestions for `/review` in VS Code and support `staged`, `unpushed`, and `quick` review modes. + +- [#13014](https://github.com/Kilo-Org/kilocode/pull/13014) [`73844ae`](https://github.com/Kilo-Org/kilocode/commit/73844ae68d27d095681c2369a8093156b106a453) - Support browsing an instance's model catalog before a session starts. + +- [#12859](https://github.com/Kilo-Org/kilocode/pull/12859) [`7db6b96`](https://github.com/Kilo-Org/kilocode/commit/7db6b9609d370c692c1b0e5d25cb898411236ef4) - Support model, agent, variant, and sandbox slash commands in Agent Manager worktree prompts. + +### Patch Changes + +- [#12940](https://github.com/Kilo-Org/kilocode/pull/12940) [`3092ce0`](https://github.com/Kilo-Org/kilocode/commit/3092ce036c493844338c5c77cb58d5a692079ce9) - Fix the Agent Manager sidebar keyboard shortcut badge so it appears on the right edge of local rows and only while hovered or holding the jump modifier, give worktree titles more room by no longer reserving space for hidden row actions, align project names and section headings with the row icons below them, and show which worktree a session belongs to in the search palette. + +- [#12975](https://github.com/Kilo-Org/kilocode/pull/12975) [`56e981b`](https://github.com/Kilo-Org/kilocode/commit/56e981b589c3c772fc388e6a0d349da6d5543b17) - Align multi-project Agent Manager header actions with the worktree controls. + +- [#13034](https://github.com/Kilo-Org/kilocode/pull/13034) [`4ff8f5e`](https://github.com/Kilo-Org/kilocode/commit/4ff8f5e1105652b98f49210608e9f28e98a92f8b) - Show underlying models for every Auto model in the model selector when the catalog includes them. + +- [#12941](https://github.com/Kilo-Org/kilocode/pull/12941) [`bddce1a`](https://github.com/Kilo-Org/kilocode/commit/bddce1a49dd78c9673e971cdedd3665758378ac6) - Automatically expose broad reasoning effort options for custom provider models and link saved providers to advanced JSON configuration. + +- [#12927](https://github.com/Kilo-Org/kilocode/pull/12927) [`58f8b02`](https://github.com/Kilo-Org/kilocode/commit/58f8b02520b816a02a3acd9fad38042c6e693f24) - Remember Agent Manager worktree dialog model, variant, mode, and sandbox selections when reopened. + +- [#12924](https://github.com/Kilo-Org/kilocode/pull/12924) [`b521829`](https://github.com/Kilo-Org/kilocode/commit/b5218296422e015b69d8019a1cb7fb864b0855e3) - Reduce Agent Manager background Git polling load across large worktree sets. + +- [#12865](https://github.com/Kilo-Org/kilocode/pull/12865) [`35801cb`](https://github.com/Kilo-Org/kilocode/commit/35801cbeed53f48c3198dc17bfebedca613ac705) - Prevent VS Code sessions and Agent Manager worktrees from starting unused file watchers and defer file indexing until search is used. + +- [#12982](https://github.com/Kilo-Org/kilocode/pull/12982) [`454cb93`](https://github.com/Kilo-Org/kilocode/commit/454cb934a04c2b62d2cbe38715e4f8f2a2c967d0) - Update the active model, mode, and thinking selectors when executing a slash command with configured overrides. + +- [#12983](https://github.com/Kilo-Org/kilocode/pull/12983) [`40121d8`](https://github.com/Kilo-Org/kilocode/commit/40121d832591d743cd360482e4f19e21a353310a) - Compress speech-to-text audio input to AAC format across macOS, Linux, and Windows to prevent payload size errors on long recordings. + +- [#13046](https://github.com/Kilo-Org/kilocode/pull/13046) [`9816009`](https://github.com/Kilo-Org/kilocode/commit/981600921ae695decac7f80449d765cae67cd83d) - Improve custom provider edit dialog layout, make advanced configuration action prominent, and add bulk toggle buttons for reasoning and image modalities across all models. + +- [#12979](https://github.com/Kilo-Org/kilocode/pull/12979) [`0485e36`](https://github.com/Kilo-Org/kilocode/commit/0485e361c6fcde687923daf2d42a1761bd315366) - Start Agent Manager worktree sessions faster by prefetching base branches, reducing workspace file-watcher load, and overlapping independent multi-session setup. + +- [#12985](https://github.com/Kilo-Org/kilocode/pull/12985) [`5ebec95`](https://github.com/Kilo-Org/kilocode/commit/5ebec9502d106a692dd8bc7adabbd69cbeb8615a) - Fix the Agent Manager terminal toolbar button to toggle panel visibility directly. + +- [#12936](https://github.com/Kilo-Org/kilocode/pull/12936) [`26e113c`](https://github.com/Kilo-Org/kilocode/commit/26e113c100eb0fd7cf17424b68954646f695ff06) - Prevent extension-managed GitHub CLI commands from opening transient Windows Terminal windows. + +- [#12958](https://github.com/Kilo-Org/kilocode/pull/12958) [`fdc4665`](https://github.com/Kilo-Org/kilocode/commit/fdc46654c5860c01276d7034ba715f779b163bca) - Reduce noisy memory timeout warnings and retry transient background consolidation failures once. + +- [#12993](https://github.com/Kilo-Org/kilocode/pull/12993) [`d3c50e6`](https://github.com/Kilo-Org/kilocode/commit/d3c50e62128ac53718b40d841f254f83dc91ba45) - Route Agent Manager tool-launched sessions to the project that owns the tool event directory, keep sandboxed worktree sessions inside their active worktree, and wait for busy managed sessions before prompting them. + +- [#12843](https://github.com/Kilo-Org/kilocode/pull/12843) [`b894206`](https://github.com/Kilo-Org/kilocode/commit/b894206b6306c42bc1bddf8eccfca4e3689d49e7) - Fix multi-project Agent Manager keyboard navigation and Cmd/Ctrl shortcut selection when worktrees are grouped in sections. + +- [#12850](https://github.com/Kilo-Org/kilocode/pull/12850) [`561d178`](https://github.com/Kilo-Org/kilocode/commit/561d1782aedd2c8b98f6e2cd6d88937b9c37fd5b) - Fix Agent Manager progress indicators when multiple projects are expanded. + +- [#12871](https://github.com/Kilo-Org/kilocode/pull/12871) [`5a95577`](https://github.com/Kilo-Org/kilocode/commit/5a955778daec603e11495343a432555736b4778f) - Speed up local VS Code snapshot installs and scope Agent Manager session events and Git status to the active project, including edits inside nested repositories. + +- [#12894](https://github.com/Kilo-Org/kilocode/pull/12894) [`573a660`](https://github.com/Kilo-Org/kilocode/commit/573a660cc1026469338b2fabcff52b8012a4f78a) Thanks [@bagatao-anaconda](https://github.com/bagatao-anaconda)! - Fix the sidebar navigation bar (New Task, History, Agent Manager, KiloClaw, Marketplace, Profile, Settings) disappearing in Cursor when the Kilo Code view is docked in the Secondary Side Bar. Cursor now renders the navigation inside the webview itself so it stays visible regardless of dock location. VS Code is unaffected — it continues to use its native title bar toolbar, which already worked correctly everywhere. + +- [#13009](https://github.com/Kilo-Org/kilocode/pull/13009) [`c514c08`](https://github.com/Kilo-Org/kilocode/commit/c514c08749dbb72fe4db64c12c75218997b769ff) - Fix flickering and sticky scrolling when scrolling up in Agent Manager and chat sessions. + +- [#13040](https://github.com/Kilo-Org/kilocode/pull/13040) [`48c4a4a`](https://github.com/Kilo-Org/kilocode/commit/48c4a4af227572011bf44c172ab0ae86e0c2a429) - Ignore negative pricing entries from model catalogs and handle unpriced models gracefully in UI price formatting. + +- [#12866](https://github.com/Kilo-Org/kilocode/pull/12866) [`ee1ac30`](https://github.com/Kilo-Org/kilocode/commit/ee1ac30e464b9f40b6362d276ec1dc059e1bd5e3) - Accept terminal input immediately while the Agent Manager shell starts. + +- [#12981](https://github.com/Kilo-Org/kilocode/pull/12981) [`ead46f4`](https://github.com/Kilo-Org/kilocode/commit/ead46f43415a121bacc28780bc47fe43b1b736ba) Thanks [@fxnie](https://github.com/fxnie)! - Fix Mermaid Copy PNG/SVG in VS Code webviews so they put images on the clipboard instead of failing or copying SVG markup. + +- [#12989](https://github.com/Kilo-Org/kilocode/pull/12989) [`e3e2327`](https://github.com/Kilo-Org/kilocode/commit/e3e2327803f72c42d44dbd587a34f884b1f2009d) - Keep Agent Manager terminals aligned and correctly wrapped when the terminal panel is narrow. + +- [#12819](https://github.com/Kilo-Org/kilocode/pull/12819) [`220cdd2`](https://github.com/Kilo-Org/kilocode/commit/220cdd20c8af4d4420ee0c4bf9bc7da8a0e82673) - Keep recent sessions and the Show History action inset and usable in narrow VS Code sidebars. + +- [#12695](https://github.com/Kilo-Org/kilocode/pull/12695) [`a606a91`](https://github.com/Kilo-Org/kilocode/commit/a606a91e6929807e9979148083fb2e73af5da85c) - Changes from opencode v1.17.9 to v1.17.13 upstream: + - Core Improvements: MCP servers can append their instructions to the model context, and MCP resources are available as tools with template listing. + - Core Improvements: Model variants are generated from models.dev data, including modes exposed as models. + - Core Improvements: Tool definitions pass `strict` through for Codex parity, and Gemini requests support video and audio media. + - Core Bugfixes: Interrupted assistant steps settle instead of leaving sessions stuck busy. + - Core Bugfixes: MCP OAuth reconnects after authorization even when the server is disabled, refreshes credentials on reauthentication, requests refresh token scope, surfaces completion errors, and binds its callback to the IPv4 loopback. + - Core Bugfixes: MCP tool results prefer content over structured output, and denied resource template tools stay hidden. + - Core Bugfixes: Stale GitHub Copilot Responses item IDs are no longer replayed, and OpenAI reasoning variants are forced where required. + - Core Bugfixes: Adaptive thinking is enabled for Claude Sonnet 5, and expired promos were removed from the zen catalog. + - Core Bugfixes: Preserve released prompt history during database replay and keep native event streams connected for all supported Kilo events. + - Core Bugfixes: Remote skill manifests support optional per-skill versions; changing a version refreshes the cached skill atomically, and skill base directories are emitted as filesystem paths. + - CLI Improvements: Ports increment from the default when busy. + - CLI Improvements: Use `--auto` to start the TUI in a run-scoped auto-approve mode, and leave the mode mid-session from the command palette. + - TUI Improvements: Redesigned crash screen, model picker sorted by release date, bindable diff viewer and Move Session commands, main-branch diff source, and inline skill load errors. + - TUI Bugfixes: File autocomplete is scoped to the session, multi-day durations format correctly, and root sessions load in the session switcher. + +- [#12844](https://github.com/Kilo-Org/kilocode/pull/12844) [`245ada5`](https://github.com/Kilo-Org/kilocode/commit/245ada532e30f0eb2ba494531f8f33dec5855861) - Allow Agent Manager project headers to collapse or expand their project body, and persist that state across panel opens and VS Code restarts. + +- [#12944](https://github.com/Kilo-Org/kilocode/pull/12944) [`1e17055`](https://github.com/Kilo-Org/kilocode/commit/1e1705551990dc07254a385a2ef3f7fb3fdd6c15) - Preserve the selected reasoning effort when switching to a model that supports the same or nearest available variant. + +- [#12845](https://github.com/Kilo-Org/kilocode/pull/12845) [`8fe5329`](https://github.com/Kilo-Org/kilocode/commit/8fe53293076e410e9428112e1ed1d0681b3b0792) - Show previous and next navigation hints using each project's own Agent Manager sidebar order. + +- [#12860](https://github.com/Kilo-Org/kilocode/pull/12860) [`a8edd85`](https://github.com/Kilo-Org/kilocode/commit/a8edd857ae43c32e4ef76faa27d5488faaa07520) - Keep Agent Manager worktree rows isolated when projects contain identical raw worktree IDs. + +- [#12883](https://github.com/Kilo-Org/kilocode/pull/12883) [`95da8ee`](https://github.com/Kilo-Org/kilocode/commit/95da8ee6fc7ff3a7d7df71d6a56068e68484cba3) - Move the prompt navigator rail to the panel edge so it is harder to open by accident, and keep it clear of the pane splitter while resizing. + +- [#12888](https://github.com/Kilo-Org/kilocode/pull/12888) [`c0649f7`](https://github.com/Kilo-Org/kilocode/commit/c0649f7cb27aabf2cf992aa88eaed132adee91f9) - Reduce Agent Manager Git polling overhead by reusing the validated Git executable and bypassing the macOS developer-tool launcher when safe. + +- [#12925](https://github.com/Kilo-Org/kilocode/pull/12925) [`70daa63`](https://github.com/Kilo-Org/kilocode/commit/70daa630d3f83f960f9255d9c95e5727b79ce229) - Use a compatible Kilo wordmark in embedded Agent Manager terminals. + +- [#12885](https://github.com/Kilo-Org/kilocode/pull/12885) [`817c04b`](https://github.com/Kilo-Org/kilocode/commit/817c04b6640fb20fc5085f9d9f45e4a53370760a) - Prevent Agent Manager overview requests from timing out while refreshing Git statistics across many worktrees. + +- [#12851](https://github.com/Kilo-Org/kilocode/pull/12851) [`44a908a`](https://github.com/Kilo-Org/kilocode/commit/44a908a8d4a94c9a41676ac895ed11fa1d2dfd9f) - Remove the redundant selected-project indicator from Agent Manager. + +- [#12600](https://github.com/Kilo-Org/kilocode/pull/12600) [`4e36297`](https://github.com/Kilo-Org/kilocode/commit/4e36297668bb36ab34c0b4f0bc6a0484baef3145) - Apply saved sandbox settings to existing sessions and use the latest settings when enabling sandboxing + +- [#13001](https://github.com/Kilo-Org/kilocode/pull/13001) [`d047224`](https://github.com/Kilo-Org/kilocode/commit/d047224302041a5388f9ad1a04ce3a7f2e652581) Thanks [@bagatao-anaconda](https://github.com/bagatao-anaconda)! - Show the permission approval reason for reads and writes outside the workspace, matching other tools + +- [#13045](https://github.com/Kilo-Org/kilocode/pull/13045) [`00dc8d0`](https://github.com/Kilo-Org/kilocode/commit/00dc8d0242f787a48e0cf44396f6a743b75b45e0) - Keep model and provider errors visible in VS Code when chat history refreshes. + +- [#12857](https://github.com/Kilo-Org/kilocode/pull/12857) [`d210a92`](https://github.com/Kilo-Org/kilocode/commit/d210a92a3fb29f2a34eb3c3e9a94e7a1741eddb5) - Show only one empty-state panel after removing the last Agent Manager worktree. + +- [#13008](https://github.com/Kilo-Org/kilocode/pull/13008) [`b234d25`](https://github.com/Kilo-Org/kilocode/commit/b234d25337142417b07d83a715e7ad4c3391dca4) - Surface process exit signals and structured spawn failure details in server startup diagnostics + +- [#12858](https://github.com/Kilo-Org/kilocode/pull/12858) [`e227378`](https://github.com/Kilo-Org/kilocode/commit/e2273783f68c039d74d666e34f45c13c07229b5a) - Persist the Agent Manager inspector width, share it between the terminal and diff viewer, and keep resizing responsive. + +- [#12956](https://github.com/Kilo-Org/kilocode/pull/12956) [`6083de8`](https://github.com/Kilo-Org/kilocode/commit/6083de856517f9a3915967a2d3e7419deaee2d8d) - Translate the Agent Manager terminal focus shortcut label in all supported locales. + +- [#12971](https://github.com/Kilo-Org/kilocode/pull/12971) [`e2db064`](https://github.com/Kilo-Org/kilocode/commit/e2db06453915fe25a42122a64348285aee2c8f60) Thanks [@rakshith1928](https://github.com/rakshith1928)! - Long skill folder paths and URLs shown in the tooltip on the Skills settings page now wrap inside the viewport instead of overflowing on a single line. + +- [#12852](https://github.com/Kilo-Org/kilocode/pull/12852) [`87b53e4`](https://github.com/Kilo-Org/kilocode/commit/87b53e4e793a59beb639ef579ea36d4e0e7189a7) - Keep multi-project worktree rename inputs focused while selection updates are still settling. + +- Updated dependencies [[`9816009`](https://github.com/Kilo-Org/kilocode/commit/981600921ae695decac7f80449d765cae67cd83d), [`fdc4665`](https://github.com/Kilo-Org/kilocode/commit/fdc46654c5860c01276d7034ba715f779b163bca), [`c514c08`](https://github.com/Kilo-Org/kilocode/commit/c514c08749dbb72fe4db64c12c75218997b769ff), [`48c4a4a`](https://github.com/Kilo-Org/kilocode/commit/48c4a4af227572011bf44c172ab0ae86e0c2a429)]: + - @kilocode/kilo-ui@7.4.21 + - @kilocode/kilo-memory@7.4.21 + - @kilocode/kilo-gateway@7.4.21 + - @opencode-ai/core@7.4.21 + - @kilocode/kilo-indexing@7.4.21 + - @opencode-ai/ui@7.4.21 + ## 7.4.20 ### Patch Changes diff --git a/packages/kilo-vscode/package.json b/packages/kilo-vscode/package.json index 2721db62a0..d1a01d843e 100644 --- a/packages/kilo-vscode/package.json +++ b/packages/kilo-vscode/package.json @@ -2,7 +2,7 @@ "name": "kilo-code", "displayName": "Kilo Code: AI Coding Agent, Copilot, and Autocomplete", "description": "Open Source AI coding agent that generates code from natural language, automates tasks, and runs terminal commands. Features inline autocomplete, browser automation, automated refactoring, and custom modes for planning, coding, and debugging. Supports 500+ AI models including Claude (Anthropic), Gemini, Grok, GPT, Codex and GLM.", - "version": "7.4.20", + "version": "7.4.21", "icon": "assets/icons/logo-outline-black.png", "galleryBanner": { "color": "#FFFFFF", diff --git a/packages/kilo-vscode/tests/package.json b/packages/kilo-vscode/tests/package.json index 9b3ef8d581..cdec7ddf5d 100644 --- a/packages/kilo-vscode/tests/package.json +++ b/packages/kilo-vscode/tests/package.json @@ -1,6 +1,6 @@ { "type": "module", - "version": "7.4.20", + "version": "7.4.21", "dependencies": {}, "devDependencies": {}, "peerDependencies": {} diff --git a/packages/kilo-web-ui/package.json b/packages/kilo-web-ui/package.json index 85f089d104..1ef02a9db2 100644 --- a/packages/kilo-web-ui/package.json +++ b/packages/kilo-web-ui/package.json @@ -1,6 +1,6 @@ { "name": "@kilocode/kilo-web-ui", - "version": "7.4.20", + "version": "7.4.21", "type": "module", "license": "MIT", "exports": { diff --git a/packages/llm/package.json b/packages/llm/package.json index 971483d092..dc372c63bb 100644 --- a/packages/llm/package.json +++ b/packages/llm/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "7.4.20", + "version": "7.4.21", "name": "@opencode-ai/llm", "type": "module", "license": "MIT", diff --git a/packages/opencode/CHANGELOG.md b/packages/opencode/CHANGELOG.md index 546437ee83..963d9d57e1 100644 --- a/packages/opencode/CHANGELOG.md +++ b/packages/opencode/CHANGELOG.md @@ -1,5 +1,88 @@ # @kilocode/cli +## 7.4.21 + +### Minor Changes + +- [#12825](https://github.com/Kilo-Org/kilocode/pull/12825) [`b692f1d`](https://github.com/Kilo-Org/kilocode/commit/b692f1ded1969165587a184e359d0848dc3e9bea) - Add kilocode command-file endpoints so clients can list editable command/workflow files, inspect model and reasoning variant metadata, and remove them. + +- [#12991](https://github.com/Kilo-Org/kilocode/pull/12991) [`0e1f11b`](https://github.com/Kilo-Org/kilocode/commit/0e1f11bed6b243f5f9379ecf05f68577e666e87a) - Add nested slash command suggestions for `/review` in VS Code and support `staged`, `unpushed`, and `quick` review modes. + +- [#12824](https://github.com/Kilo-Org/kilocode/pull/12824) [`e87dc77`](https://github.com/Kilo-Org/kilocode/commit/e87dc77b73c9e5226a79d7736ce85acc367b607e) - Import conversation history from Claude Code and OpenAI Codex sessions with the /resume-claude and /resume-codex slash commands. + +### Patch Changes + +- [#12941](https://github.com/Kilo-Org/kilocode/pull/12941) [`bddce1a`](https://github.com/Kilo-Org/kilocode/commit/bddce1a49dd78c9673e971cdedd3665758378ac6) - Automatically expose broad reasoning effort options for custom provider models and link saved providers to advanced JSON configuration. + +- [#12865](https://github.com/Kilo-Org/kilocode/pull/12865) [`35801cb`](https://github.com/Kilo-Org/kilocode/commit/35801cbeed53f48c3198dc17bfebedca613ac705) - Prevent VS Code sessions and Agent Manager worktrees from starting unused file watchers and defer file indexing until search is used. + +- [#12926](https://github.com/Kilo-Org/kilocode/pull/12926) [`90ac91d`](https://github.com/Kilo-Org/kilocode/commit/90ac91d501eae78602db747ec35ea16473d2fb8f) - Prevent built-in skill documentation examples from triggering shell permission prompts. + +- [#13007](https://github.com/Kilo-Org/kilocode/pull/13007) [`910f0f2`](https://github.com/Kilo-Org/kilocode/commit/910f0f24d2b38ff04b43dee694f6968608f54eb1) - Disable the suggest tool and auto-dismiss pending suggestions in non-interactive CLI runs to prevent hanging on benchmarks and automated pipelines. + +- [#13044](https://github.com/Kilo-Org/kilocode/pull/13044) [`2e5199a`](https://github.com/Kilo-Org/kilocode/commit/2e5199a40d8791e99dc304ad722bf6baa707f09c) - Exclude ChatGPT subscriptions from explicit prompt cache breakpoints. + +- [#12935](https://github.com/Kilo-Org/kilocode/pull/12935) [`3917ed1`](https://github.com/Kilo-Org/kilocode/commit/3917ed1f9bd50232b311efc47974e4df0a30ef6c) - Restore keyboard input for interactive terminal prompts when the CLI session uses a workspace. + +- [#12554](https://github.com/Kilo-Org/kilocode/pull/12554) [`dcf9c5a`](https://github.com/Kilo-Org/kilocode/commit/dcf9c5a0be9bdd4ccfeeea531ceafb07c74d0286) Thanks [@arimu1](https://github.com/arimu1)! - Prevent project MCP configs from resolving variable-backed headers or inheriting trusted headers when changing endpoints, while preserving unaffected servers. + +- [#12958](https://github.com/Kilo-Org/kilocode/pull/12958) [`fdc4665`](https://github.com/Kilo-Org/kilocode/commit/fdc46654c5860c01276d7034ba715f779b163bca) - Reduce noisy memory timeout warnings and retry transient background consolidation failures once. + +- [#12993](https://github.com/Kilo-Org/kilocode/pull/12993) [`d3c50e6`](https://github.com/Kilo-Org/kilocode/commit/d3c50e62128ac53718b40d841f254f83dc91ba45) - Route Agent Manager tool-launched sessions to the project that owns the tool event directory, keep sandboxed worktree sessions inside their active worktree, and wait for busy managed sessions before prompting them. + +- [#12937](https://github.com/Kilo-Org/kilocode/pull/12937) [`4ea52f2`](https://github.com/Kilo-Org/kilocode/commit/4ea52f2dd17d56ba6c7a1ac0896b17ff020314ba) - Allow subagent tasks to be resumed after their parent session is forked. + +- [#12946](https://github.com/Kilo-Org/kilocode/pull/12946) [`24da90f`](https://github.com/Kilo-Org/kilocode/commit/24da90ff579dcbac6c5d8c5930f9bffb6da66f26) - Support the Agent Manager tool with llama.cpp servers that reject prefix-only JSON Schema patterns. + +- [#12882](https://github.com/Kilo-Org/kilocode/pull/12882) [`9bfbc35`](https://github.com/Kilo-Org/kilocode/commit/9bfbc35c5c674b09600f169a87704c2dee50a1b4) - Add bounded, context-aware grep controls without leaving agents waiting on completed searches. + +- [#13040](https://github.com/Kilo-Org/kilocode/pull/13040) [`48c4a4a`](https://github.com/Kilo-Org/kilocode/commit/48c4a4af227572011bf44c172ab0ae86e0c2a429) - Ignore negative pricing entries from model catalogs and handle unpriced models gracefully in UI price formatting. + +- [#13022](https://github.com/Kilo-Org/kilocode/pull/13022) [`9dbf276`](https://github.com/Kilo-Org/kilocode/commit/9dbf276adec8a6b88e451eb13422272e16435cbe) - Set explicit prompt cache breakpoints on stable prefixes for OpenAI GPT-5.6+ models. + +- [#12695](https://github.com/Kilo-Org/kilocode/pull/12695) [`a606a91`](https://github.com/Kilo-Org/kilocode/commit/a606a91e6929807e9979148083fb2e73af5da85c) - Changes from opencode v1.17.9 to v1.17.13 upstream: + - Core Improvements: MCP servers can append their instructions to the model context, and MCP resources are available as tools with template listing. + - Core Improvements: Model variants are generated from models.dev data, including modes exposed as models. + - Core Improvements: Tool definitions pass `strict` through for Codex parity, and Gemini requests support video and audio media. + - Core Bugfixes: Interrupted assistant steps settle instead of leaving sessions stuck busy. + - Core Bugfixes: MCP OAuth reconnects after authorization even when the server is disabled, refreshes credentials on reauthentication, requests refresh token scope, surfaces completion errors, and binds its callback to the IPv4 loopback. + - Core Bugfixes: MCP tool results prefer content over structured output, and denied resource template tools stay hidden. + - Core Bugfixes: Stale GitHub Copilot Responses item IDs are no longer replayed, and OpenAI reasoning variants are forced where required. + - Core Bugfixes: Adaptive thinking is enabled for Claude Sonnet 5, and expired promos were removed from the zen catalog. + - Core Bugfixes: Preserve released prompt history during database replay and keep native event streams connected for all supported Kilo events. + - Core Bugfixes: Remote skill manifests support optional per-skill versions; changing a version refreshes the cached skill atomically, and skill base directories are emitted as filesystem paths. + - CLI Improvements: Ports increment from the default when busy. + - CLI Improvements: Use `--auto` to start the TUI in a run-scoped auto-approve mode, and leave the mode mid-session from the command palette. + - TUI Improvements: Redesigned crash screen, model picker sorted by release date, bindable diff viewer and Move Session commands, main-branch diff source, and inline skill load errors. + - TUI Bugfixes: File autocomplete is scoped to the session, multi-day durations format correctly, and root sessions load in the session switcher. + +- [#12442](https://github.com/Kilo-Org/kilocode/pull/12442) [`6b8c736`](https://github.com/Kilo-Org/kilocode/commit/6b8c736dc1c97544467f6edf8026d271149e4164) Thanks [@IamCoder18](https://github.com/IamCoder18)! - Add a privacy mode that blurs PII in the TUI (personal balance, Kilo Pass usage, etc.) and requires confirmation before `/profile` reveals email, name, balance, and team. Toggle with the new `/privacy` command or by setting `privacy_mode` in `kilo.json`. The `kilo profile` CLI command is unaffected. + +- [#12897](https://github.com/Kilo-Org/kilocode/pull/12897) [`e83b25e`](https://github.com/Kilo-Org/kilocode/commit/e83b25e8d93ee9c236514e4562f828b2e5f858e4) - Fix high CPU and runaway memory growth in the JetBrains background `kilo serve` process on macOS by no longer eagerly starting native file watchers, matching the VS Code backend. + +- [#12884](https://github.com/Kilo-Org/kilocode/pull/12884) [`c9199cb`](https://github.com/Kilo-Org/kilocode/commit/c9199cb529fd24be5d7deaa2cffc853d251ebbca) - Show a concise retryable message when concurrent Kilo processes temporarily lock the SQLite database instead of printing the full server error trace. + +- [#12929](https://github.com/Kilo-Org/kilocode/pull/12929) [`16deb19`](https://github.com/Kilo-Org/kilocode/commit/16deb199d738fb3a67d5deee5ff9f66eaa7a54a5) - Prevent TUI config reload logs from corrupting the interactive terminal. + +- [#12950](https://github.com/Kilo-Org/kilocode/pull/12950) [`d03d579`](https://github.com/Kilo-Org/kilocode/commit/d03d579fa2c1c3588b53a9f6ed47ebfc1856aa0a) - Avoid printing an error when closing the TUI cancels in-flight startup refreshes. + +- [#12978](https://github.com/Kilo-Org/kilocode/pull/12978) [`1406d71`](https://github.com/Kilo-Org/kilocode/commit/1406d719e36afd6d98d60b9fe54fa79b51e6b294) - Remove unsupported `kilo web` CLI command. + +- [#12947](https://github.com/Kilo-Org/kilocode/pull/12947) [`154b1ae`](https://github.com/Kilo-Org/kilocode/commit/154b1ae53ca1a4bca1aa4c43f4ef95fe6cafaa75) - Prevent concurrent Kilo startups from rewriting unchanged credentials, retry transient database locks, and redact bound values from database errors. + +- [#12600](https://github.com/Kilo-Org/kilocode/pull/12600) [`4e36297`](https://github.com/Kilo-Org/kilocode/commit/4e36297668bb36ab34c0b4f0bc6a0484baef3145) - Apply saved sandbox settings to existing sessions and use the latest settings when enabling sandboxing + +- [#13047](https://github.com/Kilo-Org/kilocode/pull/13047) [`e48c534`](https://github.com/Kilo-Org/kilocode/commit/e48c534978e5864662f9f155815c029cfe549f30) - Separate autocomplete item names from their descriptions in the TUI. + +- Updated dependencies [[`fdc4665`](https://github.com/Kilo-Org/kilocode/commit/fdc46654c5860c01276d7034ba715f779b163bca), [`48c4a4a`](https://github.com/Kilo-Org/kilocode/commit/48c4a4af227572011bf44c172ab0ae86e0c2a429)]: + - @kilocode/kilo-memory@7.4.21 + - @kilocode/kilo-gateway@7.4.21 + - @kilocode/kilo-indexing@7.4.21 + - @kilocode/kilo-telemetry@7.4.21 + - @opencode-ai/server@7.4.21 + - @opencode-ai/tui@7.4.21 + - @opencode-ai/ui@7.4.21 + ## 7.4.20 ### Patch Changes diff --git a/packages/opencode/package.json b/packages/opencode/package.json index af48b9c5fd..d7e257190a 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "7.4.20", + "version": "7.4.21", "name": "@kilocode/cli", "type": "module", "license": "MIT", diff --git a/packages/plugin-atomic-chat/package.json b/packages/plugin-atomic-chat/package.json index 56cc83f529..b1b8201d84 100644 --- a/packages/plugin-atomic-chat/package.json +++ b/packages/plugin-atomic-chat/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@kilocode/plugin-atomic-chat", - "version": "7.4.20", + "version": "7.4.21", "description": "Kilo Code plugin for Atomic Chat: auto-detection and dynamic model discovery (OpenAI-compatible local API)", "type": "module", "license": "MIT", diff --git a/packages/plugin/package.json b/packages/plugin/package.json index f83b7a84f7..ce3c8853ff 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@kilocode/plugin", - "version": "7.4.20", + "version": "7.4.21", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/protocol/package.json b/packages/protocol/package.json index f538635b18..9807f4aac0 100644 --- a/packages/protocol/package.json +++ b/packages/protocol/package.json @@ -19,5 +19,5 @@ "@types/bun": "catalog:", "@typescript/native-preview": "catalog:" }, - "version": "7.4.16" + "version": "7.4.21" } diff --git a/packages/schema/package.json b/packages/schema/package.json index 577df3666b..9c32ab9c3a 100644 --- a/packages/schema/package.json +++ b/packages/schema/package.json @@ -19,5 +19,5 @@ "@types/bun": "catalog:", "@typescript/native-preview": "catalog:" }, - "version": "7.4.16" + "version": "7.4.21" } diff --git a/packages/script/package.json b/packages/script/package.json index 2148c37554..a3dd3de32a 100644 --- a/packages/script/package.json +++ b/packages/script/package.json @@ -12,6 +12,6 @@ "exports": { ".": "./src/index.ts" }, - "version": "7.4.20", + "version": "7.4.21", "peerDependencies": {} } diff --git a/packages/sdk-next/package.json b/packages/sdk-next/package.json index dc4a77656c..c817fdbb10 100644 --- a/packages/sdk-next/package.json +++ b/packages/sdk-next/package.json @@ -23,5 +23,5 @@ "@types/bun": "catalog:", "@typescript/native-preview": "catalog:" }, - "version": "7.4.16" + "version": "7.4.21" } diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 51c5a26646..46302dceea 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@kilocode/sdk", - "version": "7.4.20", + "version": "7.4.21", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/server/package.json b/packages/server/package.json index 99a80f091e..1faa6a21a4 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/server", - "version": "7.4.20", + "version": "7.4.21", "private": true, "type": "module", "license": "MIT", diff --git a/packages/session-ui/package.json b/packages/session-ui/package.json index ab3353ae08..ebd2fdb4f4 100644 --- a/packages/session-ui/package.json +++ b/packages/session-ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/session-ui", - "version": "7.4.16", + "version": "7.4.21", "private": true, "type": "module", "license": "MIT", diff --git a/packages/storybook/package.json b/packages/storybook/package.json index 62c6557e44..6aca4a764f 100644 --- a/packages/storybook/package.json +++ b/packages/storybook/package.json @@ -27,7 +27,7 @@ "vite": "catalog:", "@opencode-ai/session-ui": "workspace:*" }, - "version": "7.4.20", + "version": "7.4.21", "dependencies": {}, "peerDependencies": {} } diff --git a/packages/tui/package.json b/packages/tui/package.json index 8e46b53bd5..70c12ee23d 100644 --- a/packages/tui/package.json +++ b/packages/tui/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/tui", - "version": "7.4.20", + "version": "7.4.21", "private": true, "type": "module", "license": "MIT", diff --git a/packages/ui/package.json b/packages/ui/package.json index c7766ab9bf..a047536707 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "7.4.20", + "version": "7.4.21", "type": "module", "license": "MIT", "repository": { diff --git a/script/upstream/package.json b/script/upstream/package.json index 9c1095b576..d27b5b7846 100644 --- a/script/upstream/package.json +++ b/script/upstream/package.json @@ -1,6 +1,6 @@ { "name": "@kilocode/upstream-merge", - "version": "7.4.20", + "version": "7.4.21", "private": true, "type": "module", "description": "Scripts for automating upstream opencode merges into Kilo",