Merge branch 'main' into hide-training-models

This commit is contained in:
Christiaan Arnoldus
2026-06-15 09:49:48 +02:00
committed by GitHub
249 changed files with 7015 additions and 1611 deletions
-5
View File
@@ -1,5 +0,0 @@
---
"@kilocode/cli": patch
---
Prevent unnecessary repeat auto-compactions when providers report inconsistent token totals.
+6
View File
@@ -0,0 +1,6 @@
---
"@kilocode/cli": patch
"kilo-code": patch
---
Support model-specific reasoning overrides for task subagents, including custom subagents with their own model and variant settings.
-5
View File
@@ -1,5 +0,0 @@
---
"kilo-code": patch
---
Preserve existing Agent Manager worktrees and use deterministic suffixes when explicit branch names collide.
-5
View File
@@ -1,5 +0,0 @@
---
"@kilocode/cli": patch
---
Allow hosted runtimes to cap shell command duration and explain environment-enforced timeouts.
-5
View File
@@ -1,5 +0,0 @@
---
"@kilocode/cli": patch
---
Indicate when no models are available in model-not-found errors.
-5
View File
@@ -1,5 +0,0 @@
---
"kilo-code": patch
---
Group Kilo Code commands in the VS Code command palette and clarify the Open in Tab command title.
-7
View File
@@ -1,7 +0,0 @@
---
"@kilocode/cli": minor
"@kilocode/kilo-indexing": minor
"kilo-code": minor
---
Use embedded LanceDB as the default semantic search vector store so indexing works without a separate Qdrant server. Existing Qdrant users and Intel Mac users can select `qdrant` with `indexing.vectorStore`.
-5
View File
@@ -1,5 +0,0 @@
---
"kilo-code": patch
---
Dismiss stale permission prompts when another view has already answered them.
-5
View File
@@ -1,5 +0,0 @@
---
"@kilocode/cli": patch
---
Speed up the first Agent Manager prompt in new worktrees by seeding snapshots from the checkout's Git index.
-5
View File
@@ -1,5 +0,0 @@
---
"kilo-code": patch
---
Keep large chat transcripts responsive by mounting only viewport-visible conversation rows.
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/cli": patch
---
Accelerate initial snapshots for regular Git sessions while preserving existing changes and asynchronously storing snapshots independently from the source repository.
-7
View File
@@ -1,7 +0,0 @@
---
"@kilocode/cli": patch
"@kilocode/kilo-ui": patch
"kilo-code": patch
---
Speed up large session forks by retaining final task outcomes instead of duplicating resumable subagent histories, and load completed task details only when expanded.
-5
View File
@@ -1,5 +0,0 @@
---
"@kilocode/cli": patch
---
Refresh connected provider model lists when the models catalog updates.
-7
View File
@@ -1,7 +0,0 @@
---
"kilo-code": patch
"@kilocode/cli": patch
"@kilocode/kilo-gateway": patch
---
Show model free and prompt-training indicators only when their explicit catalog metadata is enabled.
-5
View File
@@ -1,5 +0,0 @@
---
"kilo-code": patch
---
Keep HTTP status codes beside tool error titles when error details wrap.
-6
View File
@@ -1,6 +0,0 @@
---
"@kilocode/cli": patch
"kilo-code": patch
---
Prevent task subagents from asking questions that users cannot answer from the parent session.
-5
View File
@@ -1,5 +0,0 @@
---
"kilo-code": patch
---
Show the first Agent Manager Local prompt and progress indicator immediately while its session is being created.
-5
View File
@@ -1,5 +0,0 @@
---
"kilo-code": patch
---
Keep binary and audio files collapsed in diff reviews instead of showing empty diff panels.
-5
View File
@@ -1,5 +0,0 @@
---
"@kilocode/cli": patch
---
Prevent duplicate CLI attention alerts and route Kilo prompts through the configurable notification system.
-5
View File
@@ -1,5 +0,0 @@
---
"@kilocode/cli": patch
---
Stabilize code indexing workers, retry Kilo model catalog downloads, reduce progress log noise, and show indexing failures as TUI notifications instead of writing over the terminal interface.
@@ -1,5 +0,0 @@
---
"kilo-code": patch
---
Remove the unsupported paste-summary toggle from VS Code settings.
@@ -1,7 +0,0 @@
---
"kilo-code": patch
"@kilocode/cli": patch
"@kilocode/kilo-gateway": patch
---
Prevent streamed tool calls from executing twice and leaving answered questions disabled in VS Code.
-5
View File
@@ -1,5 +0,0 @@
---
"@kilocode/cli": patch
---
Restore Kilo branding, fork-specific CLI commands, and CLI lifecycle initialization after upstream merges.
-6
View File
@@ -1,6 +0,0 @@
---
"@kilocode/cli": patch
"kilo-code": patch
---
Remove the unsupported code search tool.
-6
View File
@@ -1,6 +0,0 @@
---
"@kilocode/cli": patch
"@kilocode/kilo-gateway": patch
---
Update the Vercel AI SDK providers for Cerebras, xAI, and OpenAI-compatible endpoints.
-5
View File
@@ -1,5 +0,0 @@
---
"kilo-code": patch
---
Keep permission approvals working when continuing or switching Agent Manager worktree sessions.
+5
View File
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---
Route question responses to the worktree where the question was created.
-6
View File
@@ -1,6 +0,0 @@
---
"@kilocode/cli": patch
"kilo-code": patch
---
Support configuring code indexing separately for global and project settings in Kilo Console, the CLI TUI, and VS Code.
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/cli": patch
---
Validate GitHub attachments and language server release paths before downloading or executing them.
@@ -1,5 +0,0 @@
---
"kilo-code": patch
---
Keep custom-answer selection in sync and show one submit action while entering custom text.
-5
View File
@@ -1,5 +0,0 @@
---
"kilo-code": patch
---
Preserve complete session state when applying partial session updates in VS Code.
@@ -1,5 +0,0 @@
---
"kilo-code": patch
---
Restore persisted sessions across development extension branches and worktrees.
-5
View File
@@ -1,5 +0,0 @@
---
"@kilocode/cli": patch
---
Restore streamed responses in the CLI TUI and move code indexing status into the session sidebar.
-5
View File
@@ -1,5 +0,0 @@
---
"kilo-vscode": patch
---
Agent Manager terminals now use `terminal.integrated.fontFamily` and `terminal.integrated.fontSize` (including Nerd Font glyphs) instead of the editor font.
-5
View File
@@ -1,5 +0,0 @@
---
"kilo-code": patch
---
Start MCP servers while Agent Manager worktree sessions initialize to reduce the delay before the first response.
+2 -2
View File
@@ -212,7 +212,7 @@ jobs:
for (const pr of stalePrs) {
const issue_number = pr.number
const closeComment = `To stay organized pull requests are automatically closed after ${PR_DAYS_INACTIVE} days of inactivity. If the pull request is still relevant please open a new one.`
const closeComment = `To stay organized pull requests are automatically closed after ${PR_DAYS_INACTIVE} days of inactivity. If the pull request is still relevant please reopen it or create a fresh new one.`
if (dryRun) {
core.info(`[dry-run] Would close PR #${issue_number} from ${pr.author?.login || 'unknown'} (last activity: ${pr.activity.date.toISOString()} via ${pr.activity.source}): ${pr.title}`)
@@ -314,7 +314,7 @@ jobs:
let skippedIssueCount = 0
for (const issue of staleIssues) {
const closeComment = `To stay organized issues are automatically closed after ${ISSUE_DAYS_INACTIVE} days of no activity. If the issue is still relevant please open a new one.`
const closeComment = `To stay organized issues are automatically closed after ${ISSUE_DAYS_INACTIVE} days of no activity. If the issue is still relevant please reopen it or create a fresh new one.`
if (dryRun) {
core.info(`[dry-run] Would close issue #${issue.number} (last activity: ${issue.activity.toISOString()} via updated): ${issue.title}`)
+20 -20
View File
@@ -33,7 +33,7 @@
},
"packages/core": {
"name": "@opencode-ai/core",
"version": "7.3.42",
"version": "7.3.45",
"bin": {
"opencode": "./bin/opencode",
},
@@ -96,7 +96,7 @@
},
"packages/http-recorder": {
"name": "@opencode-ai/http-recorder",
"version": "7.3.42",
"version": "7.3.45",
"dependencies": {
"@effect/platform-node": "catalog:",
"effect": "catalog:",
@@ -109,7 +109,7 @@
},
"packages/kilo-console": {
"name": "@kilocode/kilo-console",
"version": "7.3.42",
"version": "7.3.45",
"dependencies": {
"@kilocode/kilo-web-ui": "workspace:*",
"@kilocode/sdk": "workspace:*",
@@ -131,7 +131,7 @@
},
"packages/kilo-docs": {
"name": "@kilocode/kilo-docs",
"version": "7.3.42",
"version": "7.3.45",
"dependencies": {
"@docsearch/css": "^4",
"@docsearch/js": "^4",
@@ -161,7 +161,7 @@
},
"packages/kilo-gateway": {
"name": "@kilocode/kilo-gateway",
"version": "7.3.42",
"version": "7.3.45",
"dependencies": {
"@ai-sdk/alibaba": "1.0.17",
"@ai-sdk/anthropic": "3.0.71",
@@ -198,7 +198,7 @@
},
"packages/kilo-i18n": {
"name": "@kilocode/kilo-i18n",
"version": "7.3.42",
"version": "7.3.45",
"devDependencies": {
"@tsconfig/node22": "catalog:",
"@types/bun": "catalog:",
@@ -208,7 +208,7 @@
},
"packages/kilo-indexing": {
"name": "@kilocode/kilo-indexing",
"version": "7.3.42",
"version": "7.3.45",
"dependencies": {
"@aws-sdk/client-bedrock-runtime": "3.1005.0",
"@aws-sdk/credential-provider-ini": "3.972.31",
@@ -240,11 +240,11 @@
},
"packages/kilo-jetbrains": {
"name": "@kilocode/kilo-jetbrains",
"version": "7.3.42",
"version": "7.3.45",
},
"packages/kilo-telemetry": {
"name": "@kilocode/kilo-telemetry",
"version": "7.3.42",
"version": "7.3.45",
"dependencies": {
"@kilocode/kilo-gateway": "workspace:*",
"posthog-node": "4.4.0",
@@ -258,7 +258,7 @@
},
"packages/kilo-ui": {
"name": "@kilocode/kilo-ui",
"version": "7.3.42",
"version": "7.3.45",
"dependencies": {
"@kilocode/sdk": "workspace:*",
"@kobalte/core": "0.13.11",
@@ -295,7 +295,7 @@
},
"packages/kilo-vscode": {
"name": "kilo-code",
"version": "7.3.42",
"version": "7.3.45",
"dependencies": {
"@anthropic-ai/sdk": "^0.39.0",
"@kilocode/kilo-gateway": "workspace:*",
@@ -361,7 +361,7 @@
},
"packages/kilo-web-ui": {
"name": "@kilocode/kilo-web-ui",
"version": "7.3.42",
"version": "7.3.45",
"dependencies": {
"@kilocode/kilo-ui": "workspace:*",
"@kobalte/core": "catalog:",
@@ -376,7 +376,7 @@
},
"packages/llm": {
"name": "@opencode-ai/llm",
"version": "7.3.42",
"version": "7.3.45",
"dependencies": {
"@smithy/eventstream-codec": "4.2.14",
"@smithy/util-utf8": "4.2.2",
@@ -394,7 +394,7 @@
},
"packages/opencode": {
"name": "@kilocode/cli",
"version": "7.3.42",
"version": "7.3.45",
"bin": {
"kilo": "./bin/kilo",
"kilocode": "./bin/kilo",
@@ -562,7 +562,7 @@
},
"packages/plugin": {
"name": "@kilocode/plugin",
"version": "7.3.42",
"version": "7.3.45",
"dependencies": {
"@kilocode/sdk": "workspace:*",
"effect": "catalog:",
@@ -590,7 +590,7 @@
},
"packages/plugin-atomic-chat": {
"name": "@kilocode/plugin-atomic-chat",
"version": "7.3.42",
"version": "7.3.45",
"dependencies": {
"@kilocode/plugin": "workspace:*",
},
@@ -604,7 +604,7 @@
},
"packages/script": {
"name": "@opencode-ai/script",
"version": "7.3.42",
"version": "7.3.45",
"dependencies": {
"semver": "^7.6.3",
},
@@ -615,7 +615,7 @@
},
"packages/sdk/js": {
"name": "@kilocode/sdk",
"version": "7.3.42",
"version": "7.3.45",
"dependencies": {
"cross-spawn": "catalog:",
},
@@ -630,7 +630,7 @@
},
"packages/storybook": {
"name": "@opencode-ai/storybook",
"version": "7.3.42",
"version": "7.3.45",
"devDependencies": {
"@opencode-ai/ui": "workspace:*",
"@solidjs/meta": "catalog:",
@@ -653,7 +653,7 @@
},
"packages/ui": {
"name": "@opencode-ai/ui",
"version": "7.3.42",
"version": "7.3.45",
"dependencies": {
"@kilocode/sdk": "workspace:*",
"@kobalte/core": "catalog:",
+1 -1
View File
@@ -153,6 +153,6 @@
"solid-js@1.9.10": "patches/solid-js@1.9.10.patch",
"mammoth@1.12.0": "patches/mammoth@1.12.0.patch"
},
"version": "7.3.42",
"version": "7.3.45",
"peerDependencies": {}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "7.3.42",
"version": "7.3.45",
"name": "@opencode-ai/core",
"type": "module",
"license": "MIT",
+6 -6
View File
@@ -1,7 +1,7 @@
id = "kilo"
name = "Kilo"
description = "The open source coding agent."
version = "7.3.42"
version = "7.3.45"
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.3.42/opencode-darwin-arm64.zip"
archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.3.45/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.3.42/opencode-darwin-x64.zip"
archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.3.45/opencode-darwin-x64.zip"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.linux-aarch64]
archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.3.42/opencode-linux-arm64.tar.gz"
archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.3.45/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.3.42/opencode-linux-x64.tar.gz"
archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.3.45/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.3.42/opencode-windows-x64.zip"
archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.3.45/opencode-windows-x64.zip"
cmd = "./opencode.exe"
args = ["acp"]
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "7.3.42",
"version": "7.3.45",
"name": "@opencode-ai/http-recorder",
"type": "module",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@kilocode/kilo-console",
"version": "7.3.42",
"version": "7.3.45",
"private": true,
"type": "module",
"scripts": {
+4 -1
View File
@@ -52,6 +52,7 @@ export type ProjectConsoleQuery = ProjectQuery & {
export type ProjectConsoleSnapshot = {
project: ProjectItem
config: EffectiveConfig
vcs: VcsInfo
worktrees: string[]
terminals: ProjectTerminalItem[]
@@ -439,7 +440,8 @@ export async function loadProjectConsole(input: ProjectConsoleQuery): Promise<Pr
const project = await resolved(input)
const query = { url: input.url, dir: project.worktree }
const sdk = client(query)
const [vcs, worktrees] = await Promise.all([
const [config, vcs, worktrees] = await Promise.all([
sdk.config.overlay({ scope: "global" }),
sdk.vcs.get({ directory: query.dir }),
sdk.worktree.list({ directory: query.dir }),
])
@@ -450,6 +452,7 @@ export async function loadProjectConsole(input: ProjectConsoleQuery): Promise<Pr
return {
project,
config: demand("Config", config).effective,
vcs: demand("VCS", vcs),
worktrees: dirs,
terminals: terminals.flat(),
@@ -1,4 +1,4 @@
import { Show } from "solid-js"
import { createUniqueId, Show } from "solid-js"
import { Button } from "@kilocode/kilo-web-ui/button"
import { Icon } from "@kilocode/kilo-web-ui/icon"
@@ -14,21 +14,31 @@ type Props = {
}
export function ConfirmDialog(props: Props) {
const id = createUniqueId()
const title = `${id}-title`
const message = `${id}-message`
return (
<Show when={props.open}>
<div class="confirm-scrim">
<section class="confirm-dialog" role="alertdialog" aria-modal="true" aria-labelledby="confirm-title">
<div class="confirm-scrim" onKeyDown={(event) => event.key === "Escape" && props.onCancel()}>
<section
class="confirm-dialog"
role="alertdialog"
aria-modal="true"
aria-labelledby={title}
aria-describedby={props.message ? message : undefined}
>
<div class="confirm-body">
<div class="confirm-icon" aria-hidden="true">
<Icon name="warning" />
</div>
<div>
<h2 id="confirm-title">{props.title}</h2>
<Show when={props.message}>{(text) => <p>{text()}</p>}</Show>
<h2 id={title}>{props.title}</h2>
<Show when={props.message}>{(text) => <p id={message}>{text()}</p>}</Show>
</div>
</div>
<footer class="confirm-actions">
<Button variant="ghost" disabled={props.busy} onClick={props.onCancel}>
<Button variant="ghost" disabled={props.busy} onClick={props.onCancel} autofocus>
{props.cancel ?? "Cancel"}
</Button>
<Button variant="primary" disabled={props.busy} onClick={props.onConfirm}>
@@ -0,0 +1,73 @@
import { Button } from "@kilocode/kilo-web-ui/button"
import { Input } from "@kilocode/kilo-web-ui/input"
import { createUniqueId, Show } from "solid-js"
type Props = {
open: boolean
title: string
message?: string
label: string
value: string
placeholder?: string
confirm?: string
cancel?: string
busy?: boolean
onInput: (value: string) => void
onCancel: () => void
onConfirm: () => void
}
export function PromptDialog(props: Props) {
const id = createUniqueId()
const title = `${id}-title`
const message = `${id}-message`
const input = `${id}-input`
return (
<Show when={props.open}>
<div class="confirm-scrim" onKeyDown={(event) => event.key === "Escape" && props.onCancel()}>
<section
class="confirm-dialog prompt-dialog"
role="dialog"
aria-modal="true"
aria-labelledby={title}
aria-describedby={props.message ? message : undefined}
>
<form
class="prompt-form"
onSubmit={(event) => {
event.preventDefault()
props.onConfirm()
}}
>
<div class="prompt-body">
<div class="prompt-copy">
<h2 id={title}>{props.title}</h2>
<Show when={props.message}>{(text) => <p id={message}>{text()}</p>}</Show>
</div>
<label class="prompt-field" for={input}>
<span>{props.label}</span>
<Input
id={input}
value={props.value}
placeholder={props.placeholder}
disabled={props.busy}
autofocus
onInput={(event) => props.onInput(event.currentTarget.value)}
/>
</label>
</div>
<footer class="confirm-actions">
<Button type="button" variant="ghost" disabled={props.busy} onClick={props.onCancel}>
{props.cancel ?? "Cancel"}
</Button>
<Button type="submit" variant="primary" disabled={props.busy}>
{props.confirm ?? "Confirm"}
</Button>
</footer>
</form>
</section>
</div>
</Show>
)
}
@@ -30,13 +30,6 @@ function ConfigContent(props: { children?: JSX.Element }) {
</Card>
)}
</Show>
<Show when={ctx.saving()}>
{(item) => (
<Card class="banner" variant="info">
{item()}...
</Card>
)}
</Show>
<Show when={ctx.data.error}>
<Card class="banner" variant="error">
<strong>Dashboard request failed</strong>
@@ -32,6 +32,7 @@ export function ConfigSidebar() {
const href = (path: string) => `${path === "/" ? base() : `${base()}${path}`}${loc.search}`
const current = (path: string) => path === active() || (path !== "/" && active().startsWith(`${path}/`))
const group = (item: ConfigNode): item is ConfigGroup => "items" in item
const navigation = createMemo(() => configNav.filter((item) => !project() || !group(item) || !item.globalOnly))
return (
<aside class="config-sidebar" aria-label="Configuration sections">
@@ -42,7 +43,7 @@ export function ConfigSidebar() {
</span>
</div>
<nav class="config-options">
<For each={configNav}>
<For each={navigation()}>
{(item) => {
if (!group(item)) {
return (
@@ -0,0 +1,100 @@
import { Show } from "solid-js"
import { Button } from "@kilocode/kilo-web-ui/button"
import { Card } from "@kilocode/kilo-web-ui/card"
import { Spinner } from "@kilocode/kilo-web-ui/spinner"
import { CustomSelect, type SelectOption } from "../../components/CustomSelect"
import { ConfigPage } from "./ConfigPage"
import {
MAX_CONTEXT_SIDEBAR_WIDTH,
MIN_CONTEXT_SIDEBAR_WIDTH,
type ConsoleDiffStyle,
useConsoleUiSettings,
} from "./state/console"
const styles = [
{ value: "unified", label: "Unified" },
{ value: "split", label: "Split" },
] satisfies SelectOption<ConsoleDiffStyle>[]
export function ConsoleUiRoute() {
const state = useConsoleUiSettings()
return (
<ConfigPage
title="Console UI"
description="Configure the local Kilo Console interface. These preferences are saved in your user config."
actions={
<>
<Show when={state.configured()}>
<Button variant="secondary" disabled={Boolean(state.ctx.saving())} onClick={state.reset}>
Use default
</Button>
</Show>
<Button
variant="primary"
disabled={Boolean(state.ctx.saving()) || !state.dirty()}
aria-busy={Boolean(state.ctx.saving())}
onClick={state.save}
>
<Show when={state.ctx.saving()}>
<Spinner />
</Show>
Save
</Button>
</>
}
>
<div class="ui-settings">
<Card class="ui-card" padding={0}>
<header class="ui-card-header">
<div>
<h2>Project context sidebar</h2>
<p>Set the default width used by the Context and Changes panel in project consoles.</p>
</div>
</header>
<div class="ui-form">
<label class="ui-field">
<span>Sidebar width</span>
<input
type="number"
min={MIN_CONTEXT_SIDEBAR_WIDTH}
max={MAX_CONTEXT_SIDEBAR_WIDTH}
step="1"
value={state.width()}
onInput={(event) => state.setWidth(event.currentTarget.value)}
/>
<small>
Width in pixels, between {MIN_CONTEXT_SIDEBAR_WIDTH} and {MAX_CONTEXT_SIDEBAR_WIDTH}. You can also
resize the sidebar by dragging its left edge.
</small>
</label>
</div>
</Card>
<Card class="ui-card" padding={0}>
<header class="ui-card-header">
<div>
<h2>Diff review</h2>
<p>Choose the default layout used when reviewing changed files in project consoles.</p>
</div>
</header>
<div class="ui-form">
<div class="ui-field">
<span>Diff layout</span>
<CustomSelect
class="console-diff-select"
label="Diff layout"
value={state.style()}
options={styles}
disabled={Boolean(state.ctx.saving())}
onSelect={state.setStyle}
/>
<small>
Unified shows changes in one column. Split shows the original and modified file side by side.
</small>
</div>
</div>
</Card>
</div>
</ConfigPage>
)
}
@@ -3,6 +3,7 @@ import type { IconProps } from "@kilocode/kilo-web-ui/icon"
import { AgentBuilderRoute, AgentsRoute } from "./AgentsRoute"
import { CliNotificationsRoute } from "./CliNotificationsRoute"
import { CliUiRoute } from "./CliUiRoute"
import { ConsoleUiRoute } from "./ConsoleUiRoute"
import { FormattersRoute, LspRoute } from "./FormattersRoute"
import { IndexingRoute } from "./IndexingRoute"
import { KeybindsRoute } from "./KeybindsRoute"
@@ -26,6 +27,7 @@ export type ConfigSection = {
export type ConfigGroup = {
id: string
label: string
globalOnly?: boolean
items: ConfigSection[]
}
@@ -130,6 +132,14 @@ export const configNav: ConfigNode[] = [
},
],
},
{
id: "console",
label: "Console",
globalOnly: true,
items: [
{ path: "/console/ui", href: "/settings/console/ui", icon: "sliders", label: "UI", component: ConsoleUiRoute },
],
},
{
id: "advanced",
label: "Advanced",
@@ -0,0 +1,36 @@
import { describe, expect, test } from "bun:test"
import {
DEFAULT_CONTEXT_SIDEBAR_WIDTH,
MAX_CONTEXT_SIDEBAR_WIDTH,
MIN_CONTEXT_SIDEBAR_WIDTH,
normalizeConsoleDiffStyle,
normalizeContextSidebarWidth,
parseContextSidebarWidth,
} from "./console"
describe("console UI config state", () => {
test("defaults the diff layout to unified", () => {
expect(normalizeConsoleDiffStyle(undefined)).toBe("unified")
expect(normalizeConsoleDiffStyle("unified")).toBe("unified")
expect(normalizeConsoleDiffStyle("split")).toBe("split")
expect(normalizeConsoleDiffStyle("side-by-side")).toBe("unified")
})
test("normalizes missing and out-of-range widths", () => {
expect(normalizeContextSidebarWidth(undefined)).toBe(DEFAULT_CONTEXT_SIDEBAR_WIDTH)
expect(normalizeContextSidebarWidth(Number.NaN)).toBe(DEFAULT_CONTEXT_SIDEBAR_WIDTH)
expect(normalizeContextSidebarWidth(100)).toBe(MIN_CONTEXT_SIDEBAR_WIDTH)
expect(normalizeContextSidebarWidth(900)).toBe(MAX_CONTEXT_SIDEBAR_WIDTH)
expect(normalizeContextSidebarWidth(411.6)).toBe(412)
})
test("accepts only integer widths within the supported range", () => {
expect(parseContextSidebarWidth("352")).toBe(352)
expect(parseContextSidebarWidth(String(MIN_CONTEXT_SIDEBAR_WIDTH))).toBe(MIN_CONTEXT_SIDEBAR_WIDTH)
expect(parseContextSidebarWidth(String(MAX_CONTEXT_SIDEBAR_WIDTH))).toBe(MAX_CONTEXT_SIDEBAR_WIDTH)
expect(parseContextSidebarWidth("249")).toBeUndefined()
expect(parseContextSidebarWidth("801")).toBeUndefined()
expect(parseContextSidebarWidth("352.5")).toBeUndefined()
expect(parseContextSidebarWidth("wide")).toBeUndefined()
})
})
@@ -0,0 +1,80 @@
import { createEffect, createSignal } from "solid-js"
import { useConfig } from "../../../context/config"
export const DEFAULT_CONTEXT_SIDEBAR_WIDTH = 300
export const MIN_CONTEXT_SIDEBAR_WIDTH = 250
export const MAX_CONTEXT_SIDEBAR_WIDTH = 800
export const DEFAULT_CONSOLE_DIFF_STYLE = "unified" as const
export type ConsoleDiffStyle = "unified" | "split"
export function normalizeConsoleDiffStyle(value: unknown): ConsoleDiffStyle {
return value === "split" ? "split" : DEFAULT_CONSOLE_DIFF_STYLE
}
export function normalizeContextSidebarWidth(value: unknown) {
if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_CONTEXT_SIDEBAR_WIDTH
return Math.min(MAX_CONTEXT_SIDEBAR_WIDTH, Math.max(MIN_CONTEXT_SIDEBAR_WIDTH, Math.round(value)))
}
export function parseContextSidebarWidth(value: string) {
const width = Number(value)
if (!Number.isInteger(width)) return undefined
if (width < MIN_CONTEXT_SIDEBAR_WIDTH || width > MAX_CONTEXT_SIDEBAR_WIDTH) return undefined
return width
}
export function useConsoleUiSettings() {
const ctx = useConfig()
const [width, setWidth] = createSignal(String(DEFAULT_CONTEXT_SIDEBAR_WIDTH))
const [style, setStyle] = createSignal<ConsoleDiffStyle>(DEFAULT_CONSOLE_DIFF_STYLE)
const [dirty, setDirty] = createSignal(false)
createEffect(() => {
if (dirty()) return
const config = ctx.data()?.effective.console
setWidth(String(normalizeContextSidebarWidth(config?.context_sidebar_width)))
setStyle(normalizeConsoleDiffStyle(config?.diff_style))
})
function save() {
const value = parseContextSidebarWidth(width())
if (value === undefined) {
ctx.fail(`Enter a sidebar width between ${MIN_CONTEXT_SIDEBAR_WIDTH} and ${MAX_CONTEXT_SIDEBAR_WIDTH} pixels.`)
return
}
ctx.patch({ console: { context_sidebar_width: value, diff_style: style() } })
setWidth(String(value))
setDirty(false)
}
function reset() {
ctx.unset([
["console", "context_sidebar_width"],
["console", "diff_style"],
])
setWidth(String(DEFAULT_CONTEXT_SIDEBAR_WIDTH))
setStyle(DEFAULT_CONSOLE_DIFF_STYLE)
setDirty(false)
}
return {
ctx,
width,
setWidth: (value: string) => {
setWidth(value)
setDirty(true)
},
style,
setStyle: (value: string) => {
setStyle(normalizeConsoleDiffStyle(value))
setDirty(true)
},
dirty,
configured: () => {
const config = ctx.data()?.effective.console
return config?.context_sidebar_width !== undefined || config?.diff_style !== undefined
},
save,
reset,
}
}
@@ -1,8 +1,17 @@
import { A, useLocation, useParams } from "@solidjs/router"
import { createEffect, createMemo, createResource, createSignal, For, onCleanup, Show } from "solid-js"
import { createEffect, createMemo, createResource, createSignal, For, on, onCleanup, Show } from "solid-js"
import { Badge } from "@kilocode/kilo-web-ui/badge"
import { Button } from "@kilocode/kilo-web-ui/button"
import { Card } from "@kilocode/kilo-web-ui/card"
import { Icon } from "@kilocode/kilo-web-ui/icon"
import { ResizeHandle } from "@kilocode/kilo-web-ui/resize-handle"
import { Spinner } from "@kilocode/kilo-web-ui/spinner"
import { File } from "@opencode-ai/ui/file"
import { FileComponentProvider } from "@opencode-ai/ui/context/file"
import { SessionReview, type SessionReviewDiffStyle } from "@opencode-ai/ui/session-review"
import { ConfirmDialog } from "../../components/ConfirmDialog"
import { LoadingScreen } from "../../components/LoadingScreen"
import { PromptDialog } from "../../components/PromptDialog"
import {
createProjectPty,
createProjectWorktree,
@@ -12,6 +21,7 @@ import {
loadProjectConsole,
loadProjectDiff,
loadProjectDiffFile,
patchConfig,
removeProjectPty,
removeProjectWorktree,
resetProjectWorktree,
@@ -21,6 +31,7 @@ import {
viewProjectSessions,
type ProjectConsoleEvent,
type ProjectConsoleQuery,
type ProjectDiffItem,
type ProjectTerminalItem,
type Query,
} from "../../client"
@@ -30,6 +41,14 @@ import {
clearUnread as storeClearUnread,
sessionHasUnread,
} from "../../shared/terminal-status"
import {
DEFAULT_CONSOLE_DIFF_STYLE,
DEFAULT_CONTEXT_SIDEBAR_WIDTH,
MAX_CONTEXT_SIDEBAR_WIDTH,
MIN_CONTEXT_SIDEBAR_WIDTH,
normalizeConsoleDiffStyle,
normalizeContextSidebarWidth,
} from "../config/state/console"
import { GhosttyTerminal } from "./terminal/GhosttyTerminal"
const ui = new Set(["3017", "3018"])
@@ -41,6 +60,13 @@ type Context = {
kind: "local" | "worktree"
}
type Editor = { kind: "create"; value: string } | { kind: "rename"; item: Context; value: string }
type Pending = {
kind: "delete" | "reset"
item: Context
}
function discoverable(search: URLSearchParams) {
if (search.get("server")) return false
return ui.has(window.location.port)
@@ -125,13 +151,21 @@ export function ProjectConsoleRoute() {
const [selected, setSelected] = createSignal(window.localStorage.getItem(`kilo.console.${params.project}.dir`) ?? "")
const [active, setActive] = createSignal(window.localStorage.getItem(`kilo.console.${params.project}.pty`) ?? "")
const [local, setLocal] = createSignal<ProjectTerminalItem[]>([])
const [file, setFile] = createSignal<string | undefined>()
const [openFiles, setOpenFiles] = createSignal<string[]>([])
const [details, setDetails] = createSignal<Record<string, ProjectDiffItem>>({})
const [infoWidth, setInfoWidth] = createSignal(DEFAULT_CONTEXT_SIDEBAR_WIDTH)
const [viewport, setViewport] = createSignal(window.innerWidth)
const [diffStyle, setDiffStyle] = createSignal<SessionReviewDiffStyle>(DEFAULT_CONSOLE_DIFF_STYLE)
const [saving, setSaving] = createSignal<string | undefined>()
const [failure, setFailure] = createSignal<string | undefined>()
const [unread, setUnread] = createSignal(new Set<string>())
const [closing, setClosing] = createSignal(new Set<string>())
const [labelRev, setLabelRev] = createSignal(0)
const [editor, setEditor] = createSignal<Editor | undefined>()
const [pending, setPending] = createSignal<Pending | undefined>()
const events = { timer: undefined as number | undefined }
const resize = { timer: undefined as number | undefined, pending: false }
const detailPending = new Set<string>()
const project = () => params.project ?? ""
const query = createMemo<ProjectConsoleQuery | undefined>(() => {
const target = clean(url()) || fallback()
@@ -139,6 +173,7 @@ export function ProjectConsoleRoute() {
return { url: target, dir: "", project: project() }
})
const [snap, { refetch }] = createResource(query, loadProjectConsole)
const visibleInfoWidth = createMemo(() => Math.min(infoWidth(), maxInfoWidth()))
const contexts = createMemo<Context[]>(() => {
const data = snap()
@@ -185,11 +220,13 @@ export function ProjectConsoleRoute() {
return { input: item, dir: item.dir }
})
const [diffs] = createResource(diffKey, (item) => loadProjectDiff(item.input, item.dir))
const detailKey = createMemo(() => {
const item = target()
const path = file()
if (!item || !path) return undefined
return { input: item, dir: item.dir, file: path }
// Diff summary items carry no content (patch/before/after empty); overlay full file
// diffs as they load so the review renders syntax-highlighted changes per file.
const reviewDiffs = createMemo<ProjectDiffItem[]>(() => {
const map = details()
return (diffs() ?? [])
.filter((item): item is ProjectDiffItem & { file: string } => typeof item.file === "string")
.map((item) => map[item.file] ?? item)
})
const terminal = createMemo(() => {
const item = activeTerminal()
@@ -205,7 +242,6 @@ export function ProjectConsoleRoute() {
return items
})
const terminalKeys = createMemo(() => Array.from(terminalMap().keys()))
const [detail] = createResource(detailKey, (item) => loadProjectDiffFile(item.input, item.dir, item.file))
const settings = createMemo(() => {
const q = search().toString()
return `/projects/${encodeURIComponent(project())}/settings${q ? `?${q}` : ""}`
@@ -300,18 +336,67 @@ export function ProjectConsoleRoute() {
setSelected(item.dir)
const pty = terminalsFor(item.dir)[0]
setActive(pty?.id ?? "")
setFile(undefined)
remember(item.dir, pty?.id)
}
function selectTerminal(item: ProjectTerminalItem) {
setSelected(item.directory)
setActive(item.id)
setFile(undefined)
clearUnread(item)
remember(item.directory, item.id)
}
function fetchDetail(path: string) {
const base = target()
if (!base) return
if (details()[path] || detailPending.has(path)) return
detailPending.add(path)
void loadProjectDiffFile(base, base.dir, path)
.then((item) => {
if (item && item.file) setDetails((prev) => ({ ...prev, [item.file!]: item }))
})
.catch((err) => console.warn("Worktree diff file:", err))
.finally(() => detailPending.delete(path))
}
function openReviewFiles(next: string[]) {
setOpenFiles(next)
for (const path of next) fetchDetail(path)
}
function changeDiffStyle(style: SessionReviewDiffStyle) {
setDiffStyle(style)
const base = query()
if (!base) return
void patchConfig({ url: base.url, dir: "", scope: "global" }, { console: { diff_style: style } }).catch((err) =>
console.warn(`Console diff style: ${errMsg(err)}`),
)
}
function resizeInfo(value: number) {
const width = normalizeContextSidebarWidth(value)
setInfoWidth(width)
resize.pending = true
if (resize.timer) window.clearTimeout(resize.timer)
resize.timer = window.setTimeout(() => {
resize.timer = undefined
const base = query()
if (!base) {
resize.pending = false
return
}
void patchConfig({ url: base.url, dir: "", scope: "global" }, { console: { context_sidebar_width: width } })
.catch((err) => console.warn(`Console sidebar width: ${errMsg(err)}`))
.finally(() => {
resize.pending = false
})
}, 350)
}
function maxInfoWidth() {
return Math.max(MIN_CONTEXT_SIDEBAR_WIDTH, Math.min(MAX_CONTEXT_SIDEBAR_WIDTH, viewport() - 604))
}
function run(label: string, job: () => Promise<unknown>) {
setSaving(label)
setFailure(undefined)
@@ -322,10 +407,26 @@ export function ProjectConsoleRoute() {
}
function addWorktree() {
if (!projectInput()) return
setEditor({ kind: "create", value: "" })
}
function submitEditor() {
const state = editor()
if (!state) return
if (state.kind === "rename") {
const value = state.value.trim()
if (value) window.localStorage.setItem(labelKey(state.item.dir), value)
if (!value) window.localStorage.removeItem(labelKey(state.item.dir))
setLabelRev((revision) => revision + 1)
setEditor(undefined)
return
}
const input = projectInput()
const data = snap()
if (!input || !data) return
const name = window.prompt("Worktree name") ?? undefined
if (!input) return
const name = state.value.trim() || undefined
setEditor(undefined)
run("Creating worktree", async () => {
const next = await createProjectWorktree(input, name)
setSelected(next.directory)
@@ -395,27 +496,12 @@ export function ProjectConsoleRoute() {
function renameWorktree(item: Context) {
if (item.kind === "local") return
const input = window.prompt("Worktree label", displayLabel(item))
if (input === null) return
const next = input.trim()
if (next) window.localStorage.setItem(labelKey(item.dir), next)
else window.localStorage.removeItem(labelKey(item.dir))
setLabelRev((value) => value + 1)
setEditor({ kind: "rename", item, value: displayLabel(item) })
}
function removeWorktree(item: Context) {
const input = projectInput()
if (!input || item.kind === "local") return
if (!window.confirm(`Remove worktree ${displayLabel(item)}?`)) return
run("Removing worktree", async () => {
await removeProjectWorktree(input, item.dir)
window.localStorage.removeItem(labelKey(item.dir))
setLabelRev((value) => value + 1)
if (selected() === item.dir) {
setSelected(input.dir)
remember(input.dir)
}
})
if (!projectInput() || item.kind === "local") return
setPending({ kind: "delete", item })
}
function removeSelected() {
@@ -425,18 +511,88 @@ export function ProjectConsoleRoute() {
}
function resetSelected() {
const input = projectInput()
const item = current()
if (!input || !item || item.kind === "local") return
if (!window.confirm(`Reset worktree ${displayLabel(item)}?`)) return
run("Resetting worktree", async () => resetProjectWorktree(input, item.dir))
if (!projectInput() || !item || item.kind === "local") return
setPending({ kind: "reset", item })
}
function confirmWorktree() {
const state = pending()
const input = projectInput()
if (!state || !input) return
setPending(undefined)
if (state.kind === "reset") {
run("Resetting worktree", async () => resetProjectWorktree(input, state.item.dir))
return
}
run("Removing worktree", async () => {
await removeProjectWorktree(input, state.item.dir)
window.localStorage.removeItem(labelKey(state.item.dir))
setLabelRev((revision) => revision + 1)
if (selected() === state.item.dir) {
setSelected(input.dir)
remember(input.dir)
}
})
}
function updateEditor(value: string) {
setEditor((state) => {
if (!state) return state
return { ...state, value }
})
}
function editorTitle() {
const state = editor()
if (!state || state.kind === "create") return "Create worktree"
return `Rename ${displayLabel(state.item)}`
}
function editorMessage() {
if (editor()?.kind === "rename") return "Leave the name blank to restore the generated worktree name."
return "Choose a recognizable name, or leave it blank to generate one automatically."
}
function pendingTitle() {
const state = pending()
if (!state) return ""
const action = state.kind === "reset" ? "Reset" : "Delete"
return `${action} worktree ${displayLabel(state.item)}?`
}
function pendingMessage() {
if (pending()?.kind === "reset") return "This discards all uncommitted changes in the worktree."
return "This removes the worktree directory and its files from the project."
}
createEffect(() => {
const data = snap()
if (!data) return
if (!resize.pending) setInfoWidth(normalizeContextSidebarWidth(data.config.console?.context_sidebar_width))
setDiffStyle(normalizeConsoleDiffStyle(data.config.console?.diff_style))
})
createEffect(() => {
const next = search().get("server")
if (next && next !== url()) setUrl(next)
})
// Reset review state when the selected worktree changes so one worktree's
// file contents never leak into another.
createEffect(
on(
() => target()?.dir,
() => {
setOpenFiles([])
setDetails({})
detailPending.clear()
},
{ defer: true },
),
)
createEffect(() => {
if (!discoverable(search())) return
void resolveServer().then((value) => {
@@ -507,8 +663,13 @@ export function ProjectConsoleRoute() {
onCleanup(stop)
})
const updateViewport = () => setViewport(window.innerWidth)
window.addEventListener("resize", updateViewport)
onCleanup(() => {
if (events.timer) window.clearTimeout(events.timer)
if (resize.timer) window.clearTimeout(resize.timer)
window.removeEventListener("resize", updateViewport)
})
createEffect(() => {
@@ -525,7 +686,7 @@ export function ProjectConsoleRoute() {
})
return (
<section class="project-console">
<section class="project-console" style={`--project-info-width: ${visibleInfoWidth()}px`}>
<aside class="project-console-sidebar" aria-label="Project console sections">
<div class="project-console-title">
<span class="project-console-heading">
@@ -734,52 +895,87 @@ export function ProjectConsoleRoute() {
</main>
<aside class="project-console-info" aria-label="Project details">
<div class="project-info-card">
<div class="project-panel-heading">Context</div>
<strong>{currentLabel()}</strong>
<code>{current()?.dir ?? snap()?.project.worktree ?? project()}</code>
<ResizeHandle
direction="horizontal"
edge="start"
size={visibleInfoWidth()}
min={MIN_CONTEXT_SIDEBAR_WIDTH}
max={maxInfoWidth()}
aria-label="Resize project context sidebar"
onResize={resizeInfo}
/>
<div class="project-info-card project-info-context">
<div class="project-info-context-head">
<span class="project-panel-heading">Context</span>
<Badge variant="outline">{current()?.kind === "worktree" ? "Worktree" : "Local"}</Badge>
</div>
<strong class="project-info-title">{currentLabel()}</strong>
<code class="project-info-path" title={current()?.dir}>
{current()?.dir ?? snap()?.project.worktree ?? project()}
</code>
<Show when={current()?.kind === "worktree"}>
<div class="project-info-actions">
<button type="button" onClick={resetSelected} disabled={!!saving()}>
<Button variant="secondary" size="small" onClick={resetSelected} disabled={!!saving()}>
Reset
</button>
<button type="button" onClick={removeSelected} disabled={!!saving()}>
Remove
</button>
</Button>
<Button variant="destructive" size="small" onClick={removeSelected} disabled={!!saving()}>
Delete
</Button>
</div>
</Show>
</div>
<div class="project-info-card grow">
<div class="project-panel-heading">Changes</div>
<Show when={diffs.loading && !diffs()}>
<p class="empty">Loading diff...</p>
<div class="project-info-review">
<Show
when={!diffs.error}
fallback={<div class="project-review-state project-review-state-error">{errMsg(diffs.error)}</div>}
>
<Show
when={!(diffs.loading && !diffs())}
fallback={
<div class="project-review-state">
<Spinner />
<span>Loading changes</span>
</div>
}
>
<FileComponentProvider component={File}>
<SessionReview
diffs={reviewDiffs()}
title={<span>Changes</span>}
diffStyle={diffStyle()}
onDiffStyleChange={changeDiffStyle}
open={openFiles()}
onOpenChange={openReviewFiles}
empty={<div class="project-review-empty">No changes detected.</div>}
/>
</FileComponentProvider>
</Show>
</Show>
<Show when={diffs.error}>
<p class="empty">{errMsg(diffs.error)}</p>
</Show>
<Show when={!diffs.loading && (diffs() ?? []).length === 0 && !diffs.error}>
<p class="empty">No changes detected.</p>
</Show>
<div class="project-diff-list">
<For each={diffs() ?? []}>
{(item) => (
<button
type="button"
class="project-diff-row"
classList={{ active: file() === item.file }}
onClick={() => setFile(item.file)}
>
<span>{item.file}</span>
<small>
+{item.additions} -{item.deletions}
</small>
</button>
)}
</For>
</div>
<Show when={detail()}>{(item) => <pre class="project-diff-detail">{item()?.patch ?? ""}</pre>}</Show>
</div>
</aside>
<PromptDialog
open={Boolean(editor())}
title={editorTitle()}
message={editorMessage()}
label="Worktree name"
value={editor()?.value ?? ""}
placeholder="feature-name"
confirm={editor()?.kind === "rename" ? "Save" : "Create"}
busy={Boolean(saving())}
onInput={updateEditor}
onCancel={() => setEditor(undefined)}
onConfirm={submitEditor}
/>
<ConfirmDialog
open={Boolean(pending())}
title={pendingTitle()}
message={pendingMessage()}
confirm={pending()?.kind === "reset" ? "Reset" : "Delete"}
busy={Boolean(saving())}
onCancel={() => setPending(undefined)}
onConfirm={confirmWorktree}
/>
</section>
)
}
@@ -8,6 +8,20 @@
background: var(--card);
}
.kilo-console .ui-card:has(.console-diff-select[open]) {
position: relative;
z-index: 40;
overflow: visible;
}
.kilo-console .console-diff-select[open] {
z-index: 41;
}
.kilo-console .console-diff-select .models-select-menu {
z-index: 42;
}
.kilo-console .ui-card-header,
.kilo-console .ui-card-footer {
display: flex;
@@ -1,6 +1,8 @@
.kilo-console .project-console {
display: grid;
grid-template-columns: 13.75rem minmax(24rem, 1fr) minmax(16rem, 22rem);
grid-template-columns:
13.75rem minmax(24rem, 1fr)
clamp(250px, var(--project-info-width, 300px), min(800px, calc(100vw - 37.75rem)));
height: 100%;
min-height: 0;
overflow: hidden;
@@ -280,8 +282,7 @@
.kilo-console .project-context span,
.kilo-console .project-terminal-row span,
.kilo-console .project-settings-link span,
.kilo-console .project-diff-row span {
.kilo-console .project-settings-link span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@@ -340,14 +341,6 @@
color: var(--foreground) !important;
}
.kilo-console .project-diff-row small {
overflow: hidden;
color: var(--muted-foreground);
font-size: 0.6875rem;
text-overflow: ellipsis;
white-space: nowrap;
}
.kilo-console .project-sidebar-bottom {
flex: 0 0 auto;
border-top: 1px solid var(--border);
@@ -460,27 +453,40 @@
display: flex;
flex-direction: column;
gap: 0.75rem;
position: relative;
overflow: hidden;
border-left: 1px solid var(--border);
background: var(--background);
padding: clamp(1rem, 2.4vw, 2rem) clamp(0.875rem, 1.8vw, 1.25rem);
padding: 0.875rem;
}
.kilo-console .project-console-info > [data-component="resize-handle"]::after {
background: var(--ring);
}
.kilo-console .project-info-card {
display: grid;
gap: 0.625rem;
min-height: 0;
gap: 0.5rem;
flex: 0 0 auto;
border: 1px solid var(--border);
border-radius: var(--radius-md);
background: color-mix(in oklab, var(--muted) 30%, transparent);
background: var(--card);
padding: 0.75rem;
}
.kilo-console .project-info-card.grow {
overflow: auto;
.kilo-console .project-info-context-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.5rem;
}
.kilo-console .project-info-card strong {
.kilo-console .project-info-context-head .project-panel-heading {
min-height: 0;
padding: 0;
}
.kilo-console .project-info-title {
overflow: hidden;
color: var(--foreground);
font-size: 0.875rem;
@@ -489,7 +495,7 @@
white-space: nowrap;
}
.kilo-console .project-info-card code {
.kilo-console .project-info-path {
overflow: hidden;
color: var(--muted-foreground);
font-family: var(--font-family-mono, monospace);
@@ -502,64 +508,81 @@
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0.5rem;
margin-top: 0.125rem;
}
.kilo-console .project-info-actions button,
.kilo-console .project-diff-row {
.kilo-console .project-info-actions [data-component="button"] {
width: 100%;
}
/* Rich review panel — fills remaining height; SessionReview scrolls internally. */
.kilo-console .project-info-review {
display: flex;
flex: 1 1 auto;
flex-direction: column;
min-height: 0;
overflow: hidden;
border: 1px solid var(--border);
border-radius: var(--radius-md);
background: var(--input-base);
color: var(--foreground);
cursor: pointer;
font: inherit;
padding: 0.5rem 0.625rem;
text-align: left;
background: var(--card);
}
.kilo-console .project-info-actions button:hover,
.kilo-console .project-info-actions button:focus-visible,
.kilo-console .project-diff-row:hover,
.kilo-console .project-diff-row:focus-visible,
.kilo-console .project-diff-row.active {
border-color: var(--ring);
outline: 0;
.kilo-console .project-info-review [data-component="session-review"] {
display: flex;
flex: 1 1 auto;
flex-direction: column;
min-height: 0;
height: 100%;
}
.kilo-console .project-info-actions button:disabled {
cursor: not-allowed;
opacity: 0.55;
}
.kilo-console .project-diff-list {
display: grid;
gap: 0.375rem;
}
.kilo-console .project-diff-row {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
gap: 0.125rem;
}
.kilo-console .project-diff-row.active {
background: color-mix(in oklab, var(--muted) 55%, transparent);
}
.kilo-console .project-diff-detail {
overflow: auto;
max-height: 18rem;
margin: 0.5rem 0 0;
border: 1px solid var(--border);
border-radius: var(--radius-md);
background: var(--background-base);
color: var(--foreground);
font-family: var(--font-family-mono, monospace);
font-size: 0.6875rem;
line-height: 1.4;
.kilo-console .project-info-review [data-slot="session-review-header"] {
box-sizing: border-box;
height: auto;
min-height: 2.5rem;
padding: 0.75rem;
}
.kilo-console .project-info-review [data-slot="session-review-title"] {
display: flex;
align-items: center;
justify-content: flex-start;
min-height: 1.25rem;
color: var(--text-weaker);
font-family: var(--font-family-sans);
font-size: 0.625rem;
font-weight: 500;
line-height: normal;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.kilo-console .project-info-review [data-slot="session-review-actions"] > [data-component="button"] {
width: auto;
min-width: 0;
height: 1.5rem;
gap: 0.25rem;
padding: 0 0.375rem;
font-size: 0.6875rem;
}
.kilo-console .project-review-state,
.kilo-console .project-review-empty {
display: flex;
flex: 1 1 auto;
align-items: center;
justify-content: center;
gap: 0.5rem;
min-height: 0;
color: var(--muted-foreground);
font-size: 0.75rem;
text-align: center;
padding: 1rem;
}
.kilo-console .project-review-state-error {
color: var(--destructive, #ef4444);
}
@media (max-width: 1180px) {
.kilo-console .project-console {
grid-template-columns: 13.75rem minmax(0, 1fr);
+32 -5
View File
@@ -408,7 +408,7 @@
}
.kilo-console .confirm-dialog {
width: min(48rem, 100%);
width: min(28rem, 100%);
overflow: hidden;
border: 1px solid var(--border-weak-base);
border-radius: var(--radius-lg);
@@ -435,16 +435,21 @@
height: 3rem;
}
.kilo-console .confirm-body h2 {
.kilo-console .confirm-body h2,
.kilo-console .prompt-copy h2 {
margin: 0;
color: var(--text-strong);
font-size: clamp(1.15rem, 2vw, 1.35rem);
line-height: 1.35;
font-size: 0.875rem;
font-weight: 500;
line-height: 1.25rem;
}
.kilo-console .confirm-body p {
.kilo-console .confirm-body p,
.kilo-console .prompt-copy p {
margin: 0.5rem 0 0;
color: var(--text-weak);
font-size: 0.75rem;
line-height: 1.625;
}
.kilo-console .confirm-actions {
@@ -455,3 +460,25 @@
background: color-mix(in srgb, var(--background-base) 65%, transparent);
padding: 1rem;
}
.kilo-console .prompt-form {
margin: 0;
}
.kilo-console .prompt-body {
display: grid;
gap: 1rem;
padding: 1.5rem;
}
.kilo-console .prompt-field {
display: grid;
gap: 0.5rem;
color: var(--text-strong);
font-size: 0.75rem;
font-weight: 500;
}
.kilo-console .prompt-field input {
height: 2rem;
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@kilocode/kilo-docs",
"version": "7.3.42",
"version": "7.3.45",
"private": true,
"scripts": {
"dev": "next dev --webpack --port 3002",
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fc50e7ce14bc197134d5fdf38ac92087940906f7b627e2507c082a5be0527d31
size 17378
oid sha256:129ea4c06998d8418bdb8edc8e61e094849835edb3f40cf6e1ddc34568168e61
size 52396
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3838b7760984c6c822078de1bdb74067839775d29ab8a0435626d0afff8cbb30
size 24826
oid sha256:1bd31cd4e01c2e2a5ed579bce1b542368e7a11fc878d375a93fb238ba4f82469
size 53288
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f5e9dd9a8b232e960fbc1925a2447a7634b2db241f6fe9b9ba8527665a5ca4c4
size 14248
oid sha256:47b7b7bfb4a6b5631d8fdb808c1b3a9eb631b6f96911ddf15880f0c2fee4f7e0
size 38558
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fbaee116f80051c31aa2b17eacca856c173c843b66c015ad8129aba83b4d29ad
size 27563
oid sha256:16c736bff5fba46d9e7d88e852ad70f96e37ad000280b03cb393da9c970e2ae9
size 1415
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fbaee116f80051c31aa2b17eacca856c173c843b66c015ad8129aba83b4d29ad
size 27563
oid sha256:288e4d74c5e74773cea45e3b7cb428903ace337f9d27a3506c9ba193fe291b86
size 3041
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2ae8197b9d63cf977fa5112a1b02eb48bc22a1577b4dee15abd475805f0db5b0
size 11769
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b70543ee69296913543f8de0f1b1d4787fb6be8a9e410423073b0a84a6da6305
size 21290
oid sha256:fe0ed476823eb03bc50fec4e9cdb656552aab3b07ee6ed3c30265ed41f0bfda0
size 21344
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:001db2cec148c97a321076e45ec4478beced1bd18f0f4347927419d7194d6a43
size 31986
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e3f86dabc39700b3a86508c1e6499a546eeabf720832b62c9ce12f2d97378ebd
size 34649
+2
View File
@@ -61,6 +61,8 @@
<!-- packages/opencode/src/provider/transform.ts -->
- <https://git-scm.com>
<!-- packages/kilo-vscode/src/agent-manager/WorktreeManager.ts -->
- <https://github.com>
<!-- packages/opencode/src/kilocode/security/github.ts -->
- <https://github.com/apps/kiloconnect>
<!-- packages/opencode/src/cli/cmd/github.ts -->
- <https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/utils/overflow.ts>
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@kilocode/kilo-gateway",
"version": "7.3.42",
"version": "7.3.45",
"type": "module",
"license": "MIT",
"description": "Unified Kilo Gateway package for OpenCode - authentication, provider, and API integration",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@kilocode/kilo-i18n",
"version": "7.3.42",
"version": "7.3.45",
"type": "module",
"license": "MIT",
"description": "Kilo-specific i18n translations and overrides",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@kilocode/kilo-indexing",
"version": "7.3.42",
"version": "7.3.45",
"type": "module",
"license": "MIT",
"description": "Standalone indexing engine and host helpers for Kilo Code",
+6
View File
@@ -1,5 +1,11 @@
# Changelog
## 7.4.0
### Minor Changes
- [#11165](https://github.com/Kilo-Org/kilocode/pull/11165) [`bf67155`](https://github.com/Kilo-Org/kilocode/commit/bf6715594bae4a1160abb7cfdfdedaba4b8358ec) - Enhance draft prompts from the JetBrains chat composer using the configured small model.
## 7.3.42
### Patch Changes
@@ -19,10 +19,18 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import okhttp3.Call
import okhttp3.Callback
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import okhttp3.RequestBody.Companion.toRequestBody
import java.io.IOException
import java.util.concurrent.TimeUnit
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
/**
* Chat orchestrator that handles message sending, history loading,
@@ -39,6 +47,7 @@ class KiloBackendChatManager(
) {
companion object {
private val JSON_TYPE = "application/json".toMediaType()
private const val ENHANCE_TIMEOUT_MINUTES = 2L
private val CHAT_EVENTS = setOf(
"message.updated",
@@ -107,6 +116,28 @@ class KiloBackendChatManager(
// ------ prompt ------
suspend fun enhancePrompt(dir: String, text: String): String {
val http = requireClient()
val url = requireBase()
val body = KiloCliDataParser.buildEnhancePromptJson(text)
val request = Request.Builder()
.url("$url/enhance-prompt?directory=${encode(dir)}")
.post(body.toRequestBody(JSON_TYPE))
.build()
val call = http.newCall(request)
call.timeout().timeout(ENHANCE_TIMEOUT_MINUTES, TimeUnit.MINUTES)
return call.await().use { response ->
val raw = response.body?.string().orEmpty()
if (!response.isSuccessful) {
log.warn("enhance prompt failed: HTTP ${response.code}")
raw.takeIf { it.isNotBlank() }?.let { log.debug { "kind=enhancePrompt error=${ChatLogSummary.body(it)}" } }
throw RuntimeException("Enhance prompt failed: HTTP ${response.code}")
}
KiloCliDataParser.parseEnhancedPrompt(raw)
}
}
fun prompt(id: String, dir: String, prompt: PromptDto) {
val meta = if (log.isDebugEnabled) {
"${ChatLogSummary.dir(dir)} ${ChatLogSummary.prompt(prompt)}"
@@ -324,6 +355,19 @@ class KiloBackendChatManager(
private fun requireBase(): String =
base ?: throw IllegalStateException("Chat manager not started")
private suspend fun Call.await(): Response = suspendCancellableCoroutine { cont ->
cont.invokeOnCancellation { cancel() }
enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
if (!cont.isCancelled) cont.resumeWithException(e)
}
override fun onResponse(call: Call, response: Response) {
cont.resume(response) { _, value, _ -> value.close() }
}
})
}
private fun encode(value: String): String =
java.net.URLEncoder.encode(value, "UTF-8")
}
@@ -365,6 +365,13 @@ object KiloCliDataParser {
// JSON serialization (DTO → JSON for outgoing requests)
// ================================================================
fun parseEnhancedPrompt(raw: String): String =
tryParseObject(raw)?.str("text")
?: throw IllegalArgumentException("Enhance prompt response is missing text")
fun buildEnhancePromptJson(text: String): String =
"""{"text":${escape(text)}}"""
/**
* Build the JSON body for `POST /session/{id}/prompt_async`.
*/
@@ -100,6 +100,9 @@ class KiloSessionRpcApiImpl : KiloSessionRpcApi {
// ------ chat ------
override suspend fun enhancePrompt(directory: String, text: String): String =
ready { chat.enhancePrompt(directory, text) }
override suspend fun prompt(id: String, directory: String, prompt: PromptDto) {
app.requireReady()
LOG.info("prompt RPC: session=$id, dir=$directory, parts=${prompt.parts.size}")
@@ -6,12 +6,17 @@ import ai.kilocode.rpc.dto.ModelSelectionDto
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
import kotlinx.coroutines.cancel
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.runBlocking
import okhttp3.OkHttpClient
import java.util.concurrent.CountDownLatch
import kotlin.test.AfterTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
@@ -39,4 +44,50 @@ class KiloBackendChatManagerTest {
assertTrue(mock.lastSummarizePath!!.startsWith("/session/ses_abc/summarize?directory="))
assertEquals("""{"providerID":"anthropic","modelID":"claude-4"}""", mock.lastSummarizeBody)
}
@Test
fun `enhance prompt posts scoped request and returns rewritten text`() = runBlocking {
val port = mock.start()
val chat = KiloBackendChatManager(scope, TestLog())
chat.start(OkHttpClient(), port, MutableSharedFlow())
mock.enhanced = """{"text":"Use a focused implementation plan"}"""
val result = chat.enhancePrompt("/test/project", "make a plan")
assertEquals("Use a focused implementation plan", result)
assertEquals(1, mock.requestCount("/enhance-prompt"))
assertTrue(mock.lastEnhancePath!!.startsWith("/enhance-prompt?directory="))
assertEquals("""{"text":"make a plan"}""", mock.lastEnhanceBody)
}
@Test
fun `enhance prompt hides provider response details`() = runBlocking {
val port = mock.start()
val chat = KiloBackendChatManager(scope, TestLog())
chat.start(OkHttpClient(), port, MutableSharedFlow())
mock.enhanceStatus = 500
mock.enhanced = """{"error":"provider unavailable"}"""
val error = assertFailsWith<RuntimeException> {
chat.enhancePrompt("/test/project", "make a plan")
}
assertEquals("Enhance prompt failed: HTTP 500", error.message)
}
@Test
fun `enhance prompt cancels the HTTP request with its coroutine`() = runBlocking {
val port = mock.start()
val chat = KiloBackendChatManager(scope, TestLog())
chat.start(OkHttpClient(), port, MutableSharedFlow())
val gate = CountDownLatch(1)
mock.responseGate = gate
val request = async(Dispatchers.Default) { chat.enhancePrompt("/test/project", "make a plan") }
assertTrue(mock.awaitRequestCount("/enhance-prompt", 1))
request.cancelAndJoin()
gate.countDown()
assertTrue(request.isCancelled)
}
}
@@ -82,6 +82,10 @@ class MockCliServer : AutoCloseable {
@Volatile var summarizeStatus = 200
@Volatile var lastSummarizePath: String? = null
@Volatile var lastSummarizeBody: String? = null
@Volatile var enhanced = """{"text":"Enhanced prompt"}"""
@Volatile var enhanceStatus = 200
@Volatile var lastEnhancePath: String? = null
@Volatile var lastEnhanceBody: String? = null
@Volatile var sessionRenameStatus = 200
@Volatile var sessionRenameResponse = """{"id":"ses_test","slug":"test","projectID":"prj_test","directory":"/test","title":"Renamed","version":"1.0.0","time":{"created":1000,"updated":2000}}"""
@Volatile var lastSessionRenamePath: String? = null
@@ -325,6 +329,11 @@ class MockCliServer : AutoCloseable {
lastSummarizeBody = body
respond(output, summarizeStatus, summarizeResponse)
}
bare == "/enhance-prompt" && method == "POST" -> {
lastEnhancePath = path
lastEnhanceBody = body
respond(output, enhanceStatus, enhanced)
}
else -> respond(output, 404, """{"error":"Not found"}""")
}
} catch (_: SocketException) {
@@ -3,6 +3,7 @@ package ai.kilocode.client
import com.intellij.notification.Notification
import com.intellij.notification.NotificationGroupManager
import com.intellij.notification.NotificationType
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ProjectManager
object KiloNotifications {
@@ -10,6 +11,10 @@ object KiloNotifications {
fun error(title: String, content: String? = null) {
val project = ProjectManager.getInstance().openProjects.firstOrNull { !it.isDefault }
error(project, title, content)
}
fun error(project: Project?, title: String, content: String? = null) {
val notification = NotificationGroupManager.getInstance()
.getNotificationGroup(GROUP)
?.createNotification(title, content ?: "", NotificationType.ERROR)
@@ -155,6 +155,9 @@ class KiloSessionService internal constructor(
// ------ Chat ops (explicit session ID) ------
suspend fun enhancePrompt(dir: String, text: String): String =
call { enhancePrompt(dir, text) }
/** Send a prompt to a session. */
suspend fun prompt(id: String, dir: String, dto: PromptDto) {
val meta = if (LOG.isDebugEnabled) {
@@ -308,6 +308,7 @@ class SessionUi(
project = project,
onSend = { text -> sendPrompt(text) },
onAbort = { controller.abort() },
onEnhance = controller::enhancePrompt,
)
sessionContent.add(header, BorderLayout.NORTH)
@@ -51,6 +51,7 @@ import ai.kilocode.log.ChatLogSummary
import ai.kilocode.log.KiloLog
import com.intellij.openapi.util.Disposer
import com.intellij.util.concurrency.annotations.RequiresEdt
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
@@ -124,6 +125,8 @@ class SessionController(
) { sid ?: ref?.key ?: "pending" }
private var disposed = false
private var enhancement = 0L
private val enhancements = mutableMapOf<Long, (Result<String>) -> Unit>()
private var partType: String? = null
private var tool: String? = null
private var eventJob: Job? = null
@@ -195,6 +198,37 @@ class SessionController(
updates.requestFlush(true)
}
fun enhancePrompt(text: String, complete: (Result<String>) -> Unit) {
assertEdt()
if (disposed) {
complete(Result.failure(CancellationException("Session controller disposed")))
return
}
val id = ++enhancement
enhancements[id] = complete
capture("Prompt Enhance Clicked", mapOf("textLength" to bucket(text)))
cs.launch {
val result = try {
Result.success(sessions.enhancePrompt(directory, text))
} catch (e: CancellationException) {
Result.failure(e)
} catch (e: Exception) {
Result.failure(e)
}
edt {
val callback = enhancements.remove(id) ?: return@edt
result.onSuccess {
capture("Prompt Enhanced", mapOf("textLength" to bucket(text)))
}.onFailure { e ->
if (e !is CancellationException) {
capture("Session Error", mapOf("context" to "enhance-prompt", "errorClass" to e::class.java.name))
}
}
callback(result)
}
}
}
fun prompt(text: String) {
assertEdt()
val start = sid ?: ref?.key ?: "pending"
@@ -1716,13 +1750,18 @@ class SessionController(
override fun dispose() {
runEdt {
if (disposed) return@runEdt
disposed = true
connectionDelay.dispose()
cancelSubscriptions()
drainJob?.cancel()
drainJob = null
val callbacks = enhancements.values.toList()
enhancements.clear()
cs.cancel()
val result = Result.failure<String>(CancellationException("Session controller disposed"))
callbacks.forEach { it(result) }
}
cs.cancel()
}
override fun toString(): String {
@@ -1,5 +1,6 @@
package ai.kilocode.client.session.ui.prompt
import ai.kilocode.client.KiloNotifications
import ai.kilocode.client.actions.SendPromptAction
import ai.kilocode.client.actions.StopSessionAction
import ai.kilocode.client.plugin.KiloBundle
@@ -33,6 +34,7 @@ import com.intellij.openapi.keymap.KeymapManagerListener
import com.intellij.openapi.keymap.KeymapUtil
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.IconLoader
import com.intellij.ui.AnimatedIcon
import com.intellij.util.concurrency.annotations.RequiresEdt
import com.intellij.xml.util.XmlStringUtil
import com.intellij.util.ui.JBValue
@@ -41,6 +43,7 @@ import com.intellij.util.ui.JBUI
import com.intellij.util.ui.UIUtil
import com.intellij.util.ui.components.BorderLayoutPanel
import com.intellij.util.messages.MessageBusConnection
import kotlinx.coroutines.CancellationException
import java.awt.BorderLayout
import java.awt.Cursor
import java.awt.Graphics
@@ -65,6 +68,7 @@ class PromptPanel(
private val project: Project,
private val onSend: (String) -> Unit,
private val onAbort: () -> Unit,
private val onEnhance: (String, (Result<String>) -> Unit) -> Unit,
) : BorderLayoutPanel(), SessionEditorStyleTarget, SendPromptContext {
companion object {
@@ -73,6 +77,7 @@ class PromptPanel(
private val STOP_ICON: Icon = IconLoader.getIcon("/icons/stop.svg", PromptPanel::class.java)
private val SHIELD_ICON: Icon = IconLoader.getIcon("/icons/shield.svg", PromptPanel::class.java)
private val SHIELD_FILLED_ICON: Icon = IconLoader.getIcon("/icons/shield-filled.svg", PromptPanel::class.java)
private val WAND_ICON: Icon = IconLoader.getIcon("/icons/wand-sparkles.svg", PromptPanel::class.java)
}
val mode = ModePicker()
@@ -146,9 +151,19 @@ class PromptPanel(
addActionListener { onAutoApproveToggle(!autoApprove) }
}
private val enhancingIcon = AnimatedIcon.Default()
private val enhance = HoverIcon().apply {
icon = WAND_ICON
toolTipText = KiloBundle.message("prompt.action.enhance")
accessibleContext.accessibleName = KiloBundle.message("prompt.action.enhance")
addActionListener { enhance() }
}
@Volatile
private var busy = false
private var ready = false
private var enhancing = false
private var request = 0L
override val isSendEnabled: Boolean
get() = ready && !busy && text().isNotEmpty()
@@ -171,6 +186,7 @@ class PromptPanel(
editor.text = ""
editor.addDocumentListener(object : DocumentListener {
override fun documentChanged(e: DocumentEvent) {
invalidateEnhancement()
syncEditorHeight()
onChange()
}
@@ -192,21 +208,26 @@ class PromptPanel(
bar.add(Box.createHorizontalGlue())
bar.add(auto)
bar.add(Box.createHorizontalStrut(JBUI.scale(SessionUiStyle.View.Prompt.CONTROL_GAP)))
bar.add(enhance)
bar.add(Box.createHorizontalStrut(JBUI.scale(SessionUiStyle.View.Prompt.CONTROL_GAP)))
bar.add(button)
shell.add(bar, BorderLayout.SOUTH)
add(shell, BorderLayout.CENTER)
syncTooltip()
syncAutoApprove()
syncEnhance()
}
@RequiresEdt
fun setReady(value: Boolean) {
ready = value
if (!value) invalidateEnhancement() else syncEnhance()
}
@RequiresEdt
fun setBusy(value: Boolean) {
busy = value
if (value) invalidateEnhancement() else syncEnhance()
button.icon = if (value) STOP_ICON else SEND_ICON
syncTooltip()
}
@@ -283,6 +304,57 @@ class PromptPanel(
super.removeNotify()
}
@RequiresEdt
private fun enhance() {
if (!enhance.isEnabled) return
val source = editor.text
if (source.isBlank()) {
editor.text = KiloBundle.message("prompt.action.enhance.description")
focus()
return
}
val id = ++request
enhancing = true
syncEnhance()
onEnhance(source.trim()) { result -> completeEnhancement(id, source, result) }
}
@RequiresEdt
private fun completeEnhancement(id: Long, source: String, result: Result<String>) {
if (id != request || editor.text != source) return
enhancing = false
syncEnhance()
result.onSuccess {
editor.text = it
focus()
}.onFailure {
if (it is CancellationException) return@onFailure
KiloNotifications.error(
project,
KiloBundle.message("prompt.action.enhance.failed"),
KiloBundle.message("prompt.action.enhance.failed.description"),
)
}
}
@RequiresEdt
private fun invalidateEnhancement() {
request++
enhancing = false
syncEnhance()
}
@RequiresEdt
private fun syncEnhance() {
enhance.isEnabled = ready && !busy && !enhancing
enhance.icon = if (enhancing) enhancingIcon else WAND_ICON
enhance.toolTipText = if (enhancing) {
KiloBundle.message("prompt.action.enhance.loading")
} else {
KiloBundle.message("prompt.action.enhance")
}
}
@RequiresEdt
private fun submit(src: String) {
if (!isSendEnabled) return
@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 21L14 10M15 4V2M15 16V14M8 9H10M20 9H22M17.8 11.8L19 13M17.8 6.2L19 5M12.2 6.2L11 5" stroke="#6C707E" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 287 B

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 21L14 10M15 4V2M15 16V14M8 9H10M20 9H22M17.8 11.8L19 13M17.8 6.2L19 5M12.2 6.2L11 5" stroke="#CED0D6" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 287 B

@@ -135,6 +135,11 @@ prompt.placeholder.with.newline=Type a message... ({0} for new line)
prompt.button.send=Send
prompt.button.stop=Stop
prompt.button.send.tooltip.stop=To stop, press {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.
prompt.action.enhance.failed=Failed to enhance prompt
prompt.action.enhance.failed.description=The configured small model could not enhance this prompt.
prompt.action.autoApprove.enable=Enable auto-approve
prompt.action.autoApprove.disable=Disable auto-approve
prompt.action.autoApprove.enabled.tooltip=Auto-approve is enabled. Permission prompts will be approved automatically.
@@ -207,7 +212,7 @@ settings.login.action=Open User Profile
settings.models.defaultModel.title=Default Model
settings.models.defaultModel.description=The model Kilo uses when no mode-specific model is configured.
settings.models.smallModel.title=Small Model
settings.models.smallModel.description=The lightweight model used for tasks such as title generation.
settings.models.smallModel.description=The lightweight model used for prompt enhancement, title generation, and other quick tasks.
settings.models.subagentModel.title=Subagent Model
settings.models.subagentModel.description=The default model for task-tool subagents. If unset, subagents inherit the calling mode model.
settings.models.subagentVariant.title=Reasoning Effort
@@ -209,6 +209,11 @@ session.login.required.dismiss=Dismiss
session.header.todos.toggle=Toggle to-dos
prompt.action.enhance=تحسين الطلب
prompt.action.enhance.loading=جارٍ تحسين الطلب...
prompt.action.enhance.description=يساعد زر 'تحسين الطلب' في تحسين طلبك بإضافة سياق أو توضيحات أو إعادة صياغة. جرّب كتابة طلب هنا، ثم انقر على الزر مرة أخرى لترى كيف يعمل.
prompt.action.enhance.failed=فشل تحسين الطلب
prompt.action.enhance.failed.description=تعذّر على النموذج الصغير الذي تم إعداده تحسين هذا الطلب.
prompt.action.autoApprove.enable=Enable auto-approve
prompt.action.autoApprove.disable=Disable auto-approve
prompt.action.autoApprove.enabled.tooltip=Auto-approve is enabled. Permission prompts will be approved automatically.
@@ -232,7 +237,7 @@ settings.login.action=Open User Profile
settings.models.defaultModel.title=Default Model
settings.models.defaultModel.description=The model Kilo uses when no mode-specific model is configured.
settings.models.smallModel.title=Small Model
settings.models.smallModel.description=The lightweight model used for tasks such as title generation.
settings.models.smallModel.description=النموذج الخفيف المستخدم لتحسين الطلبات وإنشاء العناوين وغيرها من المهام السريعة.
settings.models.subagentModel.title=Subagent Model
settings.models.subagentModel.description=The default model for task-tool subagents. If unset, subagents inherit the calling mode model.
settings.models.subagentVariant.title=Reasoning Effort
@@ -209,6 +209,11 @@ session.login.required.dismiss=Dismiss
session.header.todos.toggle=Toggle to-dos
prompt.action.enhance=Poboljšaj upit
prompt.action.enhance.loading=Poboljšavanje upita...
prompt.action.enhance.description=Dugme 'Poboljšaj upit' pomaže vam da poboljšate upit dodavanjem dodatnog konteksta, pojašnjenja ili preformulacije. Unesite upit ovdje i ponovo kliknite na dugme da vidite kako funkcioniše.
prompt.action.enhance.failed=Poboljšavanje upita nije uspjelo
prompt.action.enhance.failed.description=Konfigurisani mali model nije uspio poboljšati ovaj upit.
prompt.action.autoApprove.enable=Enable auto-approve
prompt.action.autoApprove.disable=Disable auto-approve
prompt.action.autoApprove.enabled.tooltip=Auto-approve is enabled. Permission prompts will be approved automatically.
@@ -232,7 +237,7 @@ settings.login.action=Open User Profile
settings.models.defaultModel.title=Default Model
settings.models.defaultModel.description=The model Kilo uses when no mode-specific model is configured.
settings.models.smallModel.title=Small Model
settings.models.smallModel.description=The lightweight model used for tasks such as title generation.
settings.models.smallModel.description=Lagani model koji se koristi za poboljšavanje upita, generisanje naslova i druge brze zadatke.
settings.models.subagentModel.title=Subagent Model
settings.models.subagentModel.description=The default model for task-tool subagents. If unset, subagents inherit the calling mode model.
settings.models.subagentVariant.title=Reasoning Effort
@@ -209,6 +209,11 @@ session.login.required.dismiss=Dismiss
session.header.todos.toggle=Toggle to-dos
prompt.action.enhance=Forbedr prompt
prompt.action.enhance.loading=Forbedrer prompt...
prompt.action.enhance.description=Knappen 'Forbedr prompt' hjælper med at forbedre din prompt ved at tilføje ekstra kontekst, præciseringer eller omformuleringer. Prøv at skrive en prompt her og klikke på knappen igen for at se, hvordan det fungerer.
prompt.action.enhance.failed=Kunne ikke forbedre prompten
prompt.action.enhance.failed.description=Den konfigurerede lille model kunne ikke forbedre denne prompt.
prompt.action.autoApprove.enable=Enable auto-approve
prompt.action.autoApprove.disable=Disable auto-approve
prompt.action.autoApprove.enabled.tooltip=Auto-approve is enabled. Permission prompts will be approved automatically.
@@ -232,7 +237,7 @@ settings.login.action=Open User Profile
settings.models.defaultModel.title=Default Model
settings.models.defaultModel.description=The model Kilo uses when no mode-specific model is configured.
settings.models.smallModel.title=Small Model
settings.models.smallModel.description=The lightweight model used for tasks such as title generation.
settings.models.smallModel.description=Den lette model, der bruges til at forbedre prompts, generere titler og udføre andre hurtige opgaver.
settings.models.subagentModel.title=Subagent Model
settings.models.subagentModel.description=The default model for task-tool subagents. If unset, subagents inherit the calling mode model.
settings.models.subagentVariant.title=Reasoning Effort
@@ -213,6 +213,11 @@ prompt.action.autoApprove.enable=Enable auto-approve
prompt.action.autoApprove.disable=Disable auto-approve
prompt.action.autoApprove.enabled.tooltip=Auto-approve is enabled. Permission prompts will be approved automatically.
prompt.action.autoApprove.disabled.tooltip=Auto-approve is disabled. Click to approve permission prompts automatically.
prompt.action.enhance=Prompt verbessern
prompt.action.enhance.loading=Prompt wird verbessert...
prompt.action.enhance.description=Die Schaltfläche „Prompt verbessern“ hilft Ihnen, Ihren Prompt durch zusätzlichen Kontext, Klarstellungen oder Umformulierungen zu verbessern. Geben Sie hier einen Prompt ein und klicken Sie erneut auf die Schaltfläche, um zu sehen, wie sie funktioniert.
prompt.action.enhance.failed=Prompt konnte nicht verbessert werden
prompt.action.enhance.failed.description=Das konfigurierte kleine Modell konnte diesen Prompt nicht verbessern.
model.picker.dataCollected=Daten können für das Training verwendet werden
model.picker.dataCollected.current=Das aktuell ausgewählte Modell kann für das Training verwendet werden
@@ -232,7 +237,7 @@ settings.login.action=Open User Profile
settings.models.defaultModel.title=Default Model
settings.models.defaultModel.description=The model Kilo uses when no mode-specific model is configured.
settings.models.smallModel.title=Small Model
settings.models.smallModel.description=The lightweight model used for tasks such as title generation.
settings.models.smallModel.description=Das leichtgewichtige Modell, das zur Verbesserung von Prompts, zur Titelgenerierung und für andere schnelle Aufgaben verwendet wird.
settings.models.subagentModel.title=Subagent Model
settings.models.subagentModel.description=The default model for task-tool subagents. If unset, subagents inherit the calling mode model.
settings.models.subagentVariant.title=Reasoning Effort
@@ -213,6 +213,11 @@ prompt.action.autoApprove.enable=Enable auto-approve
prompt.action.autoApprove.disable=Disable auto-approve
prompt.action.autoApprove.enabled.tooltip=Auto-approve is enabled. Permission prompts will be approved automatically.
prompt.action.autoApprove.disabled.tooltip=Auto-approve is disabled. Click to approve permission prompts automatically.
prompt.action.enhance=Mejorar mensaje
prompt.action.enhance.loading=Mejorando mensaje...
prompt.action.enhance.description=El botón «Mejorar mensaje» ayuda a mejorar el mensaje aportando contexto adicional, aclaraciones o una reformulación. Escribe un mensaje aquí y vuelve a hacer clic en el botón para ver cómo funciona.
prompt.action.enhance.failed=No se pudo mejorar el mensaje
prompt.action.enhance.failed.description=El modelo pequeño configurado no pudo mejorar este mensaje.
model.picker.dataCollected=Los datos pueden usarse para entrenamiento
model.picker.dataCollected.current=El modelo seleccionado actualmente puede usarse para entrenamiento
@@ -232,7 +237,7 @@ settings.login.action=Open User Profile
settings.models.defaultModel.title=Default Model
settings.models.defaultModel.description=The model Kilo uses when no mode-specific model is configured.
settings.models.smallModel.title=Small Model
settings.models.smallModel.description=The lightweight model used for tasks such as title generation.
settings.models.smallModel.description=El modelo ligero que se utiliza para mejorar mensajes, generar títulos y realizar otras tareas rápidas.
settings.models.subagentModel.title=Subagent Model
settings.models.subagentModel.description=The default model for task-tool subagents. If unset, subagents inherit the calling mode model.
settings.models.subagentVariant.title=Reasoning Effort
@@ -213,6 +213,11 @@ prompt.action.autoApprove.enable=Enable auto-approve
prompt.action.autoApprove.disable=Disable auto-approve
prompt.action.autoApprove.enabled.tooltip=Auto-approve is enabled. Permission prompts will be approved automatically.
prompt.action.autoApprove.disabled.tooltip=Auto-approve is disabled. Click to approve permission prompts automatically.
prompt.action.enhance=Améliorer l'invite
prompt.action.enhance.loading=Amélioration de l'invite...
prompt.action.enhance.description=Le bouton « Améliorer l'invite » permet d'améliorer votre invite en ajoutant du contexte, des précisions ou en la reformulant. Saisissez une invite ici, puis cliquez à nouveau sur le bouton pour voir comment il fonctionne.
prompt.action.enhance.failed=Impossible d'améliorer l'invite
prompt.action.enhance.failed.description=Le petit modèle configuré n'a pas pu améliorer cette invite.
model.picker.dataCollected=Les données peuvent être utilisées pour lentraînement
model.picker.dataCollected.current=Le modèle actuellement sélectionné peut être utilisé pour lentraînement
@@ -232,7 +237,7 @@ settings.login.action=Open User Profile
settings.models.defaultModel.title=Default Model
settings.models.defaultModel.description=The model Kilo uses when no mode-specific model is configured.
settings.models.smallModel.title=Small Model
settings.models.smallModel.description=The lightweight model used for tasks such as title generation.
settings.models.smallModel.description=Le modèle léger utilisé pour améliorer les invites, générer des titres et effectuer d'autres tâches rapides.
settings.models.subagentModel.title=Subagent Model
settings.models.subagentModel.description=The default model for task-tool subagents. If unset, subagents inherit the calling mode model.
settings.models.subagentVariant.title=Reasoning Effort
@@ -209,6 +209,11 @@ session.login.required.dismiss=Dismiss
session.header.todos.toggle=Toggle to-dos
prompt.action.enhance=プロンプトを改善
prompt.action.enhance.loading=プロンプトを改善中…
prompt.action.enhance.description='プロンプトを改善'ボタンを使うと、コンテキストの追加、内容の明確化、言い換えによってプロンプトを改善できます。ここにプロンプトを入力し、もう一度ボタンをクリックして動作を確認してみてください。
prompt.action.enhance.failed=プロンプトの改善に失敗しました
prompt.action.enhance.failed.description=設定された小型モデルでは、このプロンプトを改善できませんでした。
prompt.action.autoApprove.enable=Enable auto-approve
prompt.action.autoApprove.disable=Disable auto-approve
prompt.action.autoApprove.enabled.tooltip=Auto-approve is enabled. Permission prompts will be approved automatically.
@@ -232,7 +237,7 @@ settings.login.action=Open User Profile
settings.models.defaultModel.title=Default Model
settings.models.defaultModel.description=The model Kilo uses when no mode-specific model is configured.
settings.models.smallModel.title=Small Model
settings.models.smallModel.description=The lightweight model used for tasks such as title generation.
settings.models.smallModel.description=プロンプトの改善やタイトル生成など、短時間で処理できるタスクに使用する軽量モデルです。
settings.models.subagentModel.title=Subagent Model
settings.models.subagentModel.description=The default model for task-tool subagents. If unset, subagents inherit the calling mode model.
settings.models.subagentVariant.title=Reasoning Effort
@@ -209,6 +209,11 @@ session.login.required.dismiss=Dismiss
session.header.todos.toggle=Toggle to-dos
prompt.action.enhance=프롬프트 개선
prompt.action.enhance.loading=프롬프트 개선 중…
prompt.action.enhance.description='프롬프트 개선' 버튼을 사용하면 컨텍스트를 추가하거나 내용을 명확히 하고 표현을 다듬어 프롬프트를 개선할 수 있습니다. 여기에 프롬프트를 입력한 다음 버튼을 다시 클릭해 작동 방식을 확인해 보세요.
prompt.action.enhance.failed=프롬프트 개선 실패
prompt.action.enhance.failed.description=설정된 소형 모델이 이 프롬프트를 개선하지 못했습니다.
prompt.action.autoApprove.enable=Enable auto-approve
prompt.action.autoApprove.disable=Disable auto-approve
prompt.action.autoApprove.enabled.tooltip=Auto-approve is enabled. Permission prompts will be approved automatically.
@@ -232,7 +237,7 @@ settings.login.action=Open User Profile
settings.models.defaultModel.title=Default Model
settings.models.defaultModel.description=The model Kilo uses when no mode-specific model is configured.
settings.models.smallModel.title=Small Model
settings.models.smallModel.description=The lightweight model used for tasks such as title generation.
settings.models.smallModel.description=프롬프트 개선, 제목 생성 및 빠르게 처리할 수 있는 기타 작업에 사용되는 경량 모델입니다.
settings.models.subagentModel.title=Subagent Model
settings.models.subagentModel.description=The default model for task-tool subagents. If unset, subagents inherit the calling mode model.
settings.models.subagentVariant.title=Reasoning Effort
@@ -209,6 +209,11 @@ session.login.required.dismiss=Dismiss
session.header.todos.toggle=Toggle to-dos
prompt.action.enhance=Prompt verbeteren
prompt.action.enhance.loading=Prompt wordt verbeterd…
prompt.action.enhance.description=Met de knop 'Prompt verbeteren' kunt u uw prompt verbeteren door extra context toe te voegen, de inhoud te verduidelijken of de tekst anders te formuleren. Typ hier een prompt en klik nogmaals op de knop om te zien hoe het werkt.
prompt.action.enhance.failed=Prompt verbeteren is mislukt
prompt.action.enhance.failed.description=Het geconfigureerde kleine model kon deze prompt niet verbeteren.
prompt.action.autoApprove.enable=Enable auto-approve
prompt.action.autoApprove.disable=Disable auto-approve
prompt.action.autoApprove.enabled.tooltip=Auto-approve is enabled. Permission prompts will be approved automatically.
@@ -232,7 +237,7 @@ settings.login.action=Open User Profile
settings.models.defaultModel.title=Default Model
settings.models.defaultModel.description=The model Kilo uses when no mode-specific model is configured.
settings.models.smallModel.title=Small Model
settings.models.smallModel.description=The lightweight model used for tasks such as title generation.
settings.models.smallModel.description=Het lichtgewichtmodel dat wordt gebruikt om prompts te verbeteren, titels te genereren en andere snelle taken uit te voeren.
settings.models.subagentModel.title=Subagent Model
settings.models.subagentModel.description=The default model for task-tool subagents. If unset, subagents inherit the calling mode model.
settings.models.subagentVariant.title=Reasoning Effort
@@ -74,6 +74,11 @@ prompt.placeholder.with.newline=Skriv en melding… ({0} for ny linje)
prompt.button.send=Send
prompt.button.stop=Stopp
prompt.button.send.tooltip.stop=Trykk {0} for å stoppe
prompt.action.enhance=Forbedre forespørsel
prompt.action.enhance.loading=Forbedrer forespørselen...
prompt.action.enhance.description=Knappen 'Forbedre forespørsel' hjelper deg med å forbedre forespørselen ved å legge til mer kontekst, presiseringer eller omformuleringer. Skriv inn en forespørsel her, og klikk på knappen igjen for å se hvordan det fungerer.
prompt.action.enhance.failed=Kunne ikke forbedre forespørselen
prompt.action.enhance.failed.description=Den konfigurerte lille modellen kunne ikke forbedre denne forespørselen.
mode.picker.tooltip=Velg modus
mode.picker.deprecated=utdatert
model.picker.tooltip=Velg modell
@@ -232,7 +237,7 @@ settings.login.action=Open User Profile
settings.models.defaultModel.title=Default Model
settings.models.defaultModel.description=The model Kilo uses when no mode-specific model is configured.
settings.models.smallModel.title=Small Model
settings.models.smallModel.description=The lightweight model used for tasks such as title generation.
settings.models.smallModel.description=Lettvektsmodellen som brukes til å forbedre forespørsler, generere titler og utføre andre raske oppgaver.
settings.models.subagentModel.title=Subagent Model
settings.models.subagentModel.description=The default model for task-tool subagents. If unset, subagents inherit the calling mode model.
settings.models.subagentVariant.title=Reasoning Effort
@@ -74,6 +74,11 @@ prompt.placeholder.with.newline=Wpisz wiadomość… ({0} nowa linia)
prompt.button.send=Wyślij
prompt.button.stop=Zatrzymaj
prompt.button.send.tooltip.stop=Aby zatrzymać, naciśnij {0}
prompt.action.enhance=Ulepsz prompt
prompt.action.enhance.loading=Ulepszanie promptu...
prompt.action.enhance.description=Przycisk 'Ulepsz prompt' pomaga dopracować prompt, dodając kontekst, wyjaśnienia lub zmieniając jego sformułowanie. Spróbuj wpisać tutaj prompt i ponownie kliknąć przycisk, aby zobaczyć, jak to działa.
prompt.action.enhance.failed=Nie udało się ulepszyć promptu
prompt.action.enhance.failed.description=Skonfigurowany mały model nie mógł ulepszyć tego promptu.
mode.picker.tooltip=Wybierz tryb
mode.picker.deprecated=przestarzały
model.picker.tooltip=Wybierz model
@@ -232,7 +237,7 @@ settings.login.action=Open User Profile
settings.models.defaultModel.title=Default Model
settings.models.defaultModel.description=The model Kilo uses when no mode-specific model is configured.
settings.models.smallModel.title=Small Model
settings.models.smallModel.description=The lightweight model used for tasks such as title generation.
settings.models.smallModel.description=Lekki model używany do ulepszania promptów, generowania tytułów i wykonywania innych szybkich zadań.
settings.models.subagentModel.title=Subagent Model
settings.models.subagentModel.description=The default model for task-tool subagents. If unset, subagents inherit the calling mode model.
settings.models.subagentVariant.title=Reasoning Effort
@@ -74,6 +74,11 @@ prompt.placeholder.with.newline=Digite uma mensagem… ({0} para nova linha)
prompt.button.send=Enviar
prompt.button.stop=Parar
prompt.button.send.tooltip.stop=Para parar, pressione {0}
prompt.action.enhance=Melhorar prompt
prompt.action.enhance.loading=Melhorando o prompt...
prompt.action.enhance.description=O botão 'Melhorar prompt' ajuda a aprimorar seu prompt, fornecendo mais contexto, esclarecimentos ou reformulações. Digite um prompt aqui e clique novamente no botão para ver como funciona.
prompt.action.enhance.failed=Falha ao melhorar o prompt
prompt.action.enhance.failed.description=O modelo leve configurado não conseguiu melhorar este prompt.
mode.picker.tooltip=Selecionar modo
mode.picker.deprecated=obsoleto
model.picker.tooltip=Selecionar modelo
@@ -232,7 +237,7 @@ settings.login.action=Open User Profile
settings.models.defaultModel.title=Default Model
settings.models.defaultModel.description=The model Kilo uses when no mode-specific model is configured.
settings.models.smallModel.title=Small Model
settings.models.smallModel.description=The lightweight model used for tasks such as title generation.
settings.models.smallModel.description=O modelo leve usado para aprimorar prompts, gerar títulos e realizar outras tarefas rápidas.
settings.models.subagentModel.title=Subagent Model
settings.models.subagentModel.description=The default model for task-tool subagents. If unset, subagents inherit the calling mode model.
settings.models.subagentVariant.title=Reasoning Effort
@@ -74,6 +74,11 @@ prompt.placeholder.with.newline=Введите сообщение… ({0} — н
prompt.button.send=Отправить
prompt.button.stop=Остановить
prompt.button.send.tooltip.stop=Для остановки нажмите {0}
prompt.action.enhance=Улучшить промпт
prompt.action.enhance.loading=Улучшаем промпт…
prompt.action.enhance.description=Кнопка 'Улучшить промпт' помогает улучшить ваш промпт, добавляя контекст, уточнения или переформулировки. Введите промпт и снова нажмите кнопку, чтобы увидеть, как это работает.
prompt.action.enhance.failed=Не удалось улучшить промпт
prompt.action.enhance.failed.description=Настроенная малая модель не смогла улучшить этот промпт.
mode.picker.tooltip=Выбрать режим
mode.picker.deprecated=устаревший
model.picker.tooltip=Выбрать модель
@@ -232,7 +237,7 @@ settings.login.action=Open User Profile
settings.models.defaultModel.title=Default Model
settings.models.defaultModel.description=The model Kilo uses when no mode-specific model is configured.
settings.models.smallModel.title=Small Model
settings.models.smallModel.description=The lightweight model used for tasks such as title generation.
settings.models.smallModel.description=Лёгкая модель, используемая для улучшения промптов, создания заголовков и других быстрых задач.
settings.models.subagentModel.title=Subagent Model
settings.models.subagentModel.description=The default model for task-tool subagents. If unset, subagents inherit the calling mode model.
settings.models.subagentVariant.title=Reasoning Effort
@@ -74,6 +74,11 @@ prompt.placeholder.with.newline=พิมพ์ข้อความ… ({0} ส
prompt.button.send=ส่ง
prompt.button.stop=หยุด
prompt.button.send.tooltip.stop=กด {0} เพื่อหยุด
prompt.action.enhance=ปรับปรุงพรอมต์
prompt.action.enhance.loading=กำลังปรับปรุงพรอมต์…
prompt.action.enhance.description=ปุ่ม 'ปรับปรุงพรอมต์' ช่วยปรับปรุงพรอมต์ของคุณโดยเพิ่มบริบท ทำให้ชัดเจนขึ้น หรือเรียบเรียงใหม่ ลองพิมพ์พรอมต์ที่นี่แล้วคลิกปุ่มอีกครั้งเพื่อดูว่าทำงานอย่างไร
prompt.action.enhance.failed=ปรับปรุงพรอมต์ไม่สำเร็จ
prompt.action.enhance.failed.description=โมเดลขนาดเล็กที่กำหนดค่าไว้ไม่สามารถปรับปรุงพรอมต์นี้ได้
mode.picker.tooltip=เลือกโหมด
mode.picker.deprecated=ล้าสมัย
model.picker.tooltip=เลือกโมเดล
@@ -232,7 +237,7 @@ settings.login.action=Open User Profile
settings.models.defaultModel.title=Default Model
settings.models.defaultModel.description=The model Kilo uses when no mode-specific model is configured.
settings.models.smallModel.title=Small Model
settings.models.smallModel.description=The lightweight model used for tasks such as title generation.
settings.models.smallModel.description=โมเดลน้ำหนักเบาที่ใช้สำหรับปรับปรุงพรอมต์ สร้างชื่อเรื่อง และทำงานด่วนอื่นๆ
settings.models.subagentModel.title=Subagent Model
settings.models.subagentModel.description=The default model for task-tool subagents. If unset, subagents inherit the calling mode model.
settings.models.subagentVariant.title=Reasoning Effort
@@ -74,6 +74,11 @@ prompt.placeholder.with.newline=Bir mesaj yazın… ({0} yeni satır için)
prompt.button.send=Gönder
prompt.button.stop=Durdur
prompt.button.send.tooltip.stop=Durdurmak için {0} basın
prompt.action.enhance=İstemi iyileştir
prompt.action.enhance.loading=İstem iyileştiriliyor…
prompt.action.enhance.description='İstemi İyileştir' düğmesi, ek bağlam, açıklama veya farklı bir ifade sunarak isteminizi iyileştirmenize yardımcı olur. Nasıl çalıştığını görmek için buraya bir istem yazıp düğmeye tekrar tıklayın.
prompt.action.enhance.failed=İstem iyileştirilemedi
prompt.action.enhance.failed.description=Yapılandırılan küçük model bu istemi iyileştiremedi.
mode.picker.tooltip=Mod seç
mode.picker.deprecated=geçersiz
model.picker.tooltip=Model seç
@@ -232,7 +237,7 @@ settings.login.action=Open User Profile
settings.models.defaultModel.title=Default Model
settings.models.defaultModel.description=The model Kilo uses when no mode-specific model is configured.
settings.models.smallModel.title=Small Model
settings.models.smallModel.description=The lightweight model used for tasks such as title generation.
settings.models.smallModel.description=İstem iyileştirme, başlık oluşturma ve diğer hızlı görevler için kullanılan hafif model.
settings.models.subagentModel.title=Subagent Model
settings.models.subagentModel.description=The default model for task-tool subagents. If unset, subagents inherit the calling mode model.
settings.models.subagentVariant.title=Reasoning Effort
@@ -209,6 +209,11 @@ session.login.required.dismiss=Dismiss
session.header.todos.toggle=Toggle to-dos
prompt.action.enhance=Покращити запит
prompt.action.enhance.loading=Покращення запиту...
prompt.action.enhance.description=Кнопка 'Покращити запит' допомагає вдосконалити ваш запит, надаючи додатковий контекст, уточнення або перефразування. Введіть запит тут і натисніть кнопку ще раз, щоб побачити, як це працює.
prompt.action.enhance.failed=Не вдалося покращити запит
prompt.action.enhance.failed.description=Налаштована мала модель не змогла покращити цей запит.
prompt.action.autoApprove.enable=Enable auto-approve
prompt.action.autoApprove.disable=Disable auto-approve
prompt.action.autoApprove.enabled.tooltip=Auto-approve is enabled. Permission prompts will be approved automatically.
@@ -232,7 +237,7 @@ settings.login.action=Open User Profile
settings.models.defaultModel.title=Default Model
settings.models.defaultModel.description=The model Kilo uses when no mode-specific model is configured.
settings.models.smallModel.title=Small Model
settings.models.smallModel.description=The lightweight model used for tasks such as title generation.
settings.models.smallModel.description=Легка модель, що використовується для покращення запитів, генерування заголовків та інших швидких завдань.
settings.models.subagentModel.title=Subagent Model
settings.models.subagentModel.description=The default model for task-tool subagents. If unset, subagents inherit the calling mode model.
settings.models.subagentVariant.title=Reasoning Effort
@@ -209,6 +209,11 @@ session.login.required.dismiss=Dismiss
session.header.todos.toggle=Toggle to-dos
prompt.action.enhance=优化提示词
prompt.action.enhance.loading=正在优化提示词...
prompt.action.enhance.description=“优化提示词”按钮可通过补充上下文、澄清内容或重新措辞来改进您的提示词。请在此输入提示词,然后再次点击该按钮,即可查看效果。
prompt.action.enhance.failed=提示词优化失败
prompt.action.enhance.failed.description=已配置的小模型无法优化此提示词。
prompt.action.autoApprove.enable=Enable auto-approve
prompt.action.autoApprove.disable=Disable auto-approve
prompt.action.autoApprove.enabled.tooltip=Auto-approve is enabled. Permission prompts will be approved automatically.
@@ -232,7 +237,7 @@ settings.login.action=Open User Profile
settings.models.defaultModel.title=Default Model
settings.models.defaultModel.description=The model Kilo uses when no mode-specific model is configured.
settings.models.smallModel.title=Small Model
settings.models.smallModel.description=The lightweight model used for tasks such as title generation.
settings.models.smallModel.description=用于优化提示词、生成标题和执行其他快速任务的轻量级模型。
settings.models.subagentModel.title=Subagent Model
settings.models.subagentModel.description=The default model for task-tool subagents. If unset, subagents inherit the calling mode model.
settings.models.subagentVariant.title=Reasoning Effort

Some files were not shown because too many files have changed in this diff Show More