Merge branch 'main' into imanolmaiztegui/kilo-opencode-v1.14.34

This commit is contained in:
Imanol Maiztegui
2026-05-20 08:59:50 +02:00
53 changed files with 740 additions and 2100 deletions
+10
View File
@@ -1,5 +1,15 @@
# @kilocode/cli
## 7.3.1
### Patch Changes
- [#10285](https://github.com/Kilo-Org/kilocode/pull/10285) [`d23e162`](https://github.com/Kilo-Org/kilocode/commit/d23e162051f118beb993f84cebad1002d974ad79) - Capture aggregate usage telemetry for experimental Morph-backed codebase search.
- [#10358](https://github.com/Kilo-Org/kilocode/pull/10358) [`413222f`](https://github.com/Kilo-Org/kilocode/commit/413222f0137a29c5cf09666ea3b515032c81f9b8) - Resume interrupted CLI turns automatically after network recovery while giving users 10 seconds to cancel.
- [#10293](https://github.com/Kilo-Org/kilocode/pull/10293) [`af115af`](https://github.com/Kilo-Org/kilocode/commit/af115afe20893f4d24d22a40411ebdbd398781d7) - Harden Mermaid diagram rendering with upstream security fixes.
## 7.3.0
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "7.3.0",
"version": "7.3.1",
"name": "@kilocode/cli",
"type": "module",
"license": "MIT",
@@ -2,6 +2,7 @@
import { RGBA } from "@opentui/core"
import { For, type JSX } from "solid-js"
import { useTheme, tint } from "@tui/context/theme"
import { tui } from "../../../../kilocode/cli/logo"
// Shadow markers (rendered chars in parens):
// _ = full shadow cell (space with bg=shadow)
@@ -9,16 +10,10 @@ import { useTheme, tint } from "@tui/context/theme"
// ~ = shadow top only (▀ with fg=shadow)
const SHADOW_MARKER = /[_^~]/
const ASCII_LOGO = [
`██ ▄█▀ ██ ██ ▄████▄ ▄█████ ██ ██ `,
`████ ██ ██ ██~~██ ██~~~~ ██ ██ `,
`██ ▀█▄ ██ ██████ ▀████▀ ▀█████ ██████ ██ `,
`~~ ~~ ~~ ~~~~~~ ~~~~ ~~~~~ ~~~~~~ ~~ `,
]
export function KiloLogo() {
const { theme } = useTheme()
const yellow = RGBA.fromHex("#F8F675")
const logo = tui()
const renderLine = (line: string): JSX.Element[] => {
const shadow = tint(theme.background, yellow, 0.25)
@@ -79,7 +74,7 @@ export function KiloLogo() {
return (
<box>
<For each={ASCII_LOGO}>{(line) => <box flexDirection="row">{renderLine(line)}</box>}</For>
<For each={logo}>{(line) => <box flexDirection="row">{renderLine(line)}</box>}</For>
</box>
)
}
@@ -85,6 +85,7 @@ import { formatTranscript } from "../../util/transcript"
import { UI } from "@/cli/ui.ts"
import { useTuiConfig } from "../../context/tui-config"
import { splitDiffHunks } from "@/kilocode/tui/diff" // kilocode_change
import { session as banner } from "@/kilocode/cli/logo" // kilocode_change
import { formatMarkdownTables } from "../../util/markdown" // kilocode_change
import { bell } from "@/kilocode/bell" // kilocode_change
@@ -358,19 +359,12 @@ export function Session() {
// Allow exit when in child session (prompt is hidden)
const exit = useExit()
// kilocode_change start
createEffect(() => {
const title = Locale.truncate(session()?.title ?? "", 50)
// kilocode_change start
return exit.message.set(
[
``,
` ██ ▄█▀ ██ ██ ▄████▄ ${UI.Style.TEXT_DIM}${title}${UI.Style.TEXT_NORMAL}`,
` ████ ██ ██ ██ ██ ${UI.Style.TEXT_DIM}kilo -s ${session()?.id}${UI.Style.TEXT_NORMAL}`,
` ██ ▀█▄ ██ ██████ ▀████▀ `,
].join("\n"),
)
// kilocode_change end
return exit.message.set(banner(title, session()?.id, UI.Style.TEXT_DIM, UI.Style.TEXT_NORMAL))
})
// kilocode_change end
// kilocode_change start - double ctrl+c to exit for child sessions
const [exitPress, setExitPress] = createSignal(0)
+3 -5
View File
@@ -1,11 +1,9 @@
// kilocode_change start
import { plain } from "../kilocode/cli/logo"
export const logo = {
left: ["", "", ""],
right: [
"██ ▄█▀ ██ ██ ▄████▄ ▄█████ ██ ██ ",
`████ ██ ██ ██ ██ ██ ██ ██ `,
`██ ▀█▄ ██ ██████ ▀████▀ ▀█████ ██████ ██ `,
],
right: plain(),
}
// kilocode_change end
+4 -6
View File
@@ -2,15 +2,13 @@ import z from "zod"
import { EOL } from "os"
import { NamedError } from "@opencode-ai/core/util/error"
import { logo as glyphs } from "./logo"
import { tui } from "../kilocode/cli/logo" // kilocode_change
// kilocode_change start
const wordmark = [
// kilocode_change start
`██ ▄█▀ ██ ██ ▄████▄ ▄█████ ██ ██ `,
`████ ██ ██ ██~~██ ██~~~~ ██ ██ `,
`██ ▀█▄ ██ ██████ ▀████▀ ▀█████ ██████ ██ `,
`~~ ~~ ~~ ~~~~~~ ~~~~ ~~~~~ ~~~~~~ ~~ `,
// kilocode_change end
...tui(),
]
// kilocode_change end
export const CancelledError = NamedError.create("UICancelledError", z.void())
@@ -0,0 +1,86 @@
// kilocode_change - new file
const yes = new Set(["1", "true", "yes", "on"])
const no = new Set(["0", "false", "no", "off"])
const modern = {
tui: [
`██ ██ ██🬺🬏 ██ ██ ██🬺🬏 ████ ██ ██🬺🬏 `,
`████🬺🬏 ~~██ ██ ~~ ██~~██ ██~~~~ ██ ~~██ `,
`██ ██ ██████ 🬁🬬████ 🬁🬬██~~ 🬁🬬████ 🬁🬬████ ██████ `,
`~~ ~~ ~~~~~~ ~~~~ ~~ ~~~~ ~~~~ ~~~~~~ `,
],
plain: [
`██ ██ ██🬺🬏 ██ ██ ██🬺🬏 ████ ██ ██🬺🬏 `,
`████🬺🬏 ██ ██ ██ ██ ██ ██ ██ `,
`██ ██ ██████ 🬁🬬████ 🬁🬬██ 🬁🬬████ 🬁🬬████ ██████ `,
],
exit: [
` ██ ██ ██🬺🬏 ██ ██ ██🬺🬏 `,
` ████🬺🬏 ██ ██ ██ ██ `,
` ██ ██ ██████ 🬁🬬████ 🬁🬬██ `,
],
}
const fallback = {
tui: [
`██ ██ ████ ██ ██ ██ ████ ██ ████ `,
`████ ~~██ ██ ~~ ██~~██ ██~~~~ ██ ~~██ `,
`██ ██ ██████ ██████ ██~~ ████ ████ ██████ `,
`~~ ~~ ~~~~~~ ~~~~~ ~~ ~~~~ ~~~~ ~~~~~~ `,
],
plain: [
`██ ██ ████ ██ ██ ███ ████ ██ ████ `,
`████ ██ ██ ██ ██ ██ ██ ██ `,
`██ ██ ██████ ██████ ██ ████ ██████ ██████ `,
],
exit: [
` ██ ██ ████ ██ ██ ██ `,
` ████ ██ ██ ██ ██ `,
` ██ ██ ██████ ██████ ██ `,
],
}
function flag(value: string | undefined) {
const key = value?.toLowerCase()
if (!key) return
if (yes.has(key)) return true
if (no.has(key)) return false
}
export function supports(env = process.env, platform = process.platform) {
const override = flag(env.KILO_UNICODE_LOGO)
if (override !== undefined) return override
// Terminals do not expose font glyph coverage over SSH, so prefer the safe logo for remote sessions.
if (env.TERM === "dumb") return false
if (env.SSH_TTY) return false
if (env.SSH_CLIENT) return false
if (env.SSH_CONNECTION) return false
if (env.ConEmuPID) return false
if (env.ANSICON) return false
return true
}
export function tui(env = process.env, platform = process.platform) {
return supports(env, platform) ? modern.tui : fallback.tui
}
export function plain(env = process.env, platform = process.platform) {
return supports(env, platform) ? modern.plain : fallback.plain
}
export function session(
title: string,
id: string | undefined,
dim: string,
normal: string,
env = process.env,
platform = process.platform,
) {
const logo = supports(env, platform) ? modern.exit : fallback.exit
return [
``,
`${logo[0]}${dim}${title}${normal}`,
`${logo[1]}${dim}kilo -s ${id}${normal}`,
logo[2],
].join("\n")
}
@@ -0,0 +1,32 @@
import { describe, expect, test } from "bun:test"
import { plain, session, supports, tui } from "../../src/kilocode/cli/logo"
describe("kilocode logo", () => {
test("falls back on remote terminals", () => {
expect(supports({ SSH_TTY: "/dev/pts/0" }, "linux")).toBe(false)
expect(supports({ SSH_CLIENT: "127.0.0.1 12345 22" }, "linux")).toBe(false)
expect(supports({ SSH_CONNECTION: "127.0.0.1 12345 127.0.0.1 22" }, "linux")).toBe(false)
})
test("allows Windows Terminal locally", () => {
expect(supports({}, "win32")).toBe(true)
expect(supports({ WT_SESSION: "session" }, "linux")).toBe(true)
})
test("allows an override", () => {
expect(supports({ KILO_UNICODE_LOGO: "1", SSH_TTY: "/dev/pts/0" }, "linux")).toBe(true)
expect(supports({ KILO_UNICODE_LOGO: "0" }, "linux")).toBe(false)
})
test("uses modern and fallback logo variants", () => {
expect(tui({ KILO_UNICODE_LOGO: "1" }, "linux").join("\n")).toContain("🬺🬏")
expect(tui({ SSH_TTY: "/dev/pts/0" }, "linux").join("\n")).not.toContain("🬺🬏")
expect(plain({ SSH_TTY: "/dev/pts/0" }, "linux").join("\n")).not.toContain("🬁🬬")
})
test("formats child session exit logo", () => {
const out = session("Title", "ses_test", "<dim>", "<reset>", { SSH_TTY: "/dev/pts/0" }, "linux")
expect(out).toContain("<dim>Title<reset>")
expect(out).not.toContain("🬺🬏")
})
})
+27 -3
View File
@@ -1524,6 +1524,7 @@ unix(
30_000,
)
// kilocode_change start - cancel persists aborted shell result when shell ignores TERM
unix(
"cancel persists aborted shell result when shell ignores TERM",
() =>
@@ -1531,12 +1532,32 @@ unix(
provideTmpdirInstance(
(_dir) =>
Effect.gen(function* () {
const { prompt, chat } = yield* boot()
const { prompt, chat, sessions } = yield* boot()
const sh = yield* prompt
.shell({ sessionID: chat.id, agent: "build", command: "trap '' TERM; sleep 30" })
.shell({
sessionID: chat.id,
agent: "build",
command: "trap '' TERM; printf started; sleep 10; printf not-killed",
})
.pipe(Effect.forkChild)
yield* Effect.sleep(50)
yield* waitFor(
"shell start output",
sessions
.messages({ sessionID: chat.id })
.pipe(
Effect.map((msgs) =>
msgs
.flatMap((msg) => msg.parts)
.find(
(part) =>
part.type === "tool" &&
part.state.status === "running" &&
(part.state.metadata?.output ?? "").includes("started"),
),
),
),
)
yield* prompt.cancel(chat.id)
@@ -1546,7 +1567,9 @@ unix(
expect(exit.value.info.role).toBe("assistant")
const tool = completedTool(exit.value.parts)
if (tool) {
expect(tool.state.output).toContain("started")
expect(tool.state.output).toContain("User aborted the command")
expect(tool.state.output).not.toContain("not-killed")
}
}
}),
@@ -1555,6 +1578,7 @@ unix(
),
30_000,
)
// kilocode_change end
unix(
"cancel finalizes interrupted bash tool output through normal truncation",