Skip to content

Feature Request: Extensibility APIs for Community-Built Statusline / HUD Plugins #2316

@lNeverl

Description

@lNeverl

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:

  1. A persistent-process plugin that lives for the whole session.
  2. A continuous data stream of internal state (context, tools, agents, todos).
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions