feat: expose ponytail as MCP prompts, add missing CLI subcommands - #102
Conversation
/ponytail* stopped being recognized as valid Claude Code slash commands once the DietrichGebert/ponytail marketplace plugin (the only thing registering them) was disabled in favor of agentflare's own routing — Claude Code validates slash commands against its registry before a prompt ever reaches a hook, so disabling the plugin silently broke /ponytail-review, /ponytail-gain, etc. Fix it the way lean-ctx does for its own /lean-ctx* commands: register MCP "prompts" (rmcp's Prompt/GetPromptResult, same crate agentflare already depends on) instead of relying on a plugin or static command files. Adds src/mcp_prompts.rs with 8 prompts (ponytail plus its 7 sub-skills), wires list_prompts/get_prompt into AgentflareMcp's ServerHandler impl, and enables the prompts capability. Also adds the two ponytail CLI subcommands (playbook, no-hallucination) that existed in the crate's sub_skills/switcher but were never wired to `agentflare ponytail <name>`, for parity with review/audit/debt/gain. Verified end-to-end over raw MCP JSON-RPC: initialize reports the prompts capability, prompts/list returns all 8, prompts/get correctly reports/switches mode and returns full skill content.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds MCP prompt listing and retrieval for ponytail modes and sub-skills, wires prompt support into the MCP server, and adds CLI actions for two new ponytail sub-skills. ChangesMCP Prompts feature
Estimated code review effort: 2 (Simple) | ~15 minutes Sequence Diagram(s)sequenceDiagram
participant MCPClient
participant AgentflareMcp
participant mcp_prompts
MCPClient->>AgentflareMcp: list_prompts / get_prompt
AgentflareMcp->>mcp_prompts: delegate request
mcp_prompts-->>AgentflareMcp: Prompt result or None
AgentflareMcp-->>MCPClient: ListPromptsResult / GetPromptResult / invalid_params
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning Tools execution failed with the following error: Failed to run tools: 13 INTERNAL: Received RST_STREAM with code 2 (Internal server error) Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
src/mcp_prompts.rs (1)
12-20: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd drift-protection test between SUB_SKILLS and
ponytail::sub_skills::get.
SUB_SKILLS(names+descriptions) andponytail::sub_skills::get's match arms must stay in sync, orget_ponytail_skill'sunwrap_or_default()silently returns an empty body for a listed prompt. Consider a test assertingponytail::sub_skills::get(name).is_some()for everySUB_SKILLSentry, plus coverage for the "off" and successful lite/full/ultra switch paths inget_ponytail_mode.Also applies to: 85-89, 91-135
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/mcp_prompts.rs` around lines 12 - 20, Add a drift-protection test to keep SUB_SKILLS and ponytail::sub_skills::get in sync: for every entry in SUB_SKILLS, assert that get(name) returns Some so get_ponytail_skill never falls back to an empty body via unwrap_or_default(). Also add coverage around get_ponytail_mode for the “off” case and the successful lite/full/ultra switch paths so the prompt-mode mapping stays locked to the documented modes.src/mcp_server.rs (1)
314-333: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low valueBlocking file I/O inside async handlers.
get_prompt/list_promptsdelegate tomcp_prompts, which does synchronousstd::fsreads/writes (viaponytail::active_mode/set_active) directly on the async task. Given the small scope (local dev tool, single flag file), impact is limited, but wrapping the sync calls intokio::task::spawn_blockingwould be more idiomatic if this server ever serves concurrent requests.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/mcp_server.rs` around lines 314 - 333, The async handlers list_prompts and get_prompt in mcp_server.rs are calling mcp_prompts functions that do synchronous file I/O on the async task. Move the blocking work behind tokio::task::spawn_blocking (or equivalent) and await the result inside list_prompts and get_prompt so the RequestContext<RoleServer> handlers stay non-blocking. Keep the existing ListPromptsResult::with_all_items and ErrorData::invalid_params behavior, just wrap the mcp_prompts::list_prompts and mcp_prompts::get_prompt calls safely.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/mcp_prompts.rs`:
- Around line 74-78: The persistence error from ponytail::set_active is being
ignored, so the mode-switch response can claim success even when the active flag
write fails. Update the mode handling in the relevant match arms in
mcp_prompts::assistant_text flow to handle the Result from ponytail::set_active
explicitly instead of calling .ok(), and surface the I/O failure to the caller
by returning an error/failed response or otherwise preventing the success
message when persistence does not succeed.
---
Nitpick comments:
In `@src/mcp_prompts.rs`:
- Around line 12-20: Add a drift-protection test to keep SUB_SKILLS and
ponytail::sub_skills::get in sync: for every entry in SUB_SKILLS, assert that
get(name) returns Some so get_ponytail_skill never falls back to an empty body
via unwrap_or_default(). Also add coverage around get_ponytail_mode for the
“off” case and the successful lite/full/ultra switch paths so the prompt-mode
mapping stays locked to the documented modes.
In `@src/mcp_server.rs`:
- Around line 314-333: The async handlers list_prompts and get_prompt in
mcp_server.rs are calling mcp_prompts functions that do synchronous file I/O on
the async task. Move the blocking work behind tokio::task::spawn_blocking (or
equivalent) and await the result inside list_prompts and get_prompt so the
RequestContext<RoleServer> handlers stay non-blocking. Keep the existing
ListPromptsResult::with_all_items and ErrorData::invalid_params behavior, just
wrap the mcp_prompts::list_prompts and mcp_prompts::get_prompt calls safely.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 77f888db-d144-4a9d-80b9-0ad1bb7087b6
📒 Files selected for processing (4)
src/cli/ponytail.rssrc/main.rssrc/mcp_prompts.rssrc/mcp_server.rs
CodeRabbit: set_active(...).ok() discarded persistence errors, so a prompt could report a mode switch (or return skill content) even when the underlying flag-file write failed, leaving reported state inconsistent with actual state.
Two independent additions to the init component pattern, both aimed at replacing Claude Code's always-on skill-description listing with agentflare's on-demand skill_search/skill_load (already merged: #102 exposes them as MCP tools, #103 nudges the model toward them): - agentflare-mcp: registers agentflare's own MCP server (skill_search/ skill_load) with claude-code (native `claude mcp add`), cline, continue, and opencode — same 4 hosts + same merge_json/ merge_opencode_mcp/write_if_absent helpers the engram component already uses for its own manual-registration branches. Other hosts report satisfied until their MCP config format is verified here. - skill-overrides-sync: Claude Code only (other agents have no equivalent per-skill mechanism). Scans every skill agentflare's registry knows about (crates/skill-registry gets a new Registry::list_all_names(), since search() requires a query and there was no "list everything" path) and sets settings.json:skillOverrides["<name>"] = "name-only" for any skill missing an entry — Claude Code's own mechanism for listing a skill's name without its description (verified against the live settings schema, not assumed). Never touches a skill the user already configured differently. Not consent-gated, so it also re-syncs on every session-start as new skills appear, not just during `init`. crates/skill-registry's `skills_db_path()` was duplicated between mcp_server.rs and the new component; moved to paths.rs as the single shared definition. Verified end-to-end: fresh `agentflare init --agent claude-code` registered the MCP server (`claude mcp list` shows it connected) and set 57 skills to name-only in one pass; re-running init is a clean no-op on both.
Summary
Found via dogfooding:
/ponytail*stopped working as Claude Code slash commands once theDietrichGebert/ponytailmarketplace plugin (the only thing registering them) was disabled in favor of agentflare's own hook-based routing. Claude Code validates slash commands against its registry before a prompt ever reaches a hook, so disabling the plugin silently broke/ponytail-review,/ponytail-gain, etc. with "Unknown command" errors.Fixed the way lean-ctx does it for its own
/lean-ctx*commands: register MCP prompts (rmcp::model::Prompt/GetPromptResult— same crate agentflare already depends on) instead of relying on a plugin or static command files.src/mcp_prompts.rs: 8 prompts (ponytail+ its 7 sub-skills: review/audit/debt/gain/help/playbook/no-hallucination).list_prompts/get_promptintoAgentflareMcp'sServerHandlerimpl, enables thepromptscapability.ponytail(bare) takes an optionalmodeargument: empty/statusreports current mode,lite|full|ultraswitches and returns the mode's skill body,offclears it, anything else reports an error — mirroring the original hook'sSwitchActionsemantics.ponytail::set_active) then returns the full skill markdown, matching what the dedicated/ponytail-reviewetc. commands did originally.playbook,no-hallucination) that existed in the crate'ssub_skills/switchermodules but were never wired toagentflare ponytail <name>, for parity withreview/audit/debt/gain.Test plan
cargo build --workspace,cargo test --workspace(195 passed, +5 new)cargo clippy --workspace --all-targets— no new findingsinitializereports thepromptscapability,prompts/listreturns all 8,prompts/getcorrectly reports/switches mode and returns full skill content for both the bareponytailprompt and a sub-skill (ponytail-review)agentflare mcpregistered and confirm/ponytail*appear in the slash-command autocompleteSummary by CodeRabbit