Skip to content
Merged
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

- Shell: Show active background agent task count in the prompt status bar — the existing `⚙ bash: N` badge only counted background Shell tasks and filtered out background Agent subagents, so when many subagents were running the prompt looked idle and users could not tell work was in progress; the toolbar now renders `⚙ bash: N` and `⚙ agent: N` as two independent badges (each hidden when its count is 0) and drops the agent badge first when the terminal is too narrow to fit both

## 1.39.0 (2026-04-24)

- Skill: Fix project-scope skills being ignored and user-scope skills silently winning name conflicts — the system prompt now groups discovered skills under `### Project` / `### User` / `### Extra` / `### Built-in` headings so the model can tell where each skill came from, and when the same name exists in multiple scopes the more specific scope wins (Project > User > Extra > Built-in) so a project's own `.kimi/skills/foo` or `.claude/skills/foo` correctly overrides a user-level or bundled `foo` instead of the other way around
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

- Shell: Show active background agent task count in the prompt status bar — the existing `⚙ bash: N` badge only counted background Shell tasks and filtered out background Agent subagents, so when many subagents were running the prompt looked idle and users could not tell work was in progress; the toolbar now renders `⚙ bash: N` and `⚙ agent: N` as two independent badges (each hidden when its count is 0) and drops the agent badge first when the terminal is too narrow to fit both

## 1.39.0 (2026-04-24)

- Skill: Fix project-scope skills being ignored and user-scope skills silently winning name conflicts — the system prompt now groups discovered skills under `### Project` / `### User` / `### Extra` / `### Built-in` headings so the model can tell where each skill came from, and when the same name exists in multiple scopes the more specific scope wins (Project > User > Extra > Built-in) so a project's own `.kimi/skills/foo` or `.claude/skills/foo` correctly overrides a user-level or bundled `foo` instead of the other way around
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 @@

## 未发布

- Shell:在提示框状态栏显示当前正在运行的后台 Agent 任务数——原有的 `⚙ bash: N` 徽章只统计后台 Shell 任务,把后台 Agent 子代理过滤掉了,所以多个子代理同时在跑时提示框看起来像空闲,用户无法判断工作是否还在进行;现在状态栏会渲染 `⚙ bash: N` 与 `⚙ agent: N` 两个相互独立的徽章(任一计数为 0 时自动隐藏),终端太窄无法同时容纳两者时优先丢弃 agent 徽章

## 1.39.0 (2026-04-24)

