SkillForge 4.0 - Intelligent Skill Router & Creator Analyzes ANY input to find, improve, or create the right skill. When to use - Skill discovery : "Do I have a skill for X?" - Skill creation : "Create a skill for Y" - Skill improvement : "Improve the Z skill" - Routing : Any ambiguous request that needs skill matching How it works 1. Phase 0 Triage : Check if skill exists (no duplicates) 2. Analysis : 11 thinking models evaluate the request 3. Action : Route to existing skill OR create new one 4. Synthesis : Multi-agent panel validates output Triggers - - Direct activation - / - Natural lang…

, name):\n return False, f\"Name '{name}' should be hyphen-case (lowercase letters, digits, and hyphens only)\"\n if name.startswith('-') or name.endswith('-') or '--' in name:\n return False, f\"Name '{name}' cannot start/end with hyphen or contain consecutive hyphens\"\n # Check name length (max 64 characters per spec)\n if len(name) > 64:\n return False, f\"Name is too long ({len(name)} characters). Maximum is 64 characters.\"\n\n # Extract and validate description\n description = frontmatter.get('description', '')\n if not isinstance(description, str):\n return False, f\"Description must be a string, got {type(description).__name__}\"\n description = description.strip()\n if description:\n # Check for angle brackets\n if '\u003c' in description or '>' in description:\n return False, \"Description cannot contain angle brackets (\u003c or >)\"\n # Check description length (max 1024 characters per spec)\n if len(description) > 1024:\n return False, f\"Description is too long ({len(description)} characters). Maximum is 1024 characters.\"\n\n return True, \"Skill is valid!\"\n\n\ndef main():\n if len(sys.argv) != 2:\n print(\"Usage: python quick_validate.py \u003cskill_directory>\")\n print(\"\\nExample:\")\n print(\" python quick_validate.py ~/.claude/skills/my-skill/\")\n sys.exit(1)\n\n skill_path = sys.argv[1]\n\n if not Path(skill_path).exists():\n print(f\"Error: Path not found: {skill_path}\")\n sys.exit(1)\n\n valid, message = validate_skill(skill_path)\n\n if valid:\n print(f\"✅ {message}\")\n else:\n print(f\"❌ {message}\")\n\n sys.exit(0 if valid else 1)\n\n\nif __name__ == \"__main__\":\n main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":4898,"content_sha256":"ddda0a8acf92f9c04bfad77a752e1e44759c86c93babe5ebc69d14e4ef0b8633"},{"filename":"scripts/triage_skill_request.py","content":"#!/usr/bin/env python3\n\"\"\"\ntriage_skill_request.py - Intelligent skill routing from any user input\n\nPart of the skillforge skill (Phase 0: Skill Triage).\n\nAnalyzes ANY user input (prompt, error, code, URL, question, request) and\ndetermines the best action:\n- USE_EXISTING: Existing skill handles this perfectly\n- IMPROVE_EXISTING: Existing skill is close but needs enhancement\n- CREATE_NEW: No good match, create new skill\n- COMPOSE: Multiple skills needed, suggest chain\n- CLARIFY: Ambiguous input, need more information\n\nUsage:\n python triage_skill_request.py \"create a skill for code review\"\n python triage_skill_request.py \"help me debug this error\" --json\n python triage_skill_request.py \"TypeError: Cannot read property 'map'\"\n\nExit Codes:\n 0 - Success\n 1 - General failure\n 2 - Skill index not found (run discover_skills.py first)\n\"\"\"\n\nimport argparse\nimport json\nimport re\nimport sys\nfrom dataclasses import dataclass, field\nfrom datetime import datetime\nfrom pathlib import Path\nfrom typing import List, Dict, Optional, Tuple, Any\n\n\n# ===========================================================================\n# RESULT TYPES\n# ===========================================================================\n\n@dataclass\nclass Result:\n \"\"\"Standard result object for script operations.\"\"\"\n success: bool\n message: str\n data: dict = field(default_factory=dict)\n errors: List[str] = field(default_factory=list)\n warnings: List[str] = field(default_factory=list)\n\n def to_dict(self) -> dict:\n return {\n \"success\": self.success,\n \"message\": self.message,\n \"data\": self.data,\n \"errors\": self.errors,\n \"warnings\": self.warnings,\n \"timestamp\": datetime.now().isoformat()\n }\n\n\nclass Action:\n \"\"\"Possible triage actions.\"\"\"\n USE_EXISTING = \"USE_EXISTING\"\n IMPROVE_EXISTING = \"IMPROVE_EXISTING\"\n CREATE_NEW = \"CREATE_NEW\"\n COMPOSE = \"COMPOSE\"\n CLARIFY = \"CLARIFY\"\n\n\nclass InputCategory:\n \"\"\"Categories of user input.\"\"\"\n EXPLICIT_CREATE = \"explicit_create\" # \"create a skill for X\"\n EXPLICIT_IMPROVE = \"explicit_improve\" # \"improve the X skill\"\n SKILL_QUESTION = \"skill_question\" # \"do I have a skill for X?\"\n TASK_REQUEST = \"task_request\" # \"help me with X\", \"I need to X\"\n ERROR_MESSAGE = \"error_message\" # Stack traces, errors\n CODE_SNIPPET = \"code_snippet\" # Code pasted\n URL_CONTENT = \"url_content\" # URLs\n GENERAL = \"general\" # Unclear\n\n\n# ===========================================================================\n# INPUT CLASSIFICATION\n# ===========================================================================\n\n# Patterns for detecting input category\nEXPLICIT_CREATE_PATTERNS = [\n r'\\b(?:create|build|make|design|develop)\\s+(?:a\\s+)?(?:new\\s+)?skill\\b',\n r'\\bskillforge[:\\s]',\n r'\\b(?:new|custom)\\s+skill\\s+(?:for|to)\\b',\n r'\\bultimate\\s+skill\\b',\n]\n\nEXPLICIT_IMPROVE_PATTERNS = [\n r'\\b(?:improve|enhance|update|upgrade|fix|extend)\\s+(?:the\\s+)?(?:\\w+\\s+)?skill\\b',\n r'\\bskill\\s+(?:needs?|could\\s+use|should\\s+have)\\b',\n r'\\b(?:add|include)\\s+(?:to|in)\\s+(?:the\\s+)?\\w+\\s+skill\\b',\n]\n\nSKILL_QUESTION_PATTERNS = [\n r'\\bdo\\s+(?:i|we)\\s+have\\s+(?:a\\s+)?skill\\b',\n r'\\bwhich\\s+skill\\b',\n r'\\bwhat\\s+skill\\b',\n r'\\brecommend\\s+(?:a\\s+)?skill\\b',\n r'\\bskill\\s+for\\b',\n r'\\bfind\\s+(?:a\\s+)?skill\\b',\n r'\\bsuggest\\s+(?:a\\s+)?skill\\b',\n r'\\bis\\s+there\\s+(?:a\\s+)?skill\\b',\n]\n\nTASK_REQUEST_PATTERNS = [\n r'\\b(?:help|assist)\\s+(?:me\\s+)?(?:with|to)\\b',\n r'\\bi\\s+need\\s+to\\b',\n r'\\bhow\\s+(?:do\\s+i|can\\s+i|to)\\b',\n r'\\bcan\\s+you\\b',\n r'\\bplease\\b.*\\b(?:help|do|make|create|fix|build)\\b',\n]\n\nERROR_PATTERNS = [\n r'Error:',\n r'Exception:',\n r'TypeError:',\n r'ReferenceError:',\n r'SyntaxError:',\n r'at\\s+\\S+\\s+\\(',\n r'Traceback \\(most recent call',\n r'File \"[^\"]+\", line \\d+',\n]\n\nCODE_PATTERNS = [\n r'^\\s*(function|const|let|var|class|import|export|def|async|await)\\s+',\n r'^\\s*\u003c[a-zA-Z][^>]*>',\n r'=>',\n r'^\\s*@\\w+',\n]\n\nURL_PATTERNS = [\n r'https?://[^\\s]+',\n]\n\n\ndef classify_input(query: str) -> Tuple[str, Dict[str, Any]]:\n \"\"\"\n Classify user input into a category and extract signals.\n\n Returns:\n Tuple of (category, signals_dict)\n \"\"\"\n query_lower = query.lower()\n signals = {\n \"has_skill_mention\": \"skill\" in query_lower,\n \"has_error\": False,\n \"has_code\": False,\n \"has_url\": False,\n \"mentioned_skill_name\": None,\n \"extracted_purpose\": None,\n }\n\n # Check for explicit skill creation request\n for pattern in EXPLICIT_CREATE_PATTERNS:\n if re.search(pattern, query_lower):\n # Extract the purpose/goal\n purpose_match = re.search(r'skill\\s+(?:for|to)\\s+(.+?)(?:\\.|$)', query_lower)\n if purpose_match:\n signals[\"extracted_purpose\"] = purpose_match.group(1).strip()\n return InputCategory.EXPLICIT_CREATE, signals\n\n # Check for explicit improvement request\n for pattern in EXPLICIT_IMPROVE_PATTERNS:\n if re.search(pattern, query_lower):\n # Try to extract skill name\n skill_match = re.search(r'(?:improve|enhance|update|fix)\\s+(?:the\\s+)?(\\w+(?:-\\w+)*)\\s+skill', query_lower)\n if skill_match:\n signals[\"mentioned_skill_name\"] = skill_match.group(1)\n return InputCategory.EXPLICIT_IMPROVE, signals\n\n # Check for skill question\n for pattern in SKILL_QUESTION_PATTERNS:\n if re.search(pattern, query_lower):\n return InputCategory.SKILL_QUESTION, signals\n\n # Check for error message\n for pattern in ERROR_PATTERNS:\n if re.search(pattern, query, re.MULTILINE):\n signals[\"has_error\"] = True\n return InputCategory.ERROR_MESSAGE, signals\n\n # Check for code snippet\n for pattern in CODE_PATTERNS:\n if re.search(pattern, query, re.MULTILINE):\n signals[\"has_code\"] = True\n return InputCategory.CODE_SNIPPET, signals\n\n # Check for URL\n for pattern in URL_PATTERNS:\n if re.search(pattern, query):\n signals[\"has_url\"] = True\n return InputCategory.URL_CONTENT, signals\n\n # Check for task request\n for pattern in TASK_REQUEST_PATTERNS:\n if re.search(pattern, query_lower):\n return InputCategory.TASK_REQUEST, signals\n\n return InputCategory.GENERAL, signals\n\n\n# ===========================================================================\n# SKILL MATCHING (from skillrecommender)\n# ===========================================================================\n\ndef get_index_path() -> Path:\n \"\"\"Get the skill index file path.\"\"\"\n return Path.home() / \".cache\" / \"skillrecommender\" / \"skill_index.json\"\n\n\ndef load_skill_index() -> Optional[Dict]:\n \"\"\"Load skill index from disk.\"\"\"\n index_path = get_index_path()\n if not index_path.exists():\n return None\n try:\n return json.loads(index_path.read_text())\n except json.JSONDecodeError:\n return None\n\n\n# ===========================================================================\n# UNIVERSAL DOMAIN SYNONYMS\n# ===========================================================================\n# These are CONCEPT-based synonyms, not tied to any specific skill names.\n# They help match user queries to skill domains regardless of what skills exist.\n\nDOMAIN_SYNONYMS = {\n # Document formats - maps concepts to related terms\n \"spreadsheet\": [\"excel\", \"xlsx\", \"xls\", \"csv\", \"workbook\", \"tabular\", \"data table\", \"cells\", \"rows columns\"],\n \"document\": [\"word\", \"docx\", \"doc\", \"text document\", \"write document\", \"report\"],\n \"presentation\": [\"powerpoint\", \"pptx\", \"slides\", \"deck\", \"slide deck\", \"keynote\", \"pitch\"],\n \"pdf\": [\"pdf\", \"export pdf\", \"portable document\"],\n\n # Development concepts\n \"debugging\": [\"debug\", \"error\", \"exception\", \"stack trace\", \"traceback\", \"crash\", \"fix bug\", \"breakpoint\", \"investigate\"],\n \"testing\": [\"test\", \"unit test\", \"integration test\", \"e2e\", \"coverage\", \"spec\", \"tdd\", \"jest\", \"vitest\", \"pytest\", \"mocha\"],\n \"security\": [\"security\", \"vulnerability\", \"owasp\", \"audit\", \"secure\", \"penetration\", \"pentest\", \"xss\", \"injection\"],\n \"code_quality\": [\"review\", \"code review\", \"pr review\", \"pull request\", \"refactor\", \"clean code\", \"code smell\", \"lint\"],\n \"database\": [\"database\", \"db\", \"schema\", \"migration\", \"sql\", \"postgres\", \"mysql\", \"mongodb\", \"data model\", \"orm\"],\n \"api\": [\"api\", \"rest\", \"graphql\", \"endpoint\", \"openapi\", \"swagger\", \"restful\", \"http\"],\n \"frontend\": [\"ui\", \"ux\", \"frontend\", \"react\", \"vue\", \"angular\", \"css\", \"styling\", \"component\", \"user interface\"],\n \"accessibility\": [\"accessibility\", \"a11y\", \"wcag\", \"screen reader\", \"aria\", \"keyboard navigation\"],\n \"performance\": [\"performance\", \"optimize\", \"slow\", \"speed\", \"fast\", \"bottleneck\", \"profiling\", \"cache\"],\n \"authentication\": [\"auth\", \"login\", \"authentication\", \"oauth\", \"jwt\", \"session\", \"sign in\", \"sign up\", \"password\"],\n \"deployment\": [\"deploy\", \"deployment\", \"production\", \"release\", \"ship\", \"hosting\", \"ci\", \"cd\", \"pipeline\"],\n \"devops\": [\"docker\", \"kubernetes\", \"k8s\", \"container\", \"helm\", \"terraform\", \"infrastructure\"],\n \"documentation\": [\"documentation\", \"docs\", \"readme\", \"changelog\", \"api docs\", \"jsdoc\"],\n \"architecture\": [\"architecture\", \"system design\", \"design pattern\", \"microservices\", \"monolith\"],\n \"workflow\": [\"flowchart\", \"diagram\", \"workflow\", \"process\", \"swimlane\", \"sequence diagram\", \"uml\"],\n\n # AI/ML concepts\n \"ai_ml\": [\"ai\", \"ml\", \"machine learning\", \"llm\", \"rag\", \"embedding\", \"langchain\", \"prompt\", \"model\"],\n\n # Creative\n \"visual\": [\"visual\", \"image\", \"graphic\", \"art\", \"canvas\", \"design\"],\n}\n\n\ndef detect_query_domains(query: str) -> List[Tuple[str, List[str]]]:\n \"\"\"\n Detect which domains a query relates to using universal synonyms.\n\n Returns:\n List of (domain_name, matched_terms) tuples, sorted by match count\n \"\"\"\n query_lower = query.lower()\n detected = []\n\n for domain, synonyms in DOMAIN_SYNONYMS.items():\n matched_terms = []\n for term in synonyms:\n if term in query_lower:\n matched_terms.append(term)\n if matched_terms:\n detected.append((domain, matched_terms))\n\n # Sort by number of matches (more matches = stronger signal)\n detected.sort(key=lambda x: len(x[1]), reverse=True)\n return detected\n\n\ndef calculate_match_score(query: str, skill: Dict) -> Tuple[float, List[str]]:\n \"\"\"\n Calculate how well a skill matches the query using UNIVERSAL domain matching.\n\n This function does NOT use any hardcoded skill names - it works purely based on:\n 1. Domain detection from query using universal synonyms\n 2. Matching detected domains against skill's domains/keywords/description\n 3. Direct name/trigger matching from whatever skills exist\n\n Returns:\n Tuple of (score 0-100, list of match reasons)\n \"\"\"\n query_lower = query.lower()\n query_words = set(query_lower.split())\n\n skill_name = skill.get(\"name\", \"\").lower()\n skill_keywords = set(k.lower() for k in skill.get(\"keywords\", []))\n skill_triggers = set(t.lower() for t in skill.get(\"triggers\", []))\n skill_domains = set(d.lower() for d in skill.get(\"domains\", []))\n skill_description = skill.get(\"description\", \"\").lower()\n\n score = 0\n reasons = []\n\n # Step 1: Detect what domains the query is about\n query_domains = detect_query_domains(query)\n\n # Step 2: Check if skill's domains match detected query domains (STRONG signal)\n domain_matched = False\n for domain, matched_terms in query_domains:\n # Direct domain match (skill has this domain in its domains list)\n if domain in skill_domains:\n # Strong domain match - base 35 + bonus for multiple term matches\n domain_score = min(50, 35 + len(matched_terms) * 5)\n score += domain_score\n reasons.append(f\"domain: {domain} ({', '.join(matched_terms[:2])})\")\n domain_matched = True\n break # Only count best domain match\n\n # Step 3: Check if query domain terms appear in skill keywords/description\n keyword_matched = False\n for domain, matched_terms in query_domains:\n # Check if domain synonyms appear in skill's keywords\n for term in matched_terms:\n if term in skill_keywords:\n score += 15\n reasons.append(f\"keyword: {term}\")\n keyword_matched = True\n break\n if keyword_matched:\n break\n\n # Also check if the DOMAIN NAME itself is in keywords (e.g., \"spreadsheet\" domain, skill has \"spreadsheet\" keyword)\n if domain in skill_keywords or domain.replace(\"_\", \" \") in skill_keywords:\n score += 15\n reasons.append(f\"keyword: {domain}\")\n keyword_matched = True\n break\n\n # Check if domain terms appear in skill's description\n desc_matched = False\n for domain, matched_terms in query_domains:\n for term in matched_terms:\n if term in skill_description:\n score += 10\n reasons.append(f\"description: {term}\")\n desc_matched = True\n break\n if desc_matched:\n break\n\n # Also check domain name in description\n if domain in skill_description:\n score += 10\n reasons.append(f\"description: {domain}\")\n desc_matched = True\n break\n\n # Step 4: Direct skill name match (works for any skill name)\n if skill_name in query_lower:\n score += 35\n reasons.append(f\"name match: {skill_name}\")\n else:\n # Check if significant query words appear in skill name\n name_words = set(skill_name.replace(\"-\", \" \").replace(\"_\", \" \").split())\n name_overlap = query_words & name_words\n if name_overlap and any(len(w) > 3 for w in name_overlap):\n score += 20\n reasons.append(f\"partial name: {', '.join(name_overlap)}\")\n\n # Step 5: Trigger match (works for any skill's triggers)\n for trigger in skill_triggers:\n if trigger in query_lower:\n score += 25\n reasons.append(f\"trigger: {trigger}\")\n break\n\n # Step 6: General keyword overlap\n keyword_overlap = query_words & skill_keywords\n significant_overlap = [w for w in keyword_overlap if len(w) > 3]\n if significant_overlap:\n kw_score = min(20, len(significant_overlap) * 6)\n score += kw_score\n if f\"keyword:\" not in str(reasons): # Avoid duplicate\n reasons.append(f\"keywords: {', '.join(significant_overlap[:3])}\")\n\n # Step 7: Description word overlap (fallback)\n desc_words = set(skill_description.split())\n desc_overlap = query_words & desc_words\n significant_desc = [w for w in desc_overlap if len(w) > 4]\n if len(significant_desc) >= 2 and \"description:\" not in str(reasons):\n score += 8\n reasons.append(\"description overlap\")\n\n return min(100, score), reasons\n\n\ndef find_matching_skills(query: str, skills: List[Dict], limit: int = 5, signals: Dict = None) -> List[Dict]:\n \"\"\"\n Find skills that match the query, sorted by score.\n\n Uses UNIVERSAL domain-based matching - no hardcoded skill names.\n \"\"\"\n matches = []\n signals = signals or {}\n\n # Detect query domains for context boosting\n query_domains = detect_query_domains(query)\n query_domain_names = [d[0] for d in query_domains]\n\n for skill in skills:\n score, reasons = calculate_match_score(query, skill)\n\n # Apply context-based boosting using DOMAINS (not skill names)\n skill_domains = [d.lower() for d in skill.get(\"domains\", [])]\n\n # Error context + debugging domain boost\n if signals.get(\"has_error\") and \"debugging\" in skill_domains:\n score += 25\n reasons.append(\"error context boost\")\n\n # Code context + code quality domain boost\n if signals.get(\"has_code\") and \"code_quality\" in skill_domains:\n score += 15\n reasons.append(\"code context boost\")\n\n # URL context boost for code-related domains\n if signals.get(\"has_url\"):\n if any(d in skill_domains for d in [\"code_quality\", \"api\", \"documentation\"]):\n score += 10\n reasons.append(\"URL context boost\")\n\n # Boost skills whose domains align with detected query domains\n matching_domains = set(skill_domains) & set(query_domain_names)\n if matching_domains and score > 0:\n # Additional boost for strong domain alignment\n score += min(15, len(matching_domains) * 5)\n\n if score > 0:\n matches.append({\n \"name\": skill.get(\"name\"),\n \"score\": min(100, score),\n \"reasons\": reasons,\n \"source\": skill.get(\"source\"),\n \"description\": skill.get(\"description\", \"\")[:100],\n \"domains\": skill.get(\"domains\", []),\n })\n\n matches.sort(key=lambda m: m[\"score\"], reverse=True)\n return matches[:limit]\n\n\n# ===========================================================================\n# TRIAGE DECISION\n# ===========================================================================\n\ndef make_triage_decision(\n category: str,\n signals: Dict,\n matches: List[Dict],\n query: str\n) -> Tuple[str, Dict]:\n \"\"\"\n Make the final triage decision based on input analysis and skill matches.\n\n Returns:\n Tuple of (action, details_dict)\n \"\"\"\n top_match = matches[0] if matches else None\n top_score = top_match[\"score\"] if top_match else 0\n\n # Multi-domain detection\n if len(matches) >= 3:\n all_domains = set()\n for m in matches[:3]:\n all_domains.update(m.get(\"domains\", []))\n multi_domain = len(all_domains) >= 3\n else:\n multi_domain = False\n\n details = {\n \"category\": category,\n \"top_match\": top_match,\n \"top_score\": top_score,\n \"match_count\": len(matches),\n \"multi_domain\": multi_domain,\n }\n\n # Decision tree based on category and match quality\n\n # Explicit create request\n if category == InputCategory.EXPLICIT_CREATE:\n if top_score >= 80:\n # High match - existing skill might already do this\n return Action.CLARIFY, {\n **details,\n \"reason\": f\"Existing skill '{top_match['name']}' ({top_score}%) may already handle this. Create anyway or use existing?\",\n \"suggested_action\": \"Ask user to clarify if they want to create despite existing skill\",\n }\n elif top_score >= 50:\n # Moderate match - could improve existing\n return Action.CLARIFY, {\n **details,\n \"reason\": f\"Existing skill '{top_match['name']}' ({top_score}%) is similar. Create new or improve existing?\",\n \"suggested_action\": \"Ask if user wants new skill or to enhance existing\",\n }\n else:\n # Low/no match - proceed with creation\n return Action.CREATE_NEW, {\n **details,\n \"reason\": \"No strong existing match found. Proceeding with skill creation.\",\n \"purpose\": signals.get(\"extracted_purpose\"),\n }\n\n # Explicit improve request\n if category == InputCategory.EXPLICIT_IMPROVE:\n skill_name = signals.get(\"mentioned_skill_name\")\n if skill_name:\n # Find the mentioned skill\n for m in matches:\n if skill_name.lower() in m[\"name\"].lower():\n return Action.IMPROVE_EXISTING, {\n **details,\n \"reason\": f\"Improving existing skill: {m['name']}\",\n \"target_skill\": m[\"name\"],\n }\n # Couldn't find mentioned skill\n return Action.CLARIFY, {\n **details,\n \"reason\": \"Could not identify which skill to improve\",\n \"suggested_action\": \"Ask user to specify skill name\",\n }\n\n # Skill question - just recommend\n if category == InputCategory.SKILL_QUESTION:\n if top_score >= 60:\n return Action.USE_EXISTING, {\n **details,\n \"reason\": f\"Recommending existing skill: {top_match['name']} ({top_score}%)\",\n \"recommended_skills\": [m[\"name\"] for m in matches[:3]],\n }\n else:\n return Action.CREATE_NEW, {\n **details,\n \"reason\": \"No good existing skill matches. Consider creating one.\",\n }\n\n # Error messages - prefer skills with debugging domain (UNIVERSAL, no hardcoded names)\n if category == InputCategory.ERROR_MESSAGE:\n # Find skills with debugging domain\n debugging_skills = [m for m in matches if \"debugging\" in [d.lower() for d in m.get(\"domains\", [])]]\n best_debug_skill = debugging_skills[0] if debugging_skills else None\n\n if best_debug_skill and best_debug_skill[\"score\"] >= 50:\n return Action.USE_EXISTING, {\n **details,\n \"reason\": f\"Error detected - recommending: {best_debug_skill['name']} ({best_debug_skill['score']}%)\",\n \"recommended_skills\": [best_debug_skill[\"name\"]],\n }\n elif top_score >= 50:\n return Action.USE_EXISTING, {\n **details,\n \"reason\": f\"Error handling skill: {top_match['name']} ({top_score}%)\",\n \"recommended_skills\": [m[\"name\"] for m in matches[:3]],\n }\n else:\n return Action.CREATE_NEW, {\n **details,\n \"reason\": \"No error handling skill found. Consider creating one.\",\n }\n\n # Code, URL, or task request - route based on match quality\n if category in [InputCategory.CODE_SNIPPET,\n InputCategory.URL_CONTENT, InputCategory.TASK_REQUEST]:\n\n if multi_domain and top_score >= 50:\n return Action.COMPOSE, {\n **details,\n \"reason\": \"Multiple domains detected. Suggest skill composition.\",\n \"recommended_chain\": [m[\"name\"] for m in matches[:3]],\n }\n elif top_score >= 80:\n return Action.USE_EXISTING, {\n **details,\n \"reason\": f\"Strong match: {top_match['name']} ({top_score}%)\",\n \"recommended_skills\": [m[\"name\"] for m in matches[:3]],\n }\n elif top_score >= 50:\n return Action.IMPROVE_EXISTING, {\n **details,\n \"reason\": f\"Partial match: {top_match['name']} ({top_score}%) could be enhanced for this use case\",\n \"target_skill\": top_match[\"name\"],\n }\n else:\n return Action.CREATE_NEW, {\n **details,\n \"reason\": \"No good existing skill handles this. Consider creating one.\",\n }\n\n # General/unclear input\n if top_score >= 70:\n return Action.USE_EXISTING, {\n **details,\n \"reason\": f\"Best match: {top_match['name']} ({top_score}%)\",\n \"recommended_skills\": [m[\"name\"] for m in matches[:3]],\n }\n elif top_score >= 40:\n return Action.CLARIFY, {\n **details,\n \"reason\": \"Unclear intent. Partial matches found.\",\n \"suggested_action\": \"Ask user to clarify what they need\",\n }\n else:\n return Action.CLARIFY, {\n **details,\n \"reason\": \"Unclear intent and no good skill matches\",\n \"suggested_action\": \"Ask user to elaborate on their goal\",\n }\n\n\n# ===========================================================================\n# MAIN TRIAGE FUNCTION\n# ===========================================================================\n\ndef triage_request(query: str) -> Result:\n \"\"\"\n Analyze any user input and determine the best skill-related action.\n\n Returns:\n Result with action recommendation and supporting data.\n \"\"\"\n # Step 1: Classify input\n category, signals = classify_input(query)\n\n # Step 2: Load skill index\n index = load_skill_index()\n if not index:\n return Result(\n success=False,\n message=\"Skill index not found. Run discover_skills.py first.\",\n errors=[\"Index file missing: ~/.cache/skillrecommender/skill_index.json\"]\n )\n\n skills = index.get(\"skills\", [])\n\n # Step 3: Find matching skills (pass signals for context-aware boosting)\n matches = find_matching_skills(query, skills, signals=signals)\n\n # Step 4: Make decision\n action, details = make_triage_decision(category, signals, matches, query)\n\n # Build response\n return Result(\n success=True,\n message=f\"Triage complete: {action}\",\n data={\n \"action\": action,\n \"details\": details,\n \"input_category\": category,\n \"signals\": signals,\n \"top_matches\": matches[:5],\n \"total_skills_scanned\": len(skills),\n }\n )\n\n\n# ===========================================================================\n# CLI\n# ===========================================================================\n\ndef format_output(result: Result) -> str:\n \"\"\"Format result for human-readable output.\"\"\"\n lines = []\n data = result.data\n\n action = data.get(\"action\", \"UNKNOWN\")\n details = data.get(\"details\", {})\n\n # Header\n lines.append(f\"\\n{'='*60}\")\n lines.append(f\"SKILL TRIAGE RESULT: {action}\")\n lines.append(f\"{'='*60}\")\n\n # Category\n lines.append(f\"\\nInput Category: {data.get('input_category', 'unknown')}\")\n\n # Reason\n if \"reason\" in details:\n lines.append(f\"Reason: {details['reason']}\")\n\n # Top matches\n matches = data.get(\"top_matches\", [])\n if matches:\n lines.append(f\"\\nTop Skill Matches:\")\n for i, m in enumerate(matches[:3], 1):\n lines.append(f\" {i}. {m['name']} ({m['score']}%)\")\n lines.append(f\" {m.get('description', '')[:60]}...\")\n if m.get(\"reasons\"):\n lines.append(f\" Matched: {', '.join(m['reasons'][:2])}\")\n\n # Action-specific guidance\n lines.append(f\"\\n{'─'*60}\")\n lines.append(\"RECOMMENDED NEXT STEP:\")\n\n if action == Action.USE_EXISTING:\n skills = details.get(\"recommended_skills\", [])\n lines.append(f\" Invoke existing skill: {skills[0] if skills else 'unknown'}\")\n lines.append(f\" Example: /{skills[0] if skills else 'skill-name'}\")\n\n elif action == Action.IMPROVE_EXISTING:\n target = details.get(\"target_skill\", \"unknown\")\n lines.append(f\" Improve skill: {target}\")\n lines.append(f\" Command: SkillForge: improve {target}\")\n\n elif action == Action.CREATE_NEW:\n purpose = details.get(\"purpose\", \"the requested functionality\")\n lines.append(f\" Create new skill for: {purpose}\")\n lines.append(f\" Command: SkillForge: create a skill for {purpose}\")\n\n elif action == Action.COMPOSE:\n chain = details.get(\"recommended_chain\", [])\n lines.append(f\" Compose skill chain: {' → '.join(chain)}\")\n lines.append(f\" Use SkillComposer to orchestrate\")\n\n elif action == Action.CLARIFY:\n suggestion = details.get(\"suggested_action\", \"Clarify your intent\")\n lines.append(f\" {suggestion}\")\n\n lines.append(f\"{'─'*60}\\n\")\n\n return \"\\n\".join(lines)\n\n\ndef main():\n parser = argparse.ArgumentParser(\n description=\"Analyze input and recommend skill action\",\n formatter_class=argparse.RawDescriptionHelpFormatter,\n epilog=\"\"\"\nExamples:\n %(prog)s \"create a skill for database migrations\"\n %(prog)s \"help me debug this error\"\n %(prog)s \"do I have a skill for testing?\" --json\n %(prog)s \"TypeError: Cannot read property 'map' of undefined\"\n \"\"\"\n )\n\n parser.add_argument(\n \"query\",\n type=str,\n help=\"The user input to analyze\"\n )\n\n parser.add_argument(\n \"--json\",\n action=\"store_true\",\n help=\"Output results as JSON\"\n )\n\n parser.add_argument(\n \"--verbose\", \"-v\",\n action=\"store_true\",\n help=\"Enable verbose output\"\n )\n\n args = parser.parse_args()\n\n # Run triage\n result = triage_request(args.query)\n\n # Output\n if args.json:\n print(json.dumps(result.to_dict(), indent=2))\n else:\n if result.success:\n print(format_output(result))\n else:\n print(f\"Error: {result.message}\", file=sys.stderr)\n for error in result.errors:\n print(f\" {error}\", file=sys.stderr)\n\n # Exit code\n if not result.success:\n sys.exit(2 if \"index\" in result.message.lower() else 1)\n sys.exit(0)\n\n\nif __name__ == \"__main__\":\n main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":29040,"content_sha256":"ea7b1ae343aba99b5585243d6507d5115628434be983eae85e11f655dd9f929c"},{"filename":"scripts/validate-skill.py","content":"#!/usr/bin/env python3\n\"\"\"\nvalidate-skill.py - Structural validation for Claude Code skills\n\nValidates that a SKILL.md file meets the requirements defined in\nSkillForge 4.0's quality standards.\n\nUsage:\n python validate-skill.py \u003cpath-to-skill-directory>\n python validate-skill.py ~/.claude/skills/my-skill/\n\"\"\"\n\nimport sys\nimport re\nimport os\nfrom pathlib import Path\nfrom typing import List, Tuple, Dict, Any\n\n\nclass SkillValidator:\n \"\"\"Validates skill files against SkillForge 4.0 standards.\"\"\"\n\n def __init__(self, skill_path: str):\n self.skill_path = Path(skill_path)\n self.skill_md_path = self._find_skill_md()\n self.content = \"\"\n self.frontmatter: Dict[str, Any] = {}\n self.errors: List[str] = []\n self.warnings: List[str] = []\n self.checks_passed = 0\n self.checks_total = 0\n\n def _find_skill_md(self) -> Path:\n \"\"\"Find the main skill file (SKILL.md or skill.md).\"\"\"\n for name in [\"SKILL.md\", \"skill.md\"]:\n path = self.skill_path / name\n if path.exists():\n return path\n return self.skill_path / \"SKILL.md\" # Default\n\n def load_skill(self) -> bool:\n \"\"\"Load the skill file content.\"\"\"\n if not self.skill_md_path.exists():\n self.errors.append(f\"Skill file not found: {self.skill_md_path}\")\n return False\n\n try:\n self.content = self.skill_md_path.read_text(encoding=\"utf-8\")\n return True\n except Exception as e:\n self.errors.append(f\"Failed to read skill file: {e}\")\n return False\n\n def parse_frontmatter(self) -> bool:\n \"\"\"Parse YAML frontmatter from skill file.\"\"\"\n match = re.match(r'^---\\n(.*?)\\n---', self.content, re.DOTALL)\n if not match:\n self.errors.append(\"Missing YAML frontmatter\")\n return False\n\n try:\n import yaml\n self.frontmatter = yaml.safe_load(match.group(1))\n return True\n except ImportError:\n # Parse basic fields without yaml library\n frontmatter_text = match.group(1)\n for line in frontmatter_text.split('\\n'):\n if ':' in line:\n key, value = line.split(':', 1)\n self.frontmatter[key.strip()] = value.strip()\n return True\n except Exception as e:\n self.errors.append(f\"Failed to parse frontmatter: {e}\")\n return False\n\n def check(self, name: str, condition: bool, error_msg: str = None, warning: bool = False):\n \"\"\"Run a check and record result.\"\"\"\n self.checks_total += 1\n if condition:\n self.checks_passed += 1\n return True\n else:\n if warning:\n self.warnings.append(error_msg or f\"Check failed: {name}\")\n else:\n self.errors.append(error_msg or f\"Check failed: {name}\")\n return False\n\n def validate_frontmatter(self):\n \"\"\"Validate frontmatter fields.\"\"\"\n required_fields = [\"name\", \"version\", \"description\", \"license\", \"model\"]\n\n for field in required_fields:\n self.check(\n f\"frontmatter.{field}\",\n field in self.frontmatter and self.frontmatter[field],\n f\"Missing required frontmatter field: {field}\"\n )\n\n # Check name format (kebab-case)\n if \"name\" in self.frontmatter:\n name = self.frontmatter[\"name\"]\n self.check(\n \"frontmatter.name.format\",\n re.match(r'^[a-z][a-z0-9-]*

SkillForge 4.0 - Intelligent Skill Router & Creator Analyzes ANY input to find, improve, or create the right skill. When to use - Skill discovery : "Do I have a skill for X?" - Skill creation : "Create a skill for Y" - Skill improvement : "Improve the Z skill" - Routing : Any ambiguous request that needs skill matching How it works 1. Phase 0 Triage : Check if skill exists (no duplicates) 2. Analysis : 11 thinking models evaluate the request 3. Action : Route to existing skill OR create new one 4. Synthesis : Multi-agent panel validates output Triggers - - Direct activation - / - Natural lang…

, str(name)),\n f\"Skill name should be kebab-case: {name}\"\n )\n\n # Check version format (semver)\n if \"version\" in self.frontmatter:\n version = self.frontmatter[\"version\"]\n self.check(\n \"frontmatter.version.format\",\n re.match(r'^\\d+\\.\\d+\\.\\d+', str(version)),\n f\"Version should be semver format: {version}\"\n )\n\n # Check description length\n if \"description\" in self.frontmatter:\n desc = str(self.frontmatter[\"description\"])\n word_count = len(desc.split())\n self.check(\n \"frontmatter.description.length\",\n word_count >= 10,\n f\"Description too short ({word_count} words, minimum 10)\",\n warning=True\n )\n\n def validate_triggers(self):\n \"\"\"Validate trigger phrases section.\"\"\"\n # Find triggers section\n triggers_match = re.search(\n r'##\\s*Triggers\\s*\\n(.*?)(?=\\n##|\\Z)',\n self.content,\n re.DOTALL | re.IGNORECASE\n )\n\n self.check(\n \"section.triggers\",\n triggers_match is not None,\n \"Missing Triggers section\"\n )\n\n if triggers_match:\n triggers_section = triggers_match.group(1)\n # Count trigger phrases (look for backtick-wrapped phrases)\n trigger_count = len(re.findall(r'`[^`]+`', triggers_section))\n\n self.check(\n \"triggers.count\",\n 3 \u003c= trigger_count \u003c= 5,\n f\"Should have 3-5 trigger phrases (found {trigger_count})\"\n )\n\n def validate_process(self):\n \"\"\"Validate process/phases section.\"\"\"\n # Look for Process section or phases\n has_process = bool(re.search(r'##\\s*Process', self.content, re.IGNORECASE))\n has_phases = bool(re.search(r'###\\s*Phase\\s*\\d', self.content, re.IGNORECASE))\n\n self.check(\n \"section.process\",\n has_process or has_phases,\n \"Missing Process section or Phase definitions\"\n )\n\n # Count phases if present\n if has_phases:\n phase_count = len(re.findall(r'###\\s*Phase\\s*\\d', self.content, re.IGNORECASE))\n self.check(\n \"phases.count\",\n 1 \u003c= phase_count \u003c= 3,\n f\"Recommend 1-3 phases, not over-engineered (found {phase_count})\",\n warning=True\n )\n\n def validate_verification(self):\n \"\"\"Validate verification/success criteria section.\"\"\"\n has_verification = bool(re.search(\n r'##\\s*(Verification|Success Criteria|Checklist)',\n self.content,\n re.IGNORECASE\n ))\n\n self.check(\n \"section.verification\",\n has_verification,\n \"Missing Verification/Success Criteria section\"\n )\n\n # Check for checkboxes\n checkbox_count = len(re.findall(r'\\[\\s*\\]', self.content))\n self.check(\n \"verification.checkboxes\",\n checkbox_count >= 2,\n f\"Verification should have concrete checkboxes (found {checkbox_count})\",\n warning=True\n )\n\n def validate_anti_patterns(self):\n \"\"\"Validate anti-patterns section.\"\"\"\n has_anti_patterns = bool(re.search(\n r'##\\s*Anti[-\\s]?Patterns',\n self.content,\n re.IGNORECASE\n ))\n\n self.check(\n \"section.anti_patterns\",\n has_anti_patterns,\n \"Missing Anti-Patterns section\",\n warning=True\n )\n\n def validate_structure(self):\n \"\"\"Validate overall document structure.\"\"\"\n # Check for H1 title\n has_h1 = bool(re.match(r'---.*?---\\s*\\n#\\s+', self.content, re.DOTALL))\n self.check(\n \"structure.h1_title\",\n has_h1,\n \"Missing H1 title after frontmatter\"\n )\n\n # Check for tables (should prefer tables over prose)\n table_count = len(re.findall(r'\\|.*\\|.*\\|', self.content))\n self.check(\n \"structure.tables\",\n table_count >= 1,\n \"Should use tables for structured information\",\n warning=True\n )\n\n # Check for extension points\n has_extensions = bool(re.search(\n r'##\\s*(Extension|Future|Evolution)',\n self.content,\n re.IGNORECASE\n ))\n self.check(\n \"section.extension_points\",\n has_extensions,\n \"Missing Extension Points section\",\n warning=True\n )\n\n def validate_references_directory(self):\n \"\"\"Validate references directory if skill is complex.\"\"\"\n refs_path = self.skill_path / \"references\"\n\n # Complex skills should have references\n line_count = len(self.content.split('\\n'))\n if line_count > 200:\n self.check(\n \"structure.references\",\n refs_path.exists() and any(refs_path.iterdir()),\n \"Complex skill (>200 lines) should have references/ directory\",\n warning=True\n )\n\n def validate_scripts_directory(self):\n \"\"\"Validate scripts directory if present.\"\"\"\n scripts_path = self.skill_path / \"scripts\"\n\n if not scripts_path.exists():\n # Scripts are optional - only warn if skill has bash examples suggesting scripts\n bash_example_count = len(re.findall(r'```bash', self.content))\n python_example_count = len(re.findall(r'python\\s+scripts/', self.content))\n\n if python_example_count > 0:\n self.check(\n \"scripts.presence\",\n False,\n f\"SKILL.md references scripts/ but no scripts directory exists\",\n warning=False\n )\n elif bash_example_count > 3:\n self.check(\n \"scripts.presence\",\n False,\n f\"Skill has {bash_example_count} bash examples - consider adding scripts/\",\n warning=True\n )\n return\n\n # Validate each Python script\n scripts = list(scripts_path.glob(\"*.py\"))\n for script in scripts:\n self._validate_script(script)\n\n # Validate script documentation in SKILL.md\n self._validate_script_documentation(scripts)\n\n def _validate_script(self, script_path: Path):\n \"\"\"Validate a single Python script file.\"\"\"\n try:\n content = script_path.read_text(encoding=\"utf-8\")\n except Exception as e:\n self.check(\n f\"script.{script_path.name}.readable\",\n False,\n f\"Cannot read script {script_path.name}: {e}\"\n )\n return\n\n script_name = script_path.name\n\n # Check for shebang and docstring\n has_shebang = content.strip().startswith('#!/usr/bin/env python3')\n has_docstring = '\"\"\"' in content[:500] or \"'''\" in content[:500]\n self.check(\n f\"script.{script_name}.header\",\n has_shebang and has_docstring,\n f\"Script {script_name} should have shebang and docstring\",\n warning=True\n )\n\n # Check for argparse usage (if main function exists)\n has_main = \"def main():\" in content or 'if __name__' in content\n has_argparse = \"argparse\" in content or \"sys.argv\" in content\n if has_main:\n self.check(\n f\"script.{script_name}.argparse\",\n has_argparse,\n f\"Script {script_name} should use argparse for CLI\",\n warning=True\n )\n\n # Check for explicit exit codes\n has_exit = \"sys.exit\" in content or \"exit(\" in content\n self.check(\n f\"script.{script_name}.exit_codes\",\n has_exit,\n f\"Script {script_name} should use explicit exit codes\",\n warning=True\n )\n\n # Check for error handling\n has_try_except = \"try:\" in content and \"except\" in content\n self.check(\n f\"script.{script_name}.error_handling\",\n has_try_except,\n f\"Script {script_name} should have error handling\",\n warning=True\n )\n\n # Check for result class or validation result pattern\n has_result_pattern = (\n \"Result\" in content or\n \"ValidationResult\" in content or\n \"return (True\" in content or\n \"return (False\" in content\n )\n self.check(\n f\"script.{script_name}.result_pattern\",\n has_result_pattern,\n f\"Script {script_name} should use Result/ValidationResult pattern\",\n warning=True\n )\n\n def _validate_script_documentation(self, scripts: List[Path]):\n \"\"\"Check that scripts are documented in SKILL.md.\"\"\"\n if not scripts:\n return\n\n # Check for Scripts section in SKILL.md\n has_scripts_section = bool(re.search(\n r'##\\s*Scripts',\n self.content,\n re.IGNORECASE\n ))\n\n self.check(\n \"scripts.documented.section\",\n has_scripts_section,\n \"Skills with scripts should have a Scripts section documenting usage\"\n )\n\n # Check that each script is mentioned in SKILL.md\n for script in scripts:\n script_mentioned = script.name in self.content\n self.check(\n f\"scripts.documented.{script.name}\",\n script_mentioned,\n f\"Script {script.name} should be documented in SKILL.md\",\n warning=True\n )\n\n # Check for exit code documentation\n has_exit_docs = bool(re.search(\n r'Exit\\s*Code|exit\\s+code|Exit:\\s*\\d',\n self.content,\n re.IGNORECASE\n ))\n if len(scripts) > 0:\n self.check(\n \"scripts.documented.exit_codes\",\n has_exit_docs,\n \"Skills with scripts should document exit codes\",\n warning=True\n )\n\n def validate(self) -> Tuple[bool, str]:\n \"\"\"Run all validations and return result.\"\"\"\n if not self.load_skill():\n return False, self._format_report()\n\n if not self.parse_frontmatter():\n return False, self._format_report()\n\n self.validate_frontmatter()\n self.validate_triggers()\n self.validate_process()\n self.validate_verification()\n self.validate_anti_patterns()\n self.validate_structure()\n self.validate_references_directory()\n self.validate_scripts_directory()\n\n return len(self.errors) == 0, self._format_report()\n\n def _format_report(self) -> str:\n \"\"\"Format validation report.\"\"\"\n lines = [\n f\"\\n{'='*60}\",\n f\"Skill Validation Report: {self.skill_path.name}\",\n f\"{'='*60}\",\n f\"\\nFile: {self.skill_md_path}\",\n f\"Checks: {self.checks_passed}/{self.checks_total} passed\",\n ]\n\n if self.errors:\n lines.append(f\"\\n{'ERRORS':=^60}\")\n for error in self.errors:\n lines.append(f\" ✗ {error}\")\n\n if self.warnings:\n lines.append(f\"\\n{'WARNINGS':=^60}\")\n for warning in self.warnings:\n lines.append(f\" ⚠ {warning}\")\n\n if not self.errors and not self.warnings:\n lines.append(\"\\n✓ All checks passed!\")\n\n lines.append(f\"\\n{'='*60}\\n\")\n\n return '\\n'.join(lines)\n\n\ndef main():\n \"\"\"Main entry point.\"\"\"\n if len(sys.argv) \u003c 2:\n print(\"Usage: python validate-skill.py \u003cpath-to-skill-directory>\")\n print(\"Example: python validate-skill.py ~/.claude/skills/my-skill/\")\n sys.exit(1)\n\n skill_path = sys.argv[1]\n validator = SkillValidator(skill_path)\n passed, report = validator.validate()\n\n print(report)\n sys.exit(0 if passed else 1)\n\n\nif __name__ == \"__main__\":\n main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":15734,"content_sha256":"62abb35352f8e42ece573f35353186248ddc8aed91b0b1c234fcded72ce57d98"},{"filename":"SESSION_HANDOFF.md","content":"# Session Handoff - skillforge\n\n**Last Updated:** 2025-12-30 11:19\n**Session Duration:** \u003c 1 minute\n\n---\n\n## Git State\n\n- **Branch:** `main`\n- **Stashes:** 0\n- **Uncommitted Files:** 1\n\n### Uncommitted Changes\n```\nSESSION_HANDOFF.md\n```\n\n## Recent Commits\n\n```\n3dc5ed4 Update README to v3.2 with new presentation images\n8e91a2e v3.2.0: Add Script Integration Framework for agentic skills\n3cce6e1 Add methodology screenshots to README\n25f933f v3.1.0: Add progressive disclosure and packaging validation\n78ff66d Initial commit: SkillCreator v3.0\n```\n\n## Session Summary\n\n*[Auto-populated section - add summary of work completed]*\n\n### Work Completed\n- \n\n### In Progress\n- \n\n### Blockers/Issues\n- None identified\n\n## Next Session Priorities\n\n1. \n2. \n3. \n\n---\n\n*This file is auto-generated at session end. Update the summary sections manually or they will be preserved between sessions.*","content_type":"text/markdown; charset=utf-8","language":"markdown","size":883,"content_sha256":"0bf3f9fec968346da984e15fa4410c608ef9d82f55f2bcb96a1e0efdd81fea7e"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"SkillForge 4.0 - Intelligent Skill Router & Creator","type":"text"}]},{"type":"paragraph","content":[{"text":"Analyzes ANY input to find, improve, or create the right skill.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When to use","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Skill discovery","type":"text","marks":[{"type":"strong"}]},{"text":": \"Do I have a skill for X?\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Skill creation","type":"text","marks":[{"type":"strong"}]},{"text":": \"Create a skill for Y\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Skill improvement","type":"text","marks":[{"type":"strong"}]},{"text":": \"Improve the Z skill\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Routing","type":"text","marks":[{"type":"strong"}]},{"text":": Any ambiguous request that needs skill matching","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"How it works","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Phase 0 Triage","type":"text","marks":[{"type":"strong"}]},{"text":": Check if skill exists (no duplicates)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Analysis","type":"text","marks":[{"type":"strong"}]},{"text":": 11 thinking models evaluate the request","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Action","type":"text","marks":[{"type":"strong"}]},{"text":": Route to existing skill OR create new one","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Synthesis","type":"text","marks":[{"type":"strong"}]},{"text":": Multi-agent panel validates output","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Triggers","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"SkillForge: {goal}","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Direct activation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"create skill","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"improve skill","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Natural language","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"do I have a skill for X?","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Discovery","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Core Principles","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"No duplication","type":"text","marks":[{"type":"strong"}]},{"text":": Always check existing skills first","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Iterative","type":"text","marks":[{"type":"strong"}]},{"text":": Refine through multiple analysis passes","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Practical","type":"text","marks":[{"type":"strong"}]},{"text":": Focus on actionable skills, not theory","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Outputs","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"New skill: ","type":"text"},{"text":"SKILL.md","type":"text","marks":[{"type":"code_inline"}]},{"text":", references/, scripts/","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Improvement: Modified SKILL.md + recommendations","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Discovery: List of matching existing skills","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"skillforge","author":"@skillopedia","source":{"stars":65,"repo_name":"claude-code-skills","origin_url":"https://github.com/aaaaqwq/claude-code-skills/blob/HEAD/skills/skillforge/SKILL.md","repo_owner":"aaaaqwq","body_sha256":"315abaa3d20b9c495e7c39bf8f8f8bd983c69b6a97e953084a3fcf9af174542b","cluster_key":"80e6f13b5b7085ee9f5b13666af931e6c6150186ed1889381dacd83d56386d24","clean_bundle":{"format":"clean-skill-bundle-v1","source":"aaaaqwq/claude-code-skills/skills/skillforge/SKILL.md","attachments":[{"id":"29d314d9-fc69-5ab3-84c0-e89554cabc6c","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/29d314d9-fc69-5ab3-84c0-e89554cabc6c/attachment","path":".gitignore","size":533,"sha256":"50a512c8f978ed2a4810ff795d00e0a023abfbcb4ac0e88b98f477279424db5c","contentType":"text/plain; charset=utf-8"},{"id":"517df313-36e0-55f8-91bd-23947804502a","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/517df313-36e0-55f8-91bd-23947804502a/attachment.md","path":"README.md","size":9163,"sha256":"5e58de8c7ea4774e12051e9d56a6a2ec4c43b0866037717386b32b3107d5b0c4","contentType":"text/markdown; charset=utf-8"},{"id":"e34cb28c-df70-5963-a75d-a2a497c8289f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/e34cb28c-df70-5963-a75d-a2a497c8289f/attachment.md","path":"SESSION_HANDOFF.md","size":883,"sha256":"0bf3f9fec968346da984e15fa4410c608ef9d82f55f2bcb96a1e0efdd81fea7e","contentType":"text/markdown; charset=utf-8"},{"id":"305db7e8-93d2-544a-94a2-fc078bc738c0","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/305db7e8-93d2-544a-94a2-fc078bc738c0/attachment.py","path":"assets/templates/script-template.py","size":8813,"sha256":"dc688f4af2d24ddeb1875e6cedcaab0d02e61623cc435e00933402ddc6f877f1","contentType":"text/x-python; charset=utf-8"},{"id":"2e2655f1-5883-5613-b52a-395fdadd51b2","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/2e2655f1-5883-5613-b52a-395fdadd51b2/attachment.md","path":"assets/templates/skill-md-template.md","size":1267,"sha256":"099b2540c1484497c2426d31667aa04d6c4317e9079e846fb985a682fdabfccf","contentType":"text/markdown; charset=utf-8"},{"id":"2604b621-9507-54c4-8944-5aed3fca85ed","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/2604b621-9507-54c4-8944-5aed3fca85ed/attachment.xml","path":"assets/templates/skill-spec-template.xml","size":17558,"sha256":"58a4c3f18d2b7e35138bf1058d787f594d46638756ba61e344553b7ee476e3cc","contentType":"application/xml"},{"id":"2c782687-158c-5768-9e59-67638b672de9","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/2c782687-158c-5768-9e59-67638b672de9/attachment.md","path":"references/evolution-scoring.md","size":8663,"sha256":"4df95d9915bfa0e14d9101fd43c173209b64a092d1f762a44f8113da126553a9","contentType":"text/markdown; charset=utf-8"},{"id":"c59c5bab-d3d9-5c1d-81a5-e0afb1493965","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/c59c5bab-d3d9-5c1d-81a5-e0afb1493965/attachment.md","path":"references/multi-lens-framework.md","size":10522,"sha256":"c1daf3c504f8e5338708b07a12f1bca42940c82413c56ca33972730abf4935d1","contentType":"text/markdown; charset=utf-8"},{"id":"dccf611c-dca1-5e74-9af0-b3cd23ca2952","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/dccf611c-dca1-5e74-9af0-b3cd23ca2952/attachment.md","path":"references/regression-questions.md","size":11210,"sha256":"b934bd9596e74585296ec916f7fa8120921b4813aaf5aeb9f6213dd80d8cf996","contentType":"text/markdown; charset=utf-8"},{"id":"82049e98-28f1-5be5-9af6-f2931a4b550a","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/82049e98-28f1-5be5-9af6-f2931a4b550a/attachment.md","path":"references/script-integration-framework.md","size":14244,"sha256":"a1aae2e1014e658f547999dc36e012187bb11ad7e1246006ad665a9612f268ba","contentType":"text/markdown; charset=utf-8"},{"id":"6d23b00e-072b-5e77-93c9-2cd4e645ef01","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/6d23b00e-072b-5e77-93c9-2cd4e645ef01/attachment.md","path":"references/script-patterns-catalog.md","size":21550,"sha256":"ac036d192b627a7bda2c87286360e16e693123d268f4af6aacc98c5ea7a21733","contentType":"text/markdown; charset=utf-8"},{"id":"3076ccf2-5f6c-5b6b-9375-193970ccd822","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/3076ccf2-5f6c-5b6b-9375-193970ccd822/attachment.md","path":"references/specification-template.md","size":16520,"sha256":"50d0a4fcf32cc7e01f58377bcad609f4177389c011f1944f146ae23b3c245184","contentType":"text/markdown; charset=utf-8"},{"id":"27868a9e-9920-51e3-936b-7b79d151efa7","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/27868a9e-9920-51e3-936b-7b79d151efa7/attachment.md","path":"references/synthesis-protocol.md","size":14644,"sha256":"3a83e8d02bdbacd35bbf9350f78a2104b8a131a654a42559feecaa07804466b5","contentType":"text/markdown; charset=utf-8"},{"id":"1c7f77be-de97-5820-a783-2becf88494d9","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/1c7f77be-de97-5820-a783-2becf88494d9/attachment.py","path":"scripts/discover_skills.py","size":13757,"sha256":"8cb6772ba387df58daffdf11c5d948e893dee66caf24389c3c1fce6e04c82477","contentType":"text/x-python; charset=utf-8"},{"id":"cef69f3d-b234-56b7-82f5-549a0947aaa2","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/cef69f3d-b234-56b7-82f5-549a0947aaa2/attachment.py","path":"scripts/package_skill.py","size":3787,"sha256":"d187b23a262a8d68072a7be137fcea2e1ffa28401325b5a9ec010847d62bfae9","contentType":"text/x-python; charset=utf-8"},{"id":"9eca56cd-c6e1-5e49-9192-bf056c718175","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/9eca56cd-c6e1-5e49-9192-bf056c718175/attachment.py","path":"scripts/quick_validate.py","size":4898,"sha256":"ddda0a8acf92f9c04bfad77a752e1e44759c86c93babe5ebc69d14e4ef0b8633","contentType":"text/x-python; charset=utf-8"},{"id":"20f5671d-7f61-55b9-9105-0529277efd8c","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/20f5671d-7f61-55b9-9105-0529277efd8c/attachment.py","path":"scripts/triage_skill_request.py","size":29040,"sha256":"ea7b1ae343aba99b5585243d6507d5115628434be983eae85e11f655dd9f929c","contentType":"text/x-python; charset=utf-8"},{"id":"8030d7c4-48b6-52f0-8d7d-21e281fa80e1","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/8030d7c4-48b6-52f0-8d7d-21e281fa80e1/attachment.py","path":"scripts/validate-skill.py","size":15734,"sha256":"62abb35352f8e42ece573f35353186248ddc8aed91b0b1c234fcded72ce57d98","contentType":"text/x-python; charset=utf-8"}],"bundle_sha256":"e1e17ffaf1cb225503ed0446ef647f11f98159962b1788ce4834855ecf6cae2e","attachment_count":18,"text_attachments":18,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"skills/skillforge/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"testing-qa","category_label":"Testing"},"exact_dupes_collapsed_into_this":0},"license":"MIT","version":"v1","category":"testing-qa","metadata":{"type":"orchestrator","model":"claude-opus-4-5-20251101","inputs":["any-input","user-goal","domain-hints"],"domains":["meta-skill","automation","skill-creation","orchestration","agentic","routing"],"outputs":["SKILL.md","references/","scripts/","SKILL_SPEC.md","recommendations"],"version":"4.0.0","subagent_model":"claude-opus-4-5-20251101"},"import_tag":"clean-skills-v1","description":"Intelligent skill router and creator. Analyzes ANY input to recommend existing skills, improve them, or create new ones. Uses deep iterative analysis with 11 thinking models, regression questioning, evolution lens, and multi-agent synthesis panel. Phase 0 triage ensures you never duplicate existing functionality."}},"renderedAt":1782986385609}

SkillForge 4.0 - Intelligent Skill Router & Creator Analyzes ANY input to find, improve, or create the right skill. When to use - Skill discovery : "Do I have a skill for X?" - Skill creation : "Create a skill for Y" - Skill improvement : "Improve the Z skill" - Routing : Any ambiguous request that needs skill matching How it works 1. Phase 0 Triage : Check if skill exists (no duplicates) 2. Analysis : 11 thinking models evaluate the request 3. Action : Route to existing skill OR create new one 4. Synthesis : Multi-agent panel validates output Triggers - - Direct activation - / - Natural lang…