| import gradio as gr |
| import os, re, json, textwrap |
| from typing import Dict, Tuple |
|
|
| SKILLS_ROOT = os.path.join(os.path.dirname(__file__), "skills") |
|
|
| def parse_frontmatter(md_text: str) -> Tuple[dict, str]: |
| if not md_text.startswith("---"): |
| return {}, md_text |
| parts = md_text.split("\n") |
| if parts[0].strip() != "---": |
| return {}, md_text |
| try: |
| end_idx = parts[1:].index("---") + 1 |
| except ValueError: |
| return {}, md_text |
| fm_lines = parts[1:end_idx] |
| body = "\n".join(parts[end_idx+1:]) |
| meta = {} |
| for line in fm_lines: |
| if ":" in line: |
| k, v = line.split(":", 1) |
| meta[k.strip()] = v.strip().strip('"').strip("'") |
| return meta, body |
|
|
| def load_skill(skill_slug: str): |
| path = os.path.join(SKILLS_ROOT, skill_slug, "SKILL.md") |
| if not os.path.exists(path): |
| return {}, f"**SKILL.md not found for skill `{skill_slug}`.**" |
| with open(path, "r", encoding="utf-8") as f: |
| text = f.read() |
| meta, body = parse_frontmatter(text) |
| return meta, body |
|
|
| def list_skills(): |
| out = [] |
| if not os.path.isdir(SKILLS_ROOT): |
| return out |
| for name in sorted(os.listdir(SKILLS_ROOT)): |
| if os.path.isdir(os.path.join(SKILLS_ROOT, name)) and os.path.exists(os.path.join(SKILLS_ROOT, name, "SKILL.md")): |
| meta, _ = load_skill(name) |
| out.append((name, meta.get("name", name), meta.get("description", ""))) |
| return out |
|
|
| def list_linked_files(skill_slug: str): |
| root = os.path.join(SKILLS_ROOT, skill_slug) |
| if not os.path.isdir(root): |
| return [] |
| return [fn for fn in sorted(os.listdir(root)) if fn.lower().endswith(".md") and fn != "SKILL.md"] |
|
|
| def read_linked_file(skill_slug: str, filename: str) -> str: |
| path = os.path.join(SKILLS_ROOT, skill_slug, filename) |
| if not os.path.exists(path): |
| return f"**{filename} not found.**" |
| with open(path, "r", encoding="utf-8") as f: |
| return f.read() |
|
|
| def run_pdf_tool(skill_slug: str, uploaded_pdf) -> str: |
| try: |
| if uploaded_pdf is None: |
| return "Please upload a PDF." |
| import runpy, json as _json, os as _os |
| tool_path = os.path.join(SKILLS_ROOT, skill_slug, "tools", "extract_form_fields.py") |
| if not os.path.exists(tool_path): |
| return "Tool script not found. Expected tools/extract_form_fields.py" |
| ns = runpy.run_path(tool_path) |
| if "extract_fields" not in ns: |
| return "extract_form_fields.py does not define extract_fields(pdf_path)." |
| fn = ns["extract_fields"] |
| result = fn(uploaded_pdf.name if hasattr(uploaded_pdf, "name") else uploaded_pdf) |
| try: |
| return "```json\n" + _json.dumps(result, indent=2) + "\n```" |
| except Exception: |
| return str(result) |
| except Exception as e: |
| return f"Error running tool: {e}" |
|
|
| def explain_progressive_disclosure() -> str: |
| return ( |
| "### Progressive Disclosure\\n\\n" |
| "1. **Startup**: Only skill *metadata* (name, description) is shown.\\n" |
| "2. **Trigger**: Loading a skill reads **SKILL.md** into context.\\n" |
| "3. **Deep Dive**: Linked files (e.g., `reference.md`, `forms.md`) are opened only when needed.\\n" |
| "4. **Tools**: For the PDF skill, run the Python tool to extract form fields without adding the PDF to context." |
| ) |
|
|
| with gr.Blocks(title="Agent Skills — Progressive Disclosure Demo") as demo: |
| gr.Markdown("# Equipping agents for the real world with Agent Skills\\nPublished Oct 16, 2025") |
| gr.Markdown("This Space demonstrates **Agent Skills** as folders containing a `SKILL.md`, optional linked files, and tools.") |
|
|
| with gr.Row(): |
| with gr.Column(scale=1): |
| gr.Markdown("### Installed Skills") |
| skills = list_skills() |
| skill_map = {f"{title} — {desc}": slug for (slug, title, desc) in skills} if skills else {} |
| labels = list(skill_map.keys()) or ["(No skills found)"] |
| skill_dd = gr.Dropdown(choices=labels, value=labels[0], label="Pick a skill") |
| meta_out = gr.JSON(label="Skill metadata") |
|
|
| def on_pick(label): |
| if not skill_map: |
| return {}, "", [], None, None |
| slug = skill_map.get(label, None) |
| if not slug: |
| return {}, "", [], None, None |
| meta, body = load_skill(slug) |
| return meta, body, list_linked_files(slug), slug, None |
|
|
| body_out = gr.Markdown(label="SKILL.md body") |
| linked_files = gr.Dropdown(choices=[], label="Linked files") |
| selected_slug_state = gr.State(value=None) |
| _reset = gr.State(value=None) |
|
|
| skill_dd.change(fn=on_pick, inputs=[skill_dd], outputs=[meta_out, body_out, linked_files, selected_slug_state, _reset]) |
|
|
| with gr.Column(scale=2): |
| gr.Markdown("### Progressive Disclosure") |
| gr.Markdown(explain_progressive_disclosure()) |
|
|
| gr.Markdown("---") |
| gr.Markdown("### Linked File Viewer") |
| linked_view = gr.Markdown() |
|
|
| def on_view_linked(filename, slug): |
| if not slug or not filename: |
| return "Pick a skill and a linked file." |
| return read_linked_file(slug, filename) |
|
|
| view_btn = gr.Button("Open linked file") |
| view_btn.click(fn=on_view_linked, inputs=[linked_files, selected_slug_state], outputs=[linked_view]) |
|
|
| gr.Markdown("---") |
| gr.Markdown("### Run Skill Tool (PDF form field extractor)") |
| pdf_in = gr.File(label="Upload a PDF", file_count="single", type="filepath") |
| tool_out = gr.Markdown() |
|
|
| def run_tool_clicked(slug, pdf): |
| if not slug: |
| return "Pick a skill first." |
| return run_pdf_tool(slug, pdf) |
|
|
| run_tool_btn = gr.Button("Extract form fields") |
| run_tool_btn.click(fn=run_tool_clicked, inputs=[selected_slug_state, pdf_in], outputs=[tool_out]) |
|
|
| if __name__ == "__main__": |
| demo.launch() |
|
|