Skip to content
Closed
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Only write entries that are worth mentioning to users.

## Unreleased

- Config: Add `kill_ring_system_clipboard` option — set to `false` to keep emacs-style kill commands (`Ctrl-W`, `Ctrl-K`, `Ctrl-U`, etc.) in an in-memory kill ring instead of overwriting the system clipboard

## 1.43.0 (2026-05-12)

- Security: Bump pillow to 12.2.0 to address CVE-2026-25990 (out-of-bounds write when loading PSD images); unblocks installs in environments that gate on the older pinned version
Expand Down
2 changes: 2 additions & 0 deletions docs/en/configuration/config-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ The configuration file contains the following top-level configuration items:
| `show_thinking_stream` | `boolean` | Whether to stream the raw reasoning text in the live area as a 6-line scrolling preview and commit the full reasoning markdown to history when the block ends (defaults to `true`; set to `false` to show only the compact `Thinking ...` indicator and a one-line trace summary) |
| `merge_all_available_skills` | `boolean` | Whether to merge skills from all brand directories (defaults to `true`); see [Skills configuration](../customization/skills.md) |
| `telemetry` | `boolean` | Whether to enable anonymous telemetry to help improve kimi-cli (defaults to `true`; set to `false` to disable) |
| `kill_ring_system_clipboard` | `boolean` | Whether emacs-style kill commands (Ctrl-W, Ctrl-K, etc.) write to the system clipboard (defaults to `true`; set to `false` to use an in-memory kill ring) |
| `providers` | `table` | API provider configuration |
| `models` | `table` | Model configuration |
| `loop_control` | `table` | Agent loop control parameters |
Expand All @@ -54,6 +55,7 @@ theme = "dark"
show_thinking_stream = true
merge_all_available_skills = true
telemetry = true
kill_ring_system_clipboard = true

[providers.kimi-for-coding]
type = "kimi"
Expand Down
2 changes: 2 additions & 0 deletions docs/en/release-notes/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ This page documents the changes in each Kimi Code CLI release.

## Unreleased

- Config: Add `kill_ring_system_clipboard` option — set to `false` to keep emacs-style kill commands (`Ctrl-W`, `Ctrl-K`, `Ctrl-U`, etc.) in an in-memory kill ring instead of overwriting the system clipboard

## 1.43.0 (2026-05-12)

- Security: Bump pillow to 12.2.0 to address CVE-2026-25990 (out-of-bounds write when loading PSD images); unblocks installs in environments that gate on the older pinned version
Expand Down
2 changes: 2 additions & 0 deletions docs/zh/configuration/config-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ kimi --config '{"default_model": "kimi-for-coding", "providers": {...}, "models"
| `show_thinking_stream` | `boolean` | 是否在 Live 区域以 6 行滚动预览方式实时展示模型的原始思考文本,并在 thinking 块结束时把完整思考内容(Markdown)写入历史记录(默认为 `true`;设为 `false` 则仅显示紧凑的 `Thinking ...` 指示器和一行 trace 总结) |
| `merge_all_available_skills` | `boolean` | 是否合并所有品牌目录中的 Skills(默认为 `true`);详见 [Skills 配置](../customization/skills.md) |
| `telemetry` | `boolean` | 是否启用匿名遥测以帮助改进 kimi-cli(默认为 `true`;设为 `false` 可关闭) |
| `kill_ring_system_clipboard` | `boolean` | Emacs 风格的删除命令(Ctrl-W、Ctrl-K 等)是否写入系统剪贴板(默认为 `true`;设为 `false` 则使用内存 kill ring) |
| `providers` | `table` | API 供应商配置 |
| `models` | `table` | 模型配置 |
| `loop_control` | `table` | Agent 循环控制参数 |
Expand All @@ -54,6 +55,7 @@ theme = "dark"
show_thinking_stream = true
merge_all_available_skills = true
telemetry = true
kill_ring_system_clipboard = true

[providers.kimi-for-coding]
type = "kimi"
Expand Down
2 changes: 2 additions & 0 deletions docs/zh/release-notes/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

## 未发布

- Config:新增 `kill_ring_system_clipboard` 配置项——设为 `false` 时,Emacs 风格的删除命令(`Ctrl-W`、`Ctrl-K`、`Ctrl-U` 等)将使用内存 kill ring,不再覆盖系统剪贴板

## 1.43.0 (2026-05-12)

- Security:将 pillow 升级到 12.2.0 以修复 CVE-2026-25990(加载 PSD 图像时存在越界写入);解除在依赖审查严格的环境下因旧版本被阻断而无法安装的限制
Expand Down
10 changes: 10 additions & 0 deletions src/kimi_cli/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,16 @@ class Config(BaseModel):
default=True,
description="Enable anonymous telemetry to help improve kimi-cli. Set to false to disable.",
)
kill_ring_system_clipboard: bool = Field(
default=True,
description=(
"When true (default), emacs-style kill commands (Ctrl-W, Ctrl-K, etc.) "
"write deleted text to the system clipboard. When false, deleted text "
"is discarded and does not pollute the system clipboard. "
"Intentional cut/copy/paste (Ctrl-W with selection, Alt-W, Ctrl-Y, Ctrl-V) "
"continue to use the system clipboard regardless of this setting."
),
)

