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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/kimi_cli/ui/shell/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
from kimi_cli.utils.signals import install_sigint_handler
from kimi_cli.utils.slashcmd import SlashCommand, SlashCommandCall, parse_slash_command_call
from kimi_cli.utils.subprocess_env import get_clean_env
from kimi_cli.utils.term import ensure_new_line, ensure_tty_sane
from kimi_cli.utils.term import ensure_new_line, ensure_tty_sane, maybe_disable_kitty_keyboard_protocol
from kimi_cli.wire.types import ContentPart, StatusUpdate


Expand Down Expand Up @@ -109,6 +109,8 @@ async def _plan_mode_toggle() -> bool:
return await self.soul.toggle_plan_mode_from_manual()
return False

maybe_disable_kitty_keyboard_protocol()

with CustomPromptSession(
status_provider=lambda: self.soul.status,
model_capabilities=self.soul.model_capabilities or set(),
Expand Down
34 changes: 34 additions & 0 deletions src/kimi_cli/utils/term.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import sys
import time

from kimi_cli.utils.envvar import get_env_bool


def ensure_new_line() -> None:
"""Ensure the next prompt starts at column 0 regardless of prior command output."""
Expand Down Expand Up @@ -50,6 +52,33 @@ def ensure_tty_sane() -> None:
termios.tcsetattr(fd, termios.TCSADRAIN, attrs)


def maybe_disable_kitty_keyboard_protocol() -> None:
"""Disable kitty keyboard protocol in terminals that send CSI-u sequences.

This is primarily a workaround for VS Code's integrated terminal, which can
emit CSI-u key sequences that prompt_toolkit doesn't parse.
"""
if sys.platform == "win32":
return
if not sys.stdout.isatty() or not sys.stdin.isatty():
return
if not _should_disable_kitty_keyboard_protocol():
return

_write_escape("\x1b[<u")


def _should_disable_kitty_keyboard_protocol() -> bool:
env_value = os.getenv("KIMI_CLI_DISABLE_KITTY_KEYS")
if env_value is not None:
return get_env_bool("KIMI_CLI_DISABLE_KITTY_KEYS", default=False)
return _is_vscode_terminal()


def _is_vscode_terminal() -> bool:
return os.getenv("TERM_PROGRAM") == "vscode" or "VSCODE_IPC_HOOK_CLI" in os.environ


def _cursor_position_unix() -> tuple[int, int] | None:
"""Get cursor position (row, column) on Unix. Both are 1-indexed."""
assert sys.platform != "win32"
Expand Down Expand Up @@ -149,6 +178,11 @@ def _write_newline() -> None:
sys.stdout.flush()


def _write_escape(value: str) -> None:
sys.stdout.write(value)
sys.stdout.flush()


def get_cursor_row() -> int | None:
"""Get the current cursor row (1-indexed)."""
if not sys.stdout.isatty() or not sys.stdin.isatty():
Expand Down
Loading