feat(plugin): add Claude-compatible local plugin support#1715
feat(plugin): add Claude-compatible local plugin support#1715GTC2080 wants to merge 31 commits intoMoonshotAI:mainfrom
Conversation
7a227cd to
6f27859
Compare
PR MoonshotAI#1715 review fixes: 1. app.py: The DEFAULT_AGENT_FILE fallback now only triggers when a plugin agent was actually matched. Previously, any .md agent_file was silently replaced with DEFAULT_AGENT_FILE whenever Claude plugins were loaded, even for non-plugin Markdown agents. 2. kimisoul.py: _make_skill_runner() error text now shows the real slash command for plugin skills (/myplugin:greet) instead of the native form (/skill:myplugin:greet). Native skills still show /skill:name as before. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PR MoonshotAI#1715 review fixes: 1. app.py: The DEFAULT_AGENT_FILE fallback now only triggers when a plugin agent was actually matched. Previously, any .md agent_file was silently replaced with DEFAULT_AGENT_FILE whenever Claude plugins were loaded, even for non-plugin Markdown agents. 2. kimisoul.py: _make_skill_runner() error text now shows the real slash command for plugin skills (/myplugin:greet) instead of the native form (/skill:myplugin:greet). Native skills still show /skill:name as before.
41b7a0f to
2ffa895
Compare
PR MoonshotAI#1715 review fixes: 1. app.py: The DEFAULT_AGENT_FILE fallback now only triggers when a plugin agent was actually matched. Previously, any .md agent_file was silently replaced with DEFAULT_AGENT_FILE whenever Claude plugins were loaded, even for non-plugin Markdown agents. 2. kimisoul.py: _make_skill_runner() error text now shows the real slash command for plugin skills (/myplugin:greet) instead of the native form (/skill:myplugin:greet). Native skills still show /skill:name as before.
A non-integer timeout value (e.g. "bad") in hooks/hooks.json caused ValueError to propagate up through _load_single_plugin, skipping the entire plugin including its skills, commands, agents, and MCP configs. Fix: wrap timeout parsing in its own try/except before the HookDef construction. Invalid timeouts produce a warning and skip only that hook entry; the rest of the plugin loads normally.
A relative --agent-file path (e.g. agents/reviewer.md) failed the is_relative_to(plugin_rt.root) check because plugin roots are absolute. The file was then passed to the YAML-only loader and crashed. Fix: resolve agent_file to an absolute path before comparing against plugin roots. The resolved path is also passed to parse_agent_md() so file reads work correctly.
2ffa895 to
bff581a
Compare
hooks/hooks.json, .mcp.json, and settings.json could all be valid JSON but have a non-object root (e.g. []). Calling .get() on a list raises AttributeError, which propagated up and skipped the entire plugin. Fix: add isinstance(raw, dict) check before accessing .get() in all three loaders. Non-object roots produce a warning and return early, leaving the rest of the plugin's capabilities unaffected.
1. app.py: wrap parse_agent_md() in try/except so a plugin agent with broken frontmatter logs a warning and skips the overlay instead of crashing KimiCLI.create(). 2. discovery.py: wrap base.iterdir() in try/except OSError so an unreadable ~/.kimi/claude-plugins/ directory returns [] instead of propagating the exception to CLI startup.
|
Thanks — the latest review findings have been addressed on It looks like the remaining GitHub Actions runs for this fork PR are still waiting for maintainer approval ( |
When two skills/*/SKILL.md files declare the same frontmatter name, the second one was silently overwriting the first in runtime.skills. Add duplicate detection (matching the existing agent pattern) so conflicting skill definitions are surfaced with a warning and the first-registered skill wins.
Replace plain header matching with explicit HTML comment sentinels (KIMI_PLUGIN_CAPABILITY_SUMMARY_BEGIN/END) to prevent accidentally truncating user prompts that happen to contain the same heading text.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a2155aba4a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| Returns an empty string when there are no advisory fields to surface. | ||
| """ | ||
| advisory: list[str] = [] | ||
| for key in sorted(spec.frontmatter): |
There was a problem hiding this comment.
Guard advisory key sorting against non-string YAML keys
build_frontmatter_context iterates sorted(spec.frontmatter), but plugin frontmatter parsing only enforces that the root is a mapping, not that all keys are strings. A command file with mixed key types (for example description plus a numeric/boolean YAML key) will raise TypeError during sorting, and invoking that plugin command then aborts the command execution path instead of failing open. Please normalize/filter keys to strings before sorting or iterate without cross-type ordering.
Useful? React with 👍 / 👎.
Related Issue
Resolve #1714
Description
This draft PR adds a local plugin compatibility layer that allows Kimi Code CLI to discover and use local Claude Plugins.
Key pieces in this PR:
--plugin-dir.claude-plugin/plugin.jsonand load supported plugin bundlesskills/,commands/,agents/,hooks/hooks.json,.mcp.json, andsettings.json(agentonly)settings.jsonTest coverage added/updated for:
Verification run locally:
uv run pytest tests/core/test_claude_plugin_discovery.py tests/core/test_claude_plugin_commands.py tests/core/test_claude_plugin_agents.py tests/core/test_claude_plugin_hooks.py tests/core/test_claude_plugin_mcp.py tests/core/test_claude_plugin_router.py tests/core/test_claude_plugin_capability_summary.py tests/core/test_kimisoul_slash_commands.py tests/core/test_load_agent.py -quv run ruff check src/kimi_cli/claude_plugin src/kimi_cli/soul/kimisoul.py src/kimi_cli/app.py tests/core/test_claude_plugin_discovery.py tests/core/test_claude_plugin_commands.py tests/core/test_claude_plugin_agents.py tests/core/test_claude_plugin_hooks.py tests/core/test_claude_plugin_mcp.py tests/core/test_claude_plugin_router.py tests/core/test_claude_plugin_capability_summary.py tests/core/test_kimisoul_slash_commands.py tests/core/test_load_agent.pyuv run pyright src/kimi_cli/claude_plugin src/kimi_cli/soul/kimisoul.py src/kimi_cli/app.py tests/core/test_claude_plugin_discovery.py tests/core/test_claude_plugin_commands.py tests/core/test_claude_plugin_agents.py tests/core/test_claude_plugin_hooks.py tests/core/test_claude_plugin_mcp.py tests/core/test_claude_plugin_router.py tests/core/test_claude_plugin_capability_summary.py tests/core/test_kimisoul_slash_commands.py tests/core/test_load_agent.pyuv run kimi --print --prompt /skill:gen-changeloguv run kimi --print --prompt /skill:gen-docsChecklist
make gen-changelogto update the changelog.make gen-docsto update the user documentation.