@model_validator(mode="after")
def validate_model(self) -> Self:
Expand Down
5 changes: 5 additions & 0 deletions src/kimi_cli/ui/shell/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,11 @@ def _bg_task_counts() -> BgTaskCounts:
self.soul.runtime.config.default_editor if isinstance(self.soul, KimiSoul) else ""
),
plan_mode_toggle_callback=_plan_mode_toggle,
kill_ring_system_clipboard=(
self.soul.runtime.config.kill_ring_system_clipboard
if isinstance(self.soul, KimiSoul)
else True
),
) as prompt_session:
self._prompt_session = prompt_session
if self._prefill_text:
Expand Down
69 changes: 68 additions & 1 deletion src/kimi_cli/ui/shell/prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
)
from prompt_toolkit.data_structures import Point
from prompt_toolkit.document import Document
from prompt_toolkit.filters import Condition, has_completions, has_focus, is_done
from prompt_toolkit.filters import Condition, has_completions, has_focus, has_selection, is_done
from prompt_toolkit.formatted_text import AnyFormattedText, FormattedText, to_formatted_text
from prompt_toolkit.history import InMemoryHistory
from prompt_toolkit.key_binding import KeyBindings, KeyPressEvent
Expand Down Expand Up @@ -1185,6 +1185,7 @@ def __init__(
shell_mode_slash_commands: Sequence[SlashCommand[Any]],
editor_command_provider: Callable[[], str] = lambda: "",
plan_mode_toggle_callback: Callable[[], Awaitable[bool]] | None = None,
kill_ring_system_clipboard: bool = True,
) -> None:
history_dir = get_share_dir() / "user-history"
history_dir.mkdir(parents=True, exist_ok=True)
Expand Down Expand Up @@ -1215,6 +1216,7 @@ def __init__(
self._prompt_buffer_container: ConditionalContainer | None = None
self._last_ui_state: PromptUIState = PromptUIState.NORMAL_INPUT
self._suspended_buffer_document: Document | None = None
self._kill_ring_system_clipboard = kill_ring_system_clipboard
clipboard_available = is_clipboard_available()
media_clipboard_available = is_media_clipboard_available()
self._tips = _build_toolbar_tips(clipboard_available or media_clipboard_available)
Expand Down Expand Up @@ -1496,6 +1498,71 @@ def _(event: KeyPressEvent) -> None:
self._insert_pasted_text(event.current_buffer, clipboard_data.text)
event.app.invalidate()

# Override built-in kill bindings so they don't pollute the system
# clipboard when the user has disabled kill-ring-to-system-clipboard.
if not self._kill_ring_system_clipboard:

@_kb.add("c-w", eager=True, filter=~has_selection)
def _(event: KeyPressEvent) -> None:
"""Unix-word-rubout without system clipboard."""
buffer = event.current_buffer
pos = buffer.document.find_start_of_previous_word(count=event.arg, WORD=True)
if pos is None:
pos = -buffer.cursor_position
if pos:
buffer.delete_before_cursor(count=-pos)
else:
Comment thread
sdkks marked this conversation as resolved.
event.app.output.bell()

@_kb.add("c-k", eager=True)
def _(event: KeyPressEvent) -> None:
"""Kill-line without system clipboard."""
buff = event.current_buffer
if event.arg < 0:
buff.delete_before_cursor(count=-buff.document.get_start_of_line_position())
else:
if buff.document.current_char == "\n":
buff.delete(1)
else:
buff.delete(count=buff.document.get_end_of_line_position())

@_kb.add("c-u", eager=True)
def _(event: KeyPressEvent) -> None:
"""Unix-line-discard without system clipboard."""
buff = event.current_buffer
if buff.document.cursor_position_col == 0 and buff.document.cursor_position > 0:
buff.delete_before_cursor(count=1)
else:
buff.delete_before_cursor(count=-buff.document.get_start_of_line_position())

@_kb.add("c-delete", eager=True)
def _(event: KeyPressEvent) -> None:
"""Kill-word (forward) without system clipboard."""
buff = event.current_buffer
pos = buff.document.find_next_word_ending(count=event.arg)
if pos:
buff.delete(count=pos)

@_kb.add("escape", "d", eager=True)
def _(event: KeyPressEvent) -> None:
"""Meta-D: kill-word (forward) without system clipboard."""
buff = event.current_buffer
pos = buff.document.find_next_word_ending(count=event.arg)
if pos:
buff.delete(count=pos)

@_kb.add("escape", "backspace", eager=True)
def _(event: KeyPressEvent) -> None:
"""Meta-Backspace: backward-kill-word without system clipboard."""
buffer = event.current_buffer
pos = buffer.document.find_start_of_previous_word(count=event.arg)
if pos is None:
pos = -buffer.cursor_position
if pos:
buffer.delete_before_cursor(count=-pos)
else:
event.app.output.bell()
Comment thread
sdkks marked this conversation as resolved.

# Only use PyperclipClipboard when pyperclip actually works.
# PromptSession built-in keybindings (ctrl-k, ctrl-w, ctrl-y)
# use clipboard without error handling, so a broken clipboard
Expand Down