""" Skill Forge — a Gradio app + MCP server for authoring Agent Skills. Tabs: validate a SKILL.md, score its `description` for trigger reliability, and scaffold a new skill. The same three functions are exposed as MCP tools (`skill_validate`, `skill_lint_description`, `skill_scaffold`) so an agent can call them while it writes skills for itself. Run locally: python app.py (UI on :7860, MCP at /gradio_api/mcp/sse) """ import gradio as gr from skillforge import validate_skill, lint_description, scaffold_skill, package_skill EXAMPLE = """--- name: changelog-writer description: Drafts a release changelog from merged PRs. Use when the user asks to \ "write the changelog", "summarize what shipped", or prep release notes for a tag. --- # Changelog Writer ## When to use this skill - The user is cutting a release and wants notes grouped by type. ## Instructions 1. Collect merged PRs since the last tag. 2. Group as Features / Fixes / Internal and write one line each. """ def skill_validate(skill_md: str) -> str: """Validate a SKILL.md against the Agent Skill format. Args: skill_md: Full text of a SKILL.md file (YAML frontmatter + Markdown body). Returns: A Markdown report listing errors, warnings, and info. """ return validate_skill(skill_md).as_markdown() def skill_lint_description(description: str) -> str: """Score a skill `description` for how reliably an agent will trigger on it. Args: description: The frontmatter `description` string on its own. Returns: A Markdown report ending with 'Trigger score: N/100'. """ return lint_description(description).as_markdown() def skill_scaffold(name: str, description: str, when_to_use: str = "") -> str: """Generate a ready-to-edit SKILL.md for a new Agent Skill. Args: name: kebab-case skill name, e.g. 'pdf-form-filler'. description: What the skill does and when to use it. when_to_use: Optional extra trigger examples. Returns: The full text of a SKILL.md file. """ return scaffold_skill(name, description, when_to_use) def _package(skill_md: str): data = package_skill(skill_md) path = "skill.zip" with open(path, "wb") as f: f.write(data) return path with gr.Blocks(title="Skill Forge") as demo: gr.Markdown( "# 🛠️ Skill Forge\n" "Validate, lint, and scaffold **Agent Skills** (`SKILL.md`). " "Also an MCP server — point your agent at `/gradio_api/mcp/sse`." ) with gr.Tab("Validate"): md_in = gr.Code(value=EXAMPLE, language="yaml", label="SKILL.md", lines=20) with gr.Row(): v_btn = gr.Button("Validate", variant="primary") z_btn = gr.Button("Package as .zip") v_out = gr.Markdown() z_out = gr.File(label="Packaged skill", visible=True) v_btn.click(skill_validate, md_in, v_out, api_name="skill_validate") z_btn.click(_package, md_in, z_out) with gr.Tab("Lint description"): d_in = gr.Textbox( label="Frontmatter `description`", lines=4, value=('Drafts a release changelog from merged PRs. Use when the user ' 'asks to "write the changelog" or prep release notes.')) d_btn = gr.Button("Score it", variant="primary") d_out = gr.Markdown() d_btn.click(skill_lint_description, d_in, d_out, api_name="skill_lint_description") with gr.Tab("Scaffold"): s_name = gr.Textbox(label="name (kebab-case)", value="terraform-plan-reviewer") s_desc = gr.Textbox(label="description", lines=3, value="Reviews a Terraform plan and summarizes drift and risk.") s_when = gr.Textbox(label="when to use (optional)", value='the user runs "terraform plan" and wants a risk summary') s_btn = gr.Button("Generate SKILL.md", variant="primary") s_out = gr.Code(language="yaml", label="SKILL.md") s_btn.click(skill_scaffold, [s_name, s_desc, s_when], s_out, api_name="skill_scaffold") gr.Markdown( "---\nRules checked: frontmatter parses as YAML · `name` kebab-case ≤64 · " "`description` present ≤1024 with a trigger cue · non-empty structured body · " "relative bundled-file links. MIT licensed." ) if __name__ == "__main__": demo.launch(mcp_server=True)