Feature Request: Extensibility APIs for Community-Built Statusline / HUD Plugins
I want to build a HUD plugin myself (like claude-hud). This issue asks for the extension interfaces that would make it possible — not for the Kimi team to build or maintain a HUD.
Motivation
Claude Code's claude-hud shows a persistent status bar at the bottom of the terminal with context usage, live tool activity, running agents, and todo progress. I want to build a similar plugin for Kimi Code CLI.
After reading the source, I found that the current plugin and hooks architecture cannot support this. The HUD needs three things Kimi CLI does not yet provide:
- A persistent-process plugin that lives for the whole session.
- A continuous data stream of internal state (context, tools, agents, todos).
- A way to render text into the bottom toolbar.
This issue proposes concrete API changes to enable community plugins like mine. The HUD itself (colors, layout, progress bars, config UI) stays entirely in userland.
How claude-hud Works
Claude Code --stdin JSON--> claude-hud process --stdout--> terminal statusline
\-- transcript JSONL (tools, agents, todos)
- Plugin runs as a persistent process for the entire session.
- Claude Code pushes JSON to the plugin's stdin (~300 ms interval) with context usage, rate limits, and transcript events.
- Plugin prints formatted text to stdout; Claude Code renders it in the statusline area.
- Plugin registers slash commands like
/claude-hud:config for user interaction.
What's Missing in Kimi CLI
| Missing |
Why it blocks a HUD plugin |
| No persistent-process plugin type |
Current plugins spawn a subprocess per AI tool call (PluginTool.__call__ creates asyncio.create_subprocess_exec, runs once, exits). A HUD needs a long-running process. |
| No continuous data stream to plugins |
Hooks fire events, but each invocation is an independent shell command with stdin JSON + exit code. No channel streams StatusUpdate-like payloads to a running plugin. |
| No toolbar rendering extension point |
_render_bottom_toolbar in prompt.py:2099 is hard-coded. Plugins cannot inject text into the terminal status bar. |
| No plugin-registered slash commands |
plugin.json only declares tools (for AI to call). Cannot declare user-facing slash commands. |
| No in-flight tool list |
PostToolUse fires after completion. No event signals "tool X is currently executing". |
| No todo state exposure |
SetTodoList stores todos in SessionState.todos (session_state.py:44), but this state is not emitted to plugins. |
| No usage/rate-limit data |
Searching rate_limit, cost.total_cost_usd in the codebase yields nothing. Not a blocker for a basic HUD, but limits feature parity. |
Proposed API Changes
1. Extend plugin.json schema
Current PluginSpec (plugin/__init__.py:30) only supports "tools".
Proposed additions:
{
"name": "kimi-hud",
"version": "1.0.0",
"type": "statusline",
"command": ["node", "dist/main.js"],
"config_file": "config.json",
"inject": { "api_key": "api_key" },
"slash_commands": [
{ "name": "kimi-hud:config", "description": "Configure HUD" }
]
}
New fields:
"type": enum ["tool" (default), "statusline"].
"slash_commands": array of {name, description}. Only meaningful for statusline plugins.
2. Persistent-process lifecycle
Current (plugin/tool.py:80):
proc = await asyncio.create_subprocess_exec(...)
stdout, stderr = await proc.communicate(input=params_json.encode())
Process exits after one invocation.
Required: CLI starts the process once per session during KimiCLI.create() (app.py:274, where plugin configs are already refreshed), keeps stdin open, writes JSON Lines on state changes, reads stdout as statusline text, and kills the process on shutdown (shutdown_background_tasks() or similar).
Suggested: new StatuslinePlugin class in src/kimi_cli/plugin/statusline.py.
3. Toolbar rendering hook (prompt.py)
Current (prompt.py:1521):
self._session = PromptSession[str](
...,
bottom_toolbar=self._render_bottom_toolbar,
)
_render_bottom_toolbar (line 2099) is entirely hard-coded.
Options (pick what fits best):
- A — Safest: Allow
Shell to accept an optional statusline_provider: Callable[[], str]. _render_bottom_toolbar appends the returned string as an extra line below the existing two. Default toolbar untouched.
- B — Minimal: Let a plugin override the right span of line 2 (currently
format_context_status(...)).
- C — Full delegation: If a statusline plugin is active, skip
_render_bottom_toolbar entirely and render only the plugin's stdout. Maximum control, but plugin must re-implement git/branch/tips if wanted.
I prefer A for backward compatibility.
4. Data stream schema
CLI writes a JSON Line to the plugin's stdin whenever state changes:
{
"event": "statusline_tick",
"session_id": "abc-123",
"cwd": "/home/user/project",
"model": "kimi-for-coding",
"mode": "agent",
"flags": {
"yolo": false,
"afk": false,
"plan_mode": false,
"thinking": true
},
"context": {
"usage_ratio": 0.45,
"tokens_used": 45000,
"tokens_max": 100000
},
"git": {
"branch": "main",
"dirty": true,
"ahead": 0,
"behind": 0
},
"background_tasks": { "bash": 0, "agent": 1 },
"mcp": { "loading": false, "servers": 3 },
"tools_in_flight": [
{ "name": "ReadFile", "call_id": "tc-1", "target": "src/auth.ts" }
],
"agents": [
{ "name": "explore", "subagent_type": "explore", "status": "running", "elapsed_s": 135 }
],
"todos": [
{ "title": "Fix auth bug", "status": "in_progress" },
{ "title": "Update docs", "status": "pending" }
]
}
5. Internal state exposure map
| Data field |
Source in codebase |
Exposure needed |
Effort |
session_id |
Runtime.session.id (agent.py) |
Include in tick |
Trivial |
model |
KimiSoul.model_name (kimisoul.py) |
Read property |
Trivial |
mode |
Shell._mode (ui/shell/__init__.py) |
Pass into tick |
Trivial |
flags.* |
StatusSnapshot (soul/__init__.py:86) |
Serialize existing struct |
Trivial |
context.* |
StatusSnapshot (soul/__init__.py:86) |
Serialize existing struct |
Trivial |
git.* |
_get_git_branch() / _get_git_status() (prompt.py:913/954) |
Extract into reusable helper |
Small |
background_tasks |
BgTaskCounts (prompt.py:1184) |
Pass through |
Trivial |
mcp |
MCPStatusSnapshot (wire/types.py:166) |
Already in StatusSnapshot |
Trivial |
tools_in_flight |
KimiToolset._current_step_calls (toolset.py:135) |
New — expose current step's calls |
Medium |
agents |
Runtime.subagent_store (agent.py:189) |
New — emit running instances |
Medium |
todos |
SessionState.todos (session_state.py:44) |
New — read root/subagent state |
Medium |
usage_limits |
Not implemented |
Optional future |
N/A |
6. Plugin slash commands
Current: KimiSoul._build_slash_commands() (kimisoul.py:731) iterates soul_slash_registry plus skills. No plugin hook.
Proposed: Also iterate statusline plugins that declared slash_commands. Create SlashCommand wrappers that forward to the plugin's stdin:
for plugin in statusline_plugins:
for cmd in plugin.spec.slash_commands:
commands.append(SlashCommand(
name=cmd.name,
func=plugin.forward_slash(cmd.name),
description=cmd.description,
))
IPC format:
{"event": "slash_command", "command": "kimi-hud:config", "args": "theme dark"}
Plugin responds via stdout (captured and shown in chat) or updates its statusline output.
7. In-flight tools
Current: HookEngine has PreToolUse (can block) and PostToolUse (after success). No "tool is running" signal.
Options:
- Add a
ToolBegin hook/event, fired at toolset.py:292 right after the PreToolUse check and before await tool.call(arguments).
- Or simpler: read
KimiToolset._current_step_calls (toolset.py:135) and _current_step_tasks (toolset.py:137) when building the statusline tick payload.
Why Workarounds Don't Work
- Wire mode (
kimi --wire): Replaces the entire Shell UI. Not a plugin; it's a full client rewrite.
- Hooks-only: Hooks are fire-and-forget shell commands, not a structured stream. And there is still no way to render into the bottom toolbar.
Environment
- Kimi Code CLI version: latest (source inspected around 2026-05)
- OS: Windows / macOS / Linux
I'm happy to prototype the plugin side and test early implementations once a direction is chosen.
Feature Request: Extensibility APIs for Community-Built Statusline / HUD Plugins
Motivation
Claude Code's claude-hud shows a persistent status bar at the bottom of the terminal with context usage, live tool activity, running agents, and todo progress. I want to build a similar plugin for Kimi Code CLI.
After reading the source, I found that the current plugin and hooks architecture cannot support this. The HUD needs three things Kimi CLI does not yet provide:
This issue proposes concrete API changes to enable community plugins like mine. The HUD itself (colors, layout, progress bars, config UI) stays entirely in userland.
How claude-hud Works
/claude-hud:configfor user interaction.What's Missing in Kimi CLI
PluginTool.__call__createsasyncio.create_subprocess_exec, runs once, exits). A HUD needs a long-running process.StatusUpdate-like payloads to a running plugin._render_bottom_toolbarinprompt.py:2099is hard-coded. Plugins cannot inject text into the terminal status bar.plugin.jsononly declarestools(for AI to call). Cannot declare user-facing slash commands.PostToolUsefires after completion. No event signals "tool X is currently executing".SetTodoListstores todos inSessionState.todos(session_state.py:44), but this state is not emitted to plugins.rate_limit,cost.total_cost_usdin the codebase yields nothing. Not a blocker for a basic HUD, but limits feature parity.Proposed API Changes
1. Extend
plugin.jsonschemaCurrent
PluginSpec(plugin/__init__.py:30) only supports"tools".Proposed additions:
{ "name": "kimi-hud", "version": "1.0.0", "type": "statusline", "command": ["node", "dist/main.js"], "config_file": "config.json", "inject": { "api_key": "api_key" }, "slash_commands": [ { "name": "kimi-hud:config", "description": "Configure HUD" } ] }New fields:
"type": enum["tool" (default), "statusline"]."slash_commands": array of{name, description}. Only meaningful for statusline plugins.2. Persistent-process lifecycle
Current (
plugin/tool.py:80):Process exits after one invocation.
Required: CLI starts the process once per session during
KimiCLI.create()(app.py:274, where plugin configs are already refreshed), keepsstdinopen, writes JSON Lines on state changes, readsstdoutas statusline text, and kills the process on shutdown (shutdown_background_tasks()or similar).Suggested: new
StatuslinePluginclass insrc/kimi_cli/plugin/statusline.py.3. Toolbar rendering hook (
prompt.py)Current (
prompt.py:1521):_render_bottom_toolbar(line 2099) is entirely hard-coded.Options (pick what fits best):
Shellto accept an optionalstatusline_provider: Callable[[], str]._render_bottom_toolbarappends the returned string as an extra line below the existing two. Default toolbar untouched.format_context_status(...))._render_bottom_toolbarentirely and render only the plugin's stdout. Maximum control, but plugin must re-implement git/branch/tips if wanted.I prefer A for backward compatibility.
4. Data stream schema
CLI writes a JSON Line to the plugin's stdin whenever state changes:
{ "event": "statusline_tick", "session_id": "abc-123", "cwd": "/home/user/project", "model": "kimi-for-coding", "mode": "agent", "flags": { "yolo": false, "afk": false, "plan_mode": false, "thinking": true }, "context": { "usage_ratio": 0.45, "tokens_used": 45000, "tokens_max": 100000 }, "git": { "branch": "main", "dirty": true, "ahead": 0, "behind": 0 }, "background_tasks": { "bash": 0, "agent": 1 }, "mcp": { "loading": false, "servers": 3 }, "tools_in_flight": [ { "name": "ReadFile", "call_id": "tc-1", "target": "src/auth.ts" } ], "agents": [ { "name": "explore", "subagent_type": "explore", "status": "running", "elapsed_s": 135 } ], "todos": [ { "title": "Fix auth bug", "status": "in_progress" }, { "title": "Update docs", "status": "pending" } ] }5. Internal state exposure map
session_idRuntime.session.id(agent.py)modelKimiSoul.model_name(kimisoul.py)modeShell._mode(ui/shell/__init__.py)flags.*StatusSnapshot(soul/__init__.py:86)context.*StatusSnapshot(soul/__init__.py:86)git.*_get_git_branch()/_get_git_status()(prompt.py:913/954)background_tasksBgTaskCounts(prompt.py:1184)mcpMCPStatusSnapshot(wire/types.py:166)StatusSnapshottools_in_flightKimiToolset._current_step_calls(toolset.py:135)agentsRuntime.subagent_store(agent.py:189)todosSessionState.todos(session_state.py:44)usage_limits6. Plugin slash commands
Current:
KimiSoul._build_slash_commands()(kimisoul.py:731) iteratessoul_slash_registryplus skills. No plugin hook.Proposed: Also iterate statusline plugins that declared
slash_commands. CreateSlashCommandwrappers that forward to the plugin's stdin:IPC format:
{"event": "slash_command", "command": "kimi-hud:config", "args": "theme dark"}Plugin responds via stdout (captured and shown in chat) or updates its statusline output.
7. In-flight tools
Current:
HookEnginehasPreToolUse(can block) andPostToolUse(after success). No "tool is running" signal.Options:
ToolBeginhook/event, fired attoolset.py:292right after thePreToolUsecheck and beforeawait tool.call(arguments).KimiToolset._current_step_calls(toolset.py:135) and_current_step_tasks(toolset.py:137) when building the statusline tick payload.Why Workarounds Don't Work
kimi --wire): Replaces the entire Shell UI. Not a plugin; it's a full client rewrite.Environment
I'm happy to prototype the plugin side and test early implementations once a direction is chosen.