"""Seed builtin skills into the global skill registry.""" from loguru import logger from sqlalchemy import select from app.database import async_session from app.models.skill import Skill, SkillFile BUILTIN_SKILLS = [ { "name": "Web Research", "description": "Systematic web searching and information synthesis. Use when: needing factual data from the web, evaluating sources, or cross-referencing claims. NOT for: simple trivia or local file search.", "category": "research", "icon": "🔍", "folder_name": "web-research", "files": [ { "path": "SKILL.md", "content": """--- name: Web Research description: Systematic web searching, source evaluation, and information synthesis --- # Web Research ## Overview Use this skill when you need to find, evaluate, and synthesize information from the web. **Keywords**: web search, information retrieval, source evaluation, fact-checking, research ## Process ### 1. Define Search Strategy - Identify key search terms and variations - Consider different angles and perspectives - Plan multiple search queries ### 2. Evaluate Sources - Check source credibility and recency - Cross-reference claims across multiple sources - Note publication dates and author expertise ### 3. Synthesize Findings - Organize information by theme or relevance - Highlight key findings and consensus views - Note conflicting information and gaps ## Output Format - Start with a brief summary of findings - Provide detailed sections with source citations - End with confidence assessment and limitations """, }, { "path": "scripts/search_helper.py", "content": ( "#!/usr/bin/env python3\n" '"""Helper utilities for structured web search."""\n\n' "from datetime import datetime\n\n\n" "def format_search_results(results: list[dict]) -> str:\n" ' """Format raw search results into a structured report."""\n' " output = []\n" " for i, r in enumerate(results, 1):\n" " title = r.get('title', 'Untitled')\n" " url = r.get('url', '#')\n" " snippet = r.get('snippet', 'No description')\n" " output.append(f'{i}. [{title}]({url})')\n" " output.append(f' {snippet}')\n" " output.append('')\n" " return '\\n'.join(output)\n\n\n" "def assess_source_credibility(url: str) -> dict:\n" ' """Basic heuristics for source credibility."""\n' " trusted = ['.edu', '.gov', '.org', 'arxiv.org', 'nature.com']\n" " score = 0.5\n" " for d in trusted:\n" " if d in url:\n" " score = 0.8\n" " break\n" " return {'url': url, 'credibility_score': score,\n" " 'assessed_at': datetime.now().isoformat()}\n" ), }, ], }, { "name": "Data Analysis", "description": "Data interpretation and structured reporting. Use when: analyzing CSV/dataset files, finding trends, or generating statistical summaries. NOT for: writing code to build data models.", "category": "analysis", "icon": "📊", "folder_name": "data-analysis", "files": [ { "path": "SKILL.md", "content": """--- name: Data Analysis description: Data interpretation, pattern recognition, and structured reporting --- # Data Analysis ## Overview Use this skill for analyzing data, identifying patterns, and creating structured reports. **Keywords**: data analysis, statistics, trends, visualization, reporting ## Process ### 1. Data Understanding - Identify data types, ranges, and distributions - Check for missing values and anomalies - Understand the business context ### 2. Analysis Methods - Descriptive statistics (mean, median, distribution) - Trend analysis (time-series patterns) - Comparative analysis (benchmarking, A/B) - Correlation and relationship discovery ### 3. Reporting - Lead with key insights and actionable findings - Use tables and structured formats for clarity - Include methodology notes for reproducibility ## Output Format - Executive summary with top 3 findings - Detailed analysis with supporting data - Recommendations based on findings """, }, { "path": "scripts/analyze_csv.py", "content": ( "#!/usr/bin/env python3\n" '"""Utility for quick CSV data analysis."""\n\n' "import csv\nimport statistics\nfrom collections import Counter\n\n\n" "def analyze_column(data: list[dict], column: str) -> dict:\n" ' """Analyze a single column from CSV data."""\n' " values = [row.get(column) for row in data if row.get(column) is not None]\n" " if not values:\n" ' return {"column": column, "count": 0, "error": "No data"}\n\n' ' result = {"column": column, "count": len(values), "unique": len(set(values))}\n\n' " # Try numeric analysis\n" " try:\n" " nums = [float(v) for v in values]\n" " result.update({\n" ' "type": "numeric",\n' ' "min": min(nums), "max": max(nums),\n' ' "mean": round(statistics.mean(nums), 2),\n' ' "median": round(statistics.median(nums), 2),\n' " })\n" " except (ValueError, TypeError):\n" " freq = Counter(values).most_common(5)\n" ' result.update({"type": "categorical", "top_values": freq})\n\n' " return result\n\n\n" "def quick_summary(filepath: str) -> str:\n" ' """Generate a quick summary of a CSV file."""\n' " with open(filepath, 'r') as f:\n" " reader = csv.DictReader(f)\n" " data = list(reader)\n" " columns = data[0].keys() if data else []\n" " return f'Rows: {len(data)}, Columns: {len(columns)}'\n" ), }, { "path": "examples/sample_report.md", "content": """# Sample Analysis Report ## Executive Summary Analysis of Q4 2024 sales data reveals a 12% increase in total revenue, driven primarily by the Enterprise segment (+23%). ## Key Findings 1. **Revenue Growth**: Total revenue increased from $2.1M to $2.35M 2. **Top Segment**: Enterprise accounts grew 23% QoQ 3. **Churn**: SMB churn rate decreased from 5.2% to 4.1% ## Detailed Analysis | Metric | Q3 2024 | Q4 2024 | Change | |--------|---------|---------|--------| | Total Revenue | $2.1M | $2.35M | +12% | | Enterprise | $1.2M | $1.47M | +23% | | SMB | $0.9M | $0.88M | -2% | | Churn Rate | 5.2% | 4.1% | -1.1pp | ## Recommendations 1. Increase investment in Enterprise sales team 2. Investigate SMB revenue decline 3. Continue churn reduction initiatives """, }, ], }, { "name": "Content Writing", "description": "Professional content creation and tone adaptation. Use when: drafting articles, emails, or marketing copy with specific stylistic requirements. NOT for: casual chat responses.", "category": "creation", "icon": "✍️", "folder_name": "content-writing", "files": [ { "path": "SKILL.md", "content": """--- name: Content Writing description: Professional content creation, editing, and tone adaptation --- # Content Writing ## Overview Use this skill for creating, editing, and polishing written content across formats. **Keywords**: writing, editing, copywriting, tone, style, proofreading ## Content Types - **Articles & Blog Posts**: Informative, engaging long-form content - **Business Communications**: Emails, memos, reports - **Marketing Copy**: Headlines, descriptions, calls-to-action - **Documentation**: Technical docs, guides, FAQs ## Guidelines ### Structure - Hook readers with a compelling opening - Use clear headings and logical flow - Keep paragraphs short (3-5 sentences) - End with a clear conclusion or call-to-action ### Tone Adaptation - **Formal**: Business reports, official communications - **Professional**: Client-facing content, documentation - **Conversational**: Blog posts, social media - **Technical**: Developer docs, specifications ### Quality Checklist - [ ] Clear main message - [ ] Consistent tone throughout - [ ] No grammatical errors - [ ] Appropriate length for format """, }, ], }, { "name": "Competitive Analysis", "description": "Competitor research and comparison frameworks. Use when: asked to compare companies, products, or perform SWOT/feature matrix analysis. NOT for: general academic research.", "category": "research", "icon": "⚔️", "folder_name": "competitive-analysis", "files": [ { "path": "SKILL.md", "content": """--- name: Competitive Analysis description: Market competitor research, comparison frameworks, and strategic insights --- # Competitive Analysis ## Overview Use this skill for analyzing competitors, market positioning, and strategic opportunities. **Keywords**: competitors, market analysis, SWOT, positioning, benchmarking ## Frameworks ### SWOT Analysis | | Helpful | Harmful | |---|---|---| | **Internal** | Strengths | Weaknesses | | **External** | Opportunities | Threats | ### Feature Comparison Matrix Compare products across key dimensions: - Core features and capabilities - Pricing and packaging - Target audience - Market positioning - Technology stack ### Porter's Five Forces 1. Competitive rivalry intensity 2. Bargaining power of suppliers 3. Bargaining power of buyers 4. Threat of new entrants 5. Threat of substitutes ## Output Format - Competitor overview table - Detailed per-competitor analysis - Strategic recommendations - Key differentiators summary """, }, ], }, { "name": "Meeting Notes", "description": "Meeting summarization and follow-up tracking. Use when: given meeting transcripts or rough notes to extract structured action items and key decisions. NOT for: generic document summarization.", "category": "productivity", "icon": "📝", "folder_name": "meeting-notes", "files": [ { "path": "SKILL.md", "content": """--- name: Meeting Notes description: Meeting summarization, action item extraction, and follow-up tracking --- # Meeting Notes ## Overview Use this skill for processing meeting content into structured summaries with clear action items. **Keywords**: meetings, notes, action items, decisions, follow-up ## Template ### Meeting Summary ``` Meeting: [Title] Date: [Date] Participants: [Names] Duration: [Time] ``` ### Key Decisions - Numbered list of decisions made ### Action Items | # | Action | Owner | Due Date | Status | |---|--------|-------|----------|--------| | 1 | [Task] | [Name] | [Date] | ⬜ Pending | ### Discussion Points Brief summary of main topics discussed ### Next Steps - Follow-up meeting date - Items deferred to next meeting """, }, ], }, { "name": "Complex Task Executor", "description": "Structured methodology for decomposing, planning, and executing complex multi-step tasks with progress tracking", "category": "productivity", "icon": "🎯", "folder_name": "complex-task-executor", "is_default": True, "files": [ { "path": "SKILL.md", "content": """--- name: Complex Task Executor description: Structured methodology for decomposing, planning, and executing complex multi-step tasks with progress tracking --- # Complex Task Executor ## When to Use This Skill Use this skill when a task meets ANY of the following criteria: - Requires more than 3 distinct steps to complete - Involves multiple tools or information sources - Has dependencies between steps (step B needs output from step A) - Requires research before execution - Could benefit from a documented plan others can review - The user explicitly asks for a thorough or systematic approach **DO NOT use this for simple tasks** like answering a question, reading a single file, or performing one tool call. ## Workflow ### Phase 1: Task Analysis (THINK before acting) Before creating any files, analyze the task: 1. **Understand the goal**: What is the final deliverable? What does "done" look like? 2. **Assess complexity**: How many steps? What tools are needed? 3. **Identify dependencies**: Which steps depend on others? 4. **Identify risks**: What could go wrong? What information is missing? 5. **Estimate scope**: Is the task feasible with available tools/skills? ### Phase 2: Create Task Plan Create a task folder and plan file in the workspace: ``` workspace//plan.md ``` The plan.md MUST follow this exact format: ```markdown # Task: ## Objective ## Steps - [ ] 1. - Details: - Output: - [ ] 2. - Details: <...> - Depends on: Step 1 - [ ] 3. - Details: <...> ## Status - Created: - Current Step: Not started - Progress: 0/ ## Notes ``` Rules for writing the plan: - Each step should be completable in 1-3 tool calls - Use verb-noun format: "Research competitors", "Draft report", "Validate data" - Mark dependencies explicitly - Include expected outputs for each step ### Phase 3: Execute Step-by-Step For EACH step in the plan: 1. **Read the plan** — Call `read_file` on `workspace//plan.md` to check current state 2. **Mark as in-progress** — Update the checkbox from `[ ]` to `[/]` and update the "Current Step" field 3. **Execute the step** — Do the actual work (tool calls, analysis, writing) 4. **Record output** — Save results to `workspace//` (e.g., intermediate files, data) 5. **Mark as complete** — Update the checkbox from `[/]` to `[x]` and update "Progress" counter 6. **Proceed to next step** — Move to the next uncompleted step ### Phase 4: Completion When all steps are done: 1. Update plan.md status to "✅ Completed" 2. Create a `workspace//summary.md` with: - What was accomplished - Key results and deliverables - Any follow-up items 3. Present the final result to the user ## Adaptive Replanning If during execution you discover: - A step is impossible → Mark it `[!]` with a reason, add alternative steps - New steps are needed → Add them to the plan with `[+]` prefix - A step produced unexpected results → Add a note and adjust subsequent steps - The plan needs major changes → Create a new section "## Revised Plan" and follow it Always update plan.md BEFORE changing course, so the plan stays the source of truth. ## Error Handling - If a tool call fails, retry once. If it fails again, mark the step as blocked and note the error. - Never silently skip a step. Always update the plan to reflect what happened. - If you're stuck, tell the user what's blocking and ask for guidance. ## Example Scenarios ### Example 1: "Research our top 3 competitors and write a comparison report" Plan would be: ``` - [ ] 1. Identify the user's company/product context - [ ] 2. Research Competitor A — website, pricing, features - [ ] 3. Research Competitor B — website, pricing, features - [ ] 4. Research Competitor C — website, pricing, features - [ ] 5. Create comparison matrix - [ ] 6. Write analysis and recommendations - [ ] 7. Compile final report ``` ### Example 2: "Analyze our Q4 sales data and prepare a board presentation" Plan would be: ``` - [ ] 1. Read and understand the sales data files - [ ] 2. Calculate key metrics (revenue, growth, trends) - [ ] 3. Identify top insights and anomalies - [ ] 4. Create data summary tables - [ ] 5. Draft presentation outline - [ ] 6. Write each presentation section - [ ] 7. Add executive summary - [ ] 8. Review and polish final document ``` ## Key Principles 1. **Plan is the source of truth** — Always update it before moving on 2. **One step at a time** — Don't skip ahead or batch too many steps 3. **Show your work** — Save intermediate results to the task folder 4. **Communicate progress** — The user can read plan.md at any time to see status 5. **Be adaptive** — Plans change; that's OK if you update the plan first """, }, { "path": "examples/plan_template.md", "content": """# Task: [Title] ## Objective [One-sentence description of the desired outcome] ## Steps - [ ] 1. [First step] - Details: [What specifically to do] - Output: [What this step produces] - [ ] 2. [Second step] - Details: [...] - Depends on: Step 1 - [ ] 3. [Third step] - Details: [...] ## Status - Created: [timestamp] - Current Step: Not started - Progress: 0/3 ## Notes - [Any assumptions, risks, or open questions] """, }, ], }, # ─── Skill Creator (mandatory default) ───────── { "name": "Skill Creator", "description": "Create new skills, modify and improve existing skills, and measure skill performance", "category": "development", "icon": "🛠️", "folder_name": "skill-creator", "is_default": True, "files": [], # populated at runtime from skill_creator_content }, # ─── Content Research Writer ────────────────── { "name": "Content Research Writer", "description": "Assists in writing high-quality content by conducting research, adding citations, improving hooks, iterating on outlines, and providing real-time section feedback", "category": "writing", "icon": "✍️", "folder_name": "content-research-writer", "files": [], # populated at runtime }, # ─── MCP Tool Installer (mandatory default) ────────────── { "name": "MCP Tool Installer", "description": "Guide users through discovering, configuring, and installing MCP tools directly in chat — no Settings page required", "category": "development", "icon": "🔌", "folder_name": "mcp-installer", "is_default": True, "files": [], # populated at runtime from agent_template/skills/MCP_INSTALLER.md }, ] async def seed_skills(): """Insert builtin skills if they don't exist.""" from app.services.skill_creator_content import get_skill_creator_files from pathlib import Path as _Path _files_dir = _Path(__file__).parent / "skill_creator_files" _template_skills_dir = _Path(__file__).parent.parent.parent / "agent_template" / "skills" # Populate skill-creator files at runtime for s in BUILTIN_SKILLS: if s["folder_name"] == "skill-creator" and not s["files"]: s["files"] = get_skill_creator_files() elif s["folder_name"] == "content-research-writer" and not s["files"]: # Load from downloaded file crw_file = _files_dir / "content_research_writer__SKILL.md" if crw_file.exists(): s["files"] = [{"path": "SKILL.md", "content": crw_file.read_text(encoding="utf-8")}] elif s["folder_name"] == "mcp-installer" and not s["files"]: mcp_file = _template_skills_dir / "MCP_INSTALLER.md" if mcp_file.exists(): s["files"] = [{"path": "SKILL.md", "content": mcp_file.read_text(encoding="utf-8")}] else: logger.warning("[SkillSeeder] MCP_INSTALLER.md not found in agent_template/skills/") async with async_session() as db: for skill_data in BUILTIN_SKILLS: result = await db.execute( select(Skill).where(Skill.folder_name == skill_data["folder_name"]) ) existing = result.scalar_one_or_none() is_default = skill_data.get("is_default", False) if existing: # Update metadata existing.name = skill_data["name"] existing.description = skill_data["description"] existing.category = skill_data["category"] existing.icon = skill_data["icon"] existing.is_default = is_default # Sync files — add missing ones from sqlalchemy.orm import selectinload res2 = await db.execute( select(Skill).where(Skill.id == existing.id).options(selectinload(Skill.files)) ) sk = res2.scalar_one() existing_paths = {f.path: f for f in sk.files} for f in skill_data["files"]: if f["path"] in existing_paths: # Update content if changed existing_file = existing_paths[f["path"]] if existing_file.content != f["content"]: existing_file.content = f["content"] logger.info(f"[SkillSeeder] Updated {f['path']} in {skill_data['name']}") else: db.add(SkillFile(skill_id=existing.id, path=f["path"], content=f["content"])) logger.info(f"[SkillSeeder] Added file {f['path']} to {skill_data['name']}") else: skill = Skill( name=skill_data["name"], description=skill_data["description"], category=skill_data["category"], icon=skill_data["icon"], folder_name=skill_data["folder_name"], is_builtin=True, is_default=is_default, ) db.add(skill) await db.flush() for f in skill_data["files"]: db.add(SkillFile(skill_id=skill.id, path=f["path"], content=f["content"])) logger.info(f"[SkillSeeder] Created skill: {skill_data['name']}") await db.commit() logger.info("[SkillSeeder] Skills seeded") async def push_default_skills_to_existing_agents(): """Deploy all is_default skills into the workspace of every existing agent that is missing them. Called at startup after seed_skills() so existing agents automatically receive new default skills like MCP_INSTALLER without requiring manual re-creation. """ from pathlib import Path from app.models.agent import Agent from app.models.skill import Skill, SkillFile from sqlalchemy.orm import selectinload from app.services.agent_manager import agent_manager async with async_session() as db: # Load all is_default skills with their files default_skills_r = await db.execute( select(Skill).where(Skill.is_default == True).options(selectinload(Skill.files)) ) default_skills = default_skills_r.scalars().all() if not default_skills: return # Load all agents agents_r = await db.execute(select(Agent)) agents = agents_r.scalars().all() pushed = 0 updated = 0 for agent in agents: agent_dir = agent_manager._agent_dir(agent.id) skills_dir = agent_dir / "skills" for skill in default_skills: if not skill.files: continue skill_folder = skills_dir / skill.folder_name skill_folder.mkdir(parents=True, exist_ok=True) for sf in skill.files: fp = (skill_folder / sf.path).resolve() fp.parent.mkdir(parents=True, exist_ok=True) if fp.exists(): existing_content = fp.read_text(encoding="utf-8") if existing_content == sf.content: continue # already up-to-date fp.write_text(sf.content, encoding="utf-8") updated += 1 else: fp.write_text(sf.content, encoding="utf-8") pushed += 1 logger.info(f"[SkillSeeder] Pushed '{skill.name}' to agent {agent.id}") if pushed or updated: logger.info(f"[SkillSeeder] Pushed {pushed} new + {updated} updated skill files to existing agents") else: logger.info("[SkillSeeder] All existing agents already have up-to-date default skills")