- Skill:修复项目级 Skill 被忽略、用户级 Skill 在同名冲突时静默获胜的问题——系统提示现在会把发现到的 Skill 按 `### Project` / `### User` / `### Extra` / `### Built-in` 四个分组呈现,让模型能分辨出每个 Skill 来自哪一层;当同一 Skill 名称同时存在于多个作用域时,越具体的作用域优先(Project > User > Extra > Built-in),项目自身的 `.kimi/skills/foo` 或 `.claude/skills/foo` 现在能正确覆盖用户级或内置的同名 `foo`,而不是被它们覆盖
Expand Down
17 changes: 10 additions & 7 deletions src/kimi_cli/ui/shell/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from kimi_cli.ui.shell.echo import render_user_echo_text
from kimi_cli.ui.shell.mcp_status import render_mcp_prompt
from kimi_cli.ui.shell.prompt import (
BgTaskCounts,
CustomPromptSession,
CwdLostError,
PromptMode,
Expand Down Expand Up @@ -416,26 +417,28 @@ def _mcp_status_loading() -> bool:
@dataclass
class _BgCountCache:
time: float = 0.0
count: int = 0
counts: BgTaskCounts = BgTaskCounts()

_bg_cache = _BgCountCache()

def _bg_task_count() -> int:
def _bg_task_counts() -> BgTaskCounts:
if not isinstance(self.soul, KimiSoul):
return 0
return BgTaskCounts()
now = time.monotonic()
if now - _bg_cache.time < 1.0:
return _bg_cache.count
return _bg_cache.counts
views = list_task_views(self.soul.runtime.background_tasks, active_only=True)
_bg_cache.count = sum(1 for v in views if v.spec.kind == "bash")
bash_n = sum(1 for v in views if v.spec.kind == "bash")
agent_n = sum(1 for v in views if v.spec.kind == "agent")
_bg_cache.counts = BgTaskCounts(bash=bash_n, agent=agent_n)
_bg_cache.time = now
return _bg_cache.count
return _bg_cache.counts

with CustomPromptSession(
status_provider=lambda: self.soul.status,
status_block_provider=_mcp_status_block,
fast_refresh_provider=_mcp_status_loading,
background_task_count_provider=_bg_task_count,
background_task_count_provider=_bg_task_counts,
model_capabilities=self.soul.model_capabilities or set(),
model_name=model_display_name(
self.soul.model_name,
Expand Down
31 changes: 22 additions & 9 deletions src/kimi_cli/ui/shell/prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -1094,6 +1094,12 @@ def should_handle_running_prompt_key(self, key: str) -> bool: ...
def handle_running_prompt_key(self, key: str, event: KeyPressEvent) -> None: ...


@dataclass(frozen=True, slots=True)
class BgTaskCounts:
bash: int = 0
agent: int = 0


@runtime_checkable
class AgentStatusProvider(Protocol):
"""Optional protocol for delegates that render always-visible agent status.
Expand Down Expand Up @@ -1170,7 +1176,7 @@ def __init__(
status_provider: Callable[[], StatusSnapshot],
status_block_provider: Callable[[int], AnyFormattedText | None] | None = None,
fast_refresh_provider: Callable[[], bool] | None = None,
background_task_count_provider: Callable[[], int] | None = None,
background_task_count_provider: Callable[[], BgTaskCounts] | None = None,
model_capabilities: set[ModelCapability],
model_name: str | None,
thinking: bool,
Expand Down Expand Up @@ -2149,16 +2155,23 @@ def _render_bottom_toolbar(self) -> FormattedText:
fragments.extend([(tc.cwd, cwd_text), ("", " ")])
remaining -= cwd_w + 2

# Active background bash task count
bg_count = (
self._background_task_count_provider() if self._background_task_count_provider else 0
# Active background task counts (bash + agent, each rendered as its own
# badge). Order matters: bash renders first; if there isn't room for the
# agent badge too, drop agent and keep bash.
bg_counts = (
self._background_task_count_provider()
if self._background_task_count_provider
else BgTaskCounts()
)
if bg_count > 0:
bg_text = f"⚙ bash: {bg_count}"
for kind_label, kind_count in (("bash", bg_counts.bash), ("agent", bg_counts.agent)):
if kind_count <= 0:
continue
bg_text = f"⚙ {kind_label}: {kind_count}"
bg_width = _display_width(bg_text)
if remaining >= bg_width + 2:
fragments.extend([(tc.bg_tasks, bg_text), ("", " ")])
remaining -= bg_width + 2
if remaining < bg_width + 2:
break
fragments.extend([(tc.bg_tasks, bg_text), ("", " ")])
Comment on lines +2166 to +2173
remaining -= bg_width + 2

# Tips fill remaining space on line 1
tip_text = self._get_two_rotating_tips()
Expand Down
43 changes: 42 additions & 1 deletion tests/ui_and_conv/test_prompt_tips.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from kimi_cli.ui.shell.prompt import (
_GIT_STATUS_TTL,
PROMPT_SYMBOL,
BgTaskCounts,
CustomPromptSession,
PromptMode,
UserInput,
Expand Down Expand Up @@ -332,7 +333,7 @@ def test_bottom_toolbar_narrow_terminal_with_full_decoration(width: int, monkeyp
model_name="kimi-latest",
tips=["ctrl-x: toggle mode"],
)
prompt_session._background_task_count_provider = lambda: 2
prompt_session._background_task_count_provider = lambda: BgTaskCounts(bash=2, agent=0)

lines = _render_toolbar_lines(
prompt_session,
Expand All @@ -352,6 +353,46 @@ def test_bottom_toolbar_narrow_terminal_with_full_decoration(width: int, monkeyp
)


def test_bottom_toolbar_shows_bash_and_agent_badges_together(monkeypatch: Any) -> None:
prompt_session = _make_toolbar_session(tips=[])
prompt_session._background_task_count_provider = lambda: BgTaskCounts(bash=3, agent=1)

lines = _render_toolbar_lines(prompt_session, 120, monkeypatch)

assert "⚙ bash: 3" in lines[1], f"bash badge missing: {lines[1]!r}"
assert "⚙ agent: 1" in lines[1], f"agent badge missing: {lines[1]!r}"
assert lines[1].index("⚙ bash: 3") < lines[1].index("⚙ agent: 1"), (
f"bash badge must come before agent badge: {lines[1]!r}"
)


def test_bottom_toolbar_shows_agent_badge_alone_when_no_bash(monkeypatch: Any) -> None:
prompt_session = _make_toolbar_session(tips=[])
prompt_session._background_task_count_provider = lambda: BgTaskCounts(bash=0, agent=2)

lines = _render_toolbar_lines(prompt_session, 120, monkeypatch)

assert "⚙ bash" not in lines[1], f"bash badge must not appear when count is 0: {lines[1]!r}"
assert "⚙ agent: 2" in lines[1], f"agent badge missing: {lines[1]!r}"


def test_bottom_toolbar_drops_agent_badge_before_bash_when_narrow(monkeypatch: Any) -> None:
# With only ~width budget for one badge after CWD/mode, keeping bash and
# dropping agent is the documented priority.
prompt_session = _make_toolbar_session(tips=[])
prompt_session._background_task_count_provider = lambda: BgTaskCounts(bash=5, agent=5)

lines = _render_toolbar_lines(prompt_session, 40, monkeypatch)

# Must never overflow and the bash badge is preferred over the agent badge.
assert _display_width(lines[1]) <= 40
if "⚙ agent" in lines[1]:
# Only acceptable if bash also fit — otherwise priority is violated.
assert "⚙ bash" in lines[1], (
f"agent badge appeared without bash badge at narrow width: {lines[1]!r}"
)
Comment on lines +380 to +393


def test_mode_shows_full_with_model_name_on_wide_terminal(monkeypatch: Any) -> None:
"""On a wide terminal the full mode string (with model name and thinking dot) is shown."""
session = _make_toolbar_session(model_name="fast-model")
Expand Down
Loading