ChatGPT Claude Codex CLI Cohere Cursor DeepSeek Gemini GitHub Copilot GLM Grok Kimi Llama MiniMax Mistral OpenAI opencode Skill

agent-orchestrator

Meta-skill que orquestra todos os agentes do ecossistema. Scan automatico de skills, match por capacidades, coordenacao de workflows multi-skill e registry management.

LLM Mart · 0 points · 21 views 1 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download sickn33-agentic-awesome-skills-skills_agent-orchestrator-286166a.zip · 20 KB
Part of sickn33/agentic-awesome-skills — 427 skills
This skill couldn't be refreshed from GitHub on the last check — you're seeing the last imported snapshot.

Install

skills CLI npx skills add https://github.com/sickn33/agentic-awesome-skills/tree/main/skills/agent-orchestrator
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install sickn33-agentic-awesome-skills@llmmart
Git git clone https://github.com/sickn33/agentic-awesome-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole sickn33/agentic-awesome-skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Agent Orchestrator

Overview

Meta-skill que orquestra todos os agentes do ecossistema. Scan automatico de skills, match por capacidades, coordenacao de workflows multi-skill e registry management.

When to Use This Skill

  • When you need specialized assistance with this domain

Do Not Use This Skill When

  • The task is unrelated to agent orchestrator
  • A simpler, more specific tool can handle the request
  • The user needs general-purpose assistance without domain expertise

How It Works

Meta-skill que funciona como camada central de decisao e coordenacao para todo o ecossistema de skills. Faz varredura automatica, identifica agentes relevantes e orquestra multiplos skills para tarefas complexas.

Principio: Zero Intervencao Manual

  • SEMPRE faz varredura antes de processar qualquer solicitacao
  • Novas skills sao auto-detectadas e incluidas ao criar SKILL.md em qualquer subpasta
  • Skills removidas sao auto-excluidas do registry
  • Nenhum comando manual e necessario para registrar novas skills

Workflow Obrigatorio (Toda Solicitacao)

Execute estes passos ANTES de processar qualquer request do usuario. Os scripts usam paths relativos automaticamente - funciona de qualquer diretorio.

Passo 1: Auto-Discovery (Varredura)

python agent-orchestrator/scripts/scan_registry.py

Ultra-rapido (<100ms) via cache de hashes MD5. So re-processa arquivos alterados. Retorna JSON com resumo de todos os skills encontrados.

Passo 2: Match De Skills

python agent-orchestrator/scripts/match_skills.py "<solicitacao do usuario>"

Retorna JSON com skills ranqueadas por relevancia. Interpretar o resultado:

Resultado Acao
matched: 0 Nenhum skill relevante. Operar normalmente sem skills.
matched: 1 Um skill relevante. Carregar seu SKILL.md e seguir.
matched: 2+ Multiplos skills. Executar Passo 3 (orquestracao).

Passo 3: Orquestracao (Se Matched >= 2)

python agent-orchestrator/scripts/orchestrate.py --skills skill1,skill2 --query "<solicitacao>"

Retorna plano de execucao com padrao, ordem dos steps e data flow entre skills.

Passo Rapido (Atalho)

Para queries simples, os passos 1+2 podem ser combinados em sequencia:

python agent-orchestrator/scripts/scan_registry.py && python agent-orchestrator/scripts/match_skills.py "<solicitacao>"

Skill Registry

O registry vive em:

agent-orchestrator/data/registry.json

Locais De Busca

O scanner procura SKILL.md em:

  1. .claude/skills/*/ (skills registradas no Claude Code)
  2. */ (skills standalone no top-level)
  3. */*\ (skills em subpastas, ate profundidade 3)

Metadata Por Skill

Cada entrada no registry contem:

Campo Descricao
name Nome da skill (do frontmatter YAML)
description Descricao completa (triggers inclusos)
location Caminho absoluto do diretorio
skill_md Caminho absoluto do SKILL.md
registered Se esta em .claude/skills/ (true/false)
capabilities Tags de capacidade (auto-extraidas + explicitas)
triggers Keywords de ativacao extraidas da description
language Linguagem principal (python/nodejs/bash/none)
status active / incomplete / missing

Comandos Do Registry


## Scan Rapido (Usa Cache De Hashes)

python agent-orchestrator/scripts/scan_registry.py

## Tabela De Status Detalhada

python agent-orchestrator/scripts/scan_registry.py --status

## Re-Scan Completo (Ignora Cache)

python agent-orchestrator/scripts/scan_registry.py --force

Algoritmo De Matching

Para cada solicitacao, o matcher pontua skills usando:

Criterio Pontos Exemplo
Nome do skill na query +15 "use web-scraper" -> web-scraper
Keyword trigger exata +10 "scrape" -> web-scraper
Categoria de capacidade +5 data-extraction -> web-scraper
Sobreposicao de palavras +1 Palavras da query na description
Boost de projeto +20 Skill atribuida ao projeto ativo

Threshold minimo: 5 pontos. Skills abaixo disso sao ignoradas.

Match Com Projeto

python agent-orchestrator/scripts/match_skills.py --project meu-projeto "query aqui"

Skills atribuidas ao projeto recebem +20 de boost automatico.


Padroes De Orquestracao

Quando multiplos skills sao relevantes, o orchestrator classifica o padrao:

1. Pipeline Sequencial

Skills formam uma cadeia onde o output de uma alimenta a proxima.

Quando: Mix de skills "produtoras" (data-extraction, government-data) e "consumidoras" (messaging, social-media).

Exemplo: web-scraper coleta precos -> whatsapp-cloud-api envia alerta

user_query -> web-scraper -> whatsapp-cloud-api -> result

2. Execucao Paralela

Skills trabalham independentemente em aspectos diferentes da solicitacao.

Quando: Todas as skills tem o mesmo papel (todas produtoras ou todas consumidoras).

Exemplo: instagram publica post + whatsapp envia notificacao (ambos recebem o mesmo conteudo)

user_query -> [instagram, whatsapp-cloud-api] -> aggregated_result

3. Primario + Suporte

Uma skill principal lidera; outras fornecem dados de apoio.

Quando: Uma skill tem score muito superior as demais (>= 2x).

Exemplo: whatsapp-cloud-api envia mensagem (primario) + web-scraper fornece dados (suporte)

user_query -> whatsapp-cloud-api (primary) + web-scraper (support) -> result

Detalhes Em References/Orchestration-Patterns.Md


Gerenciamento De Projetos

Atribuir skills a projetos permite boost de relevancia e contexto persistente.

Arquivo De Projetos

agent-orchestrator/data/projects.json

Operacoes

Criar projeto: Adicionar entrada ao projects.json:

{
  "name": "nome-do-projeto",
  "created_at": "2026-02-25T12:00:00",
  "skills": ["web-scraper", "whatsapp-cloud-api"],
  "description": "Descricao do projeto"
}

Adicionar skill a projeto: Atualizar o array skills do projeto.

Remover skill de projeto: Remover do array skills.

Consultar skills do projeto: Ler o projects.json e listar skills atribuidas.


Adicionando Novas Skills

Para adicionar uma nova skill ao ecossistema:

  1. Criar uma pasta em qualquer lugar sob skills root:
  2. Criar um SKILL.md com frontmatter YAML:
---
name: minha-nova-skill
description: "Descricao com keywords de ativacao..."
---

## Documentacao Da Skill

  1. Pronto! O auto-discovery detecta automaticamente na proxima solicitacao.

Opcionalmente, para discovery nativo do Claude Code: 4. Copiar o SKILL.md para .claude/skills/<nome>/SKILL.md

Tags De Capacidade Explicitas (Opcional)

Adicionar ao frontmatter para matching mais preciso:

capabilities: [data-extraction, web-automation]

Ver Status De Todos Os Skills

python agent-orchestrator/scripts/scan_registry.py --status

Interpretar Status

Status Significado
active SKILL.md com name + description presentes
incomplete SKILL.md existe mas falta name ou description
missing Diretorio existe mas sem SKILL.md

Skills Atuais Do Ecossistema

Skill Capacidades Status
web-scraper data-extraction, web-automation active
junta-leiloeiros government-data, data-extraction active
whatsapp-cloud-api messaging, api-integration active
instagram social-media, api-integration partial

Esta tabela e atualizada automaticamente via scan_registry.py --status.

Best Practices

  • Provide clear, specific context about your project and requirements
  • Review all suggestions before applying them to production code
  • Combine with other complementary skills for comprehensive analysis

Common Pitfalls

  • Using this skill for tasks outside its domain expertise
  • Applying recommendations without understanding your specific context
  • Not providing enough project context for accurate analysis

Related Skills

  • multi-advisor - Complementary skill for enhanced analysis
  • task-intelligence - Complementary skill for enhanced analysis

Limitations

  • Use this skill only when the task clearly matches the scope described above.
  • Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
  • Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.
Files (agentic-awesome-skills)
  • references
    • capability-taxonomy.md 3.2 KB
      # Taxonomia de Capacidades (Capability Tags)
      
      Categorias padrao para classificar skills no ecossistema.
      Cada skill pode ter multiplas categorias.
      
      ---
      
      ## Categorias
      
      ### data-extraction
      **Descricao:** Coleta e extracao de dados de fontes web ou APIs.
      **Keywords PT:** raspar, extrair, coletar, dados, tabela
      **Keywords EN:** scrape, extract, crawl, parse, harvest, collect, data, table, csv
      **Skills atuais:** web-scraper, junta-leiloeiros
      
      ### messaging
      **Descricao:** Envio e recebimento de mensagens via plataformas de comunicacao.
      **Keywords PT:** mensagem, enviar, notificacao, atendimento, comunicar, avisar
      **Keywords EN:** whatsapp, message, send, chat, notify, notification, sms
      **Skills atuais:** whatsapp-cloud-api
      
      ### social-media
      **Descricao:** Interacao com plataformas de redes sociais (posts, stories, analytics).
      **Keywords PT:** publicar, rede social, engajamento, post, stories
      **Keywords EN:** instagram, facebook, twitter, post, stories, reels, social, feed, follower
      **Skills atuais:** instagram
      
      ### government-data
      **Descricao:** Coleta de dados governamentais, registros publicos, orgaos oficiais.
      **Keywords PT:** junta, leiloeiro, cadastro, governo, comercial, tribunal, certidao, registro
      **Keywords EN:** government, registry, official, court, public records
      **Skills atuais:** junta-leiloeiros
      
      ### web-automation
      **Descricao:** Automacao de navegador, preenchimento de formularios, interacao com paginas.
      **Keywords PT:** navegador, automatizar, automacao, preencher
      **Keywords EN:** browser, selenium, playwright, automate, click, fill form
      **Skills atuais:** web-scraper
      
      ### api-integration
      **Descricao:** Integracao com APIs externas, webhooks, autenticacao OAuth.
      **Keywords PT:** integracao, integrar, conectar, api, webhook
      **Keywords EN:** api, endpoint, webhook, rest, graph, oauth, token
      **Skills atuais:** whatsapp-cloud-api, instagram
      
      ### analytics
      **Descricao:** Analise de dados, metricas, dashboards, relatorios.
      **Keywords PT:** relatorio, metricas, analise, estatistica
      **Keywords EN:** insight, analytics, metrics, dashboard, report, stats
      **Skills atuais:** (nenhuma dedicada ainda)
      
      ### content-management
      **Descricao:** Publicacao, agendamento e gestao de conteudo em plataformas.
      **Keywords PT:** publicar, agendar, conteudo, midia, template
      **Keywords EN:** publish, schedule, template, content, media, upload
      **Skills atuais:** instagram
      
      ---
      
      ## Roles (Papeis)
      
      As categorias se agrupam em papeis para orquestracao:
      
      | Papel      | Categorias                                      | Descricao                        |
      |:-----------|:------------------------------------------------|:---------------------------------|
      | Producer   | data-extraction, government-data, analytics     | Gera/coleta dados                |
      | Consumer   | messaging, social-media, content-management     | Atua sobre dados (envia, publica)|
      | Hybrid     | api-integration, web-automation                 | Pode produzir e consumir dados   |
      
      ---
      
      ## Como Declarar no SKILL.md
      
      Adicionar campo `capabilities` ao frontmatter YAML:
      
      ```yaml
      ---
      name: minha-skill
      description: "..."
      capabilities: [data-extraction, web-automation]
      ---
      ```
      
      Se omitido, o scanner extrai automaticamente da `description` via keywords.
      Tags explicitas tem prioridade e nao sao duplicadas com as auto-extraidas.
      
    • orchestration-patterns.md 3.8 KB
      # Padroes de Orquestracao Multi-Skill
      
      Guia detalhado para coordenar multiplos skills em workflows complexos.
      
      ---
      
      ## 1. Pipeline Sequencial
      
      Output de um skill alimenta o input do proximo.
      
      ### Quando Usar
      - Mix de skills "produtoras" (data-extraction, government-data, analytics) e "consumidoras" (messaging, social-media, content-management)
      - A tarefa tem etapas distintas: coletar -> processar -> entregar
      
      ### Fluxo
      ```
      user_query -> Skill A (produtora) -> dados -> Skill B (consumidora) -> resultado
      ```
      
      ### Exemplo Concreto
      **Solicitacao:** "Coletar precos de leiloeiros de SP e enviar por WhatsApp"
      ```
      1. junta-leiloeiros: Executar scraper para SP, exportar dados
      2. whatsapp-cloud-api: Formatar dados como mensagem e enviar
      ```
      
      ### Regras de Contexto
      - O output de cada step deve ser passado como contexto para o proximo
      - Formatos comuns de passagem: JSON, tabela Markdown, texto resumido
      - Se um step falhar, interromper o pipeline e reportar ao usuario
      
      ---
      
      ## 2. Execucao Paralela
      
      Skills trabalham independentemente em aspectos diferentes.
      
      ### Quando Usar
      - Todas as skills tem o mesmo papel (todas produtoras OU todas consumidoras)
      - Os aspectos da tarefa sao independentes entre si
      - Nao ha dependencia de dados entre skills
      
      ### Fluxo
      ```
                    ┌─> Skill A ─> output A ─┐
      user_query ──>├─> Skill B ─> output B ─├──> resultado agregado
                    └─> Skill C ─> output C ─┘
      ```
      
      ### Exemplo Concreto
      **Solicitacao:** "Publicar a promocao no Instagram e enviar por WhatsApp"
      ```
      1. (paralelo) instagram: Criar e publicar post da promocao
      1. (paralelo) whatsapp-cloud-api: Enviar mensagem da promocao
      -> Agregar: reportar status de ambas as publicacoes
      ```
      
      ### Regras de Contexto
      - Cada skill recebe a query original completa
      - Os outputs sao agregados em uma resposta unificada
      - Se um skill falhar, os outros continuam normalmente
      - Reportar sucesso/falha de cada skill individualmente
      
      ---
      
      ## 3. Primario + Suporte
      
      Uma skill principal lidera; outras fornecem dados de apoio.
      
      ### Quando Usar
      - Uma skill tem score de relevancia muito superior (>= 2x a proxima)
      - A tarefa principal e clara, mas pode se beneficiar de dados adicionais
      - Skills de suporte sao opcionais / "nice to have"
      
      ### Fluxo
      ```
      user_query -> Skill A (primaria) ──────────────> resultado
                        ↑
                    Skill B (suporte) ─> dados extras
      ```
      
      ### Exemplo Concreto
      **Solicitacao:** "Configurar chatbot WhatsApp para responder com dados de leiloeiros"
      ```
      1. (primaria) whatsapp-cloud-api: Configurar webhook e logica do chatbot
      2. (suporte) junta-leiloeiros: Fornecer endpoint/dados para o chatbot consultar
      ```
      
      ### Regras de Contexto
      - A skill primaria conduz o workflow
      - Skills de suporte sao consultadas sob demanda
      - Se skill de suporte falhar, a primaria deve continuar (graceful degradation)
      
      ---
      
      ## Tratamento de Erros
      
      ### Regras Gerais
      1. **Falha em skill individual**: Reportar ao usuario qual skill falhou e por que
      2. **Falha em pipeline**: Interromper e mostrar ate onde chegou
      3. **Falha parcial em paralelo**: Continuar com as demais, reportar falha(s)
      4. **Skill incomplete**: Avisar que a skill esta com status incompleto antes de tentar usa-la
      
      ### Fallback
      - Se uma skill falha, verificar se outra skill tem capacidade similar
      - Se nao houver alternativa, operar sem a skill e informar o usuario
      
      ---
      
      ## Serializacao de Contexto
      
      Formato padrao para passar dados entre skills:
      
      ```json
      {
        "source_skill": "web-scraper",
        "target_skill": "whatsapp-cloud-api",
        "data_type": "table",
        "data": [
          {"nome": "Joao Silva", "uf": "SP", "registro": "12345"},
          {"nome": "Maria Santos", "uf": "RJ", "registro": "67890"}
        ],
        "metadata": {
          "total_items": 2,
          "collected_at": "2026-02-25T12:00:00",
          "query": "leiloeiros de SP e RJ"
        }
      }
      ```
      
  • scripts
    • match_skills.py 11.5 KB
      #!/usr/bin/env python3
      """
      Skill Matching Algorithm for Agent Orchestrator.
      
      Scores and ranks skills against a user query to determine
      which agents are relevant for the current request.
      
      Scoring:
      - Skill name appears in query: +15
      - Exact trigger keyword match: +10 per keyword
      - Capability category match:   +5 per category
      - Description word overlap:    +1 per word
      - Project assignment boost:    +20 if skill is assigned to active project
      
      Usage:
          python match_skills.py "raspar dados de um site"
          python match_skills.py "coletar precos e enviar por whatsapp"
          python match_skills.py --project myproject "query here"
      """
      
      import json
      import sys
      import os
      import re
      import subprocess
      from pathlib import Path
      
      # ── Configuration ──────────────────────────────────────────────────────────
      
      # Resolve paths relative to this script's location
      _SCRIPT_DIR = Path(__file__).resolve().parent
      ORCHESTRATOR_DIR = _SCRIPT_DIR.parent
      SKILLS_ROOT = ORCHESTRATOR_DIR.parent
      DATA_DIR = ORCHESTRATOR_DIR / "data"
      REGISTRY_PATH = DATA_DIR / "registry.json"
      PROJECTS_PATH = DATA_DIR / "projects.json"
      SCAN_SCRIPT = _SCRIPT_DIR / "scan_registry.py"
      
      # Capability keywords for query -> category matching (PT + EN)
      CAPABILITY_KEYWORDS = {
          "data-extraction": [
              "scrape", "extract", "crawl", "parse", "harvest", "collect", "data",
              "raspar", "extrair", "coletar", "dados", "tabela", "table", "csv",
              "web data", "pull info", "get data",
          ],
          "messaging": [
              "whatsapp", "message", "send", "chat", "notify", "notification", "sms",
              "mensagem", "enviar", "notificar", "notificacao", "atendimento",
              "comunicar", "avisar",
          ],
          "social-media": [
              "instagram", "facebook", "twitter", "post", "stories", "reels",
              "social", "feed", "follower", "publicar", "rede social", "engajamento",
          ],
          "government-data": [
              "junta", "leiloeiro", "cadastro", "governo", "comercial", "tribunal",
              "diario oficial", "certidao", "registro", "uf", "estado",
          ],
          "web-automation": [
              "browser", "selenium", "playwright", "automate", "click", "fill form",
              "navegador", "automatizar", "automacao", "preencher",
          ],
          "api-integration": [
              "api", "endpoint", "webhook", "rest", "graph", "oauth", "token",
              "integracao", "integrar", "conectar",
          ],
          "analytics": [
              "insight", "analytics", "metrics", "dashboard", "report", "stats",
              "relatorio", "metricas", "analise", "estatistica",
          ],
          "content-management": [
              "publish", "schedule", "template", "content", "media", "upload",
              "publicar", "agendar", "conteudo", "midia",
          ],
          "legal": [
              "advogado", "direito", "juridico", "lei", "processo",
              "acao", "peticao", "recurso", "sentenca", "juiz",
              "divorcio", "guarda", "alimentos", "pensao", "alimenticia", "inventario", "heranca", "partilha",
              "acidente de trabalho", "acidente",
              "familia", "criminal", "penal", "crime", "feminicidio", "maria da penha",
              "violencia domestica", "medida protetiva", "stalking",
              "danos morais", "responsabilidade civil", "indenizacao", "dano",
              "consumidor", "cdc", "plano de saude",
              "trabalhista", "clt", "rescisao", "fgts", "horas extras",
              "previdenciario", "aposentadoria", "aposentar", "inss",
              "imobiliario", "usucapiao", "despejo", "inquilinato",
              "alienacao fiduciaria", "bem de familia",
              "tributario", "imposto", "icms", "execucao fiscal",
              "administrativo", "licitacao", "improbidade", "mandado de seguranca",
              "empresarial", "societario", "falencia", "recuperacao judicial",
              "empresa", "ltda", "cnpj", "mei", "eireli", "contrato social",
              "contrato", "clausula", "contestacao", "apelacao", "agravo",
              "habeas corpus", "mandado", "liminar", "tutela",
              "cpc", "stj", "stf", "sumula", "jurisprudencia",
              "oab", "honorarios", "custas",
          ],
          "auction": [
              "leilao", "leilao judicial", "leilao extrajudicial", "hasta publica",
              "arrematacao", "arrematar", "arrematante", "lance", "desagio",
              "edital leilao", "penhora", "adjudicacao", "praca",
              "imissao na posse", "carta arrematacao", "vil preco",
              "avaliacao imovel", "laudo", "perito", "matricula",
              "leiloeiro", "comissao leiloeiro",
          ],
          "security": [
              "seguranca", "security", "owasp", "vulnerability", "incident",
              "pentest", "firewall", "malware", "phishing", "cve",
              "autenticacao", "criptografia", "encryption",
          ],
          "image-generation": [
              "imagem", "image", "gerar imagem", "generate image",
              "stable diffusion", "comfyui", "midjourney", "dall-e",
              "foto", "ilustracao", "arte", "design",
          ],
          "monitoring": [
              "monitor", "monitorar", "health", "status",
              "audit", "auditoria", "sentinel", "check",
          ],
          "context-management": [
              "contexto", "context", "sessao", "session", "compactacao", "compaction",
              "comprimir", "compress", "snapshot", "checkpoint", "briefing",
              "continuidade", "continuity", "preservar", "preserve",
              "memoria", "memory", "resumo", "summary",
              "salvar estado", "save state", "context window", "janela de contexto",
              "perda de dados", "data loss", "backup",
          ],
      }
      
      
      # ── Functions ──────────────────────────────────────────────────────────────
      
      def ensure_registry():
          """Run scan if registry doesn't exist."""
          if not REGISTRY_PATH.exists():
              subprocess.run(
                  [sys.executable, str(SCAN_SCRIPT)],
                  capture_output=True, text=True
              )
      
      
      def load_registry() -> list[dict]:
          """Load skills from registry.json."""
          ensure_registry()
          if not REGISTRY_PATH.exists():
              return []
          try:
              data = json.loads(REGISTRY_PATH.read_text(encoding="utf-8"))
              return data.get("skills", [])
          except Exception:
              return []
      
      
      def load_projects() -> dict:
          """Load project assignments."""
          if not PROJECTS_PATH.exists():
              return {"projects": []}
          try:
              return json.loads(PROJECTS_PATH.read_text(encoding="utf-8"))
          except Exception:
              return {"projects": []}
      
      
      def get_project_skills(project_name: str) -> set:
          """Get set of skill names assigned to a project."""
          projects = load_projects()
          for p in projects.get("projects", []):
              if p.get("name", "").lower() == project_name.lower():
                  return set(p.get("skills", []))
          return set()
      
      
      def query_to_capabilities(query: str) -> list[str]:
          """Map a query to capability categories using word boundary matching."""
          q_lower = query.lower()
          q_words = set(re.findall(r'[a-zA-ZÀ-ÿ]+', q_lower))
          caps = []
          for cap, keywords in CAPABILITY_KEYWORDS.items():
              for kw in keywords:
                  # Multi-word keywords: substring match. Single-word: exact word match.
                  if " " in kw:
                      if kw in q_lower:
                          caps.append(cap)
                          break
                  elif kw in q_words:
                      caps.append(cap)
                      break
          return caps
      
      
      def normalize(text: str) -> set[str]:
          """Normalize text to a set of lowercase words."""
          return set(re.findall(r'[a-zA-ZÀ-ÿ]{3,}', text.lower()))
      
      
      def score_skill(skill: dict, query: str, project_skills: set = None) -> dict:
          """
          Score a skill's relevance to a query.
      
          Returns dict with score, reasons, and skill info.
          """
          q_lower = query.lower()
          score = 0
          reasons = []
      
          name = skill.get("name", "")
          description = skill.get("description", "")
          triggers = skill.get("triggers", [])
          capabilities = skill.get("capabilities", [])
      
          # 1. Skill name in query (+15)
          if name.lower() in q_lower or name.lower().replace("-", " ") in q_lower:
              score += 15
              reasons.append(f"name:{name}")
      
          # 2. Trigger keyword matches (+10 each) - word boundary matching
          q_words = set(re.findall(r'[a-zA-ZÀ-ÿ]+', q_lower))
          for trigger in triggers:
              trigger_lower = trigger.lower()
              # Multi-word triggers: substring match. Single-word: exact word match.
              if " " in trigger_lower:
                  if trigger_lower in q_lower:
                      score += 10
                      reasons.append(f"trigger:{trigger}")
              elif trigger_lower in q_words:
                  score += 10
                  reasons.append(f"trigger:{trigger}")
      
          # 3. Capability category match (+5 each)
          query_caps = query_to_capabilities(query)
          for cap in capabilities:
              if cap in query_caps:
                  score += 5
                  reasons.append(f"capability:{cap}")
      
          # 4. Description word overlap (+1 each, max 10)
          query_words = normalize(query)
          desc_words = normalize(description)
          overlap = query_words & desc_words
          overlap_score = min(len(overlap), 10)
          if overlap_score > 0:
              score += overlap_score
              reasons.append(f"word_overlap:{overlap_score}")
      
          # 5. Project assignment boost (+20)
          if project_skills and name in project_skills:
              score += 20
              reasons.append("project_boost")
      
          return {
              "name": name,
              "score": score,
              "reasons": reasons,
              "location": skill.get("location", ""),
              "skill_md": skill.get("skill_md", ""),
              "capabilities": capabilities,
              "status": skill.get("status", "unknown"),
          }
      
      
      def match(query: str, project: str = None, top_n: int = 5, threshold: int = 5) -> list[dict]:
          """
          Match a query against all registered skills.
      
          Returns top N skills with score >= threshold, sorted by score descending.
          """
          skills = load_registry()
          if not skills:
              return []
      
          project_skills = get_project_skills(project) if project else set()
      
          results = []
          for skill in skills:
              result = score_skill(skill, query, project_skills)
              if result["score"] >= threshold:
                  results.append(result)
      
          results.sort(key=lambda x: x["score"], reverse=True)
          return results[:top_n]
      
      
      # ── CLI Entry Point ────────────────────────────────────────────────────────
      
      def main():
          args = sys.argv[1:]
          project = None
          query_parts = []
      
          i = 0
          while i < len(args):
              if args[i] == "--project" and i + 1 < len(args):
                  project = args[i + 1]
                  i += 2
              else:
                  query_parts.append(args[i])
                  i += 1
      
          query = " ".join(query_parts)
      
          if not query:
              print(json.dumps({
                  "error": "No query provided",
                  "usage": 'python match_skills.py "your query here"'
              }, indent=2))
              sys.exit(1)
      
          results = match(query, project=project)
      
          output = {
              "query": query,
              "project": project,
              "matched": len(results),
              "skills": results,
          }
      
          if len(results) == 0:
              output["recommendation"] = "No skills matched. Operate without skills or suggest creating a new one."
          elif len(results) == 1:
              output["recommendation"] = f"Single skill match: use '{results[0]['name']}' directly."
              output["action"] = "load_skill"
          else:
              output["recommendation"] = f"Multiple skills matched ({len(results)}). Use orchestration."
              output["action"] = "orchestrate"
      
          print(json.dumps(output, indent=2, ensure_ascii=False))
      
      
      if __name__ == "__main__":
          main()
      
    • orchestrate.py 10.8 KB
      #!/usr/bin/env python3
      """
      Multi-Skill Orchestration Engine for Agent Orchestrator.
      
      Given matched skills and a query, determines the orchestration pattern
      and generates an execution plan for Claude to follow.
      
      Patterns:
      - single:          One skill handles the entire request
      - sequential:      Skills form a pipeline (A output -> B input)
      - parallel:        Skills work independently on different aspects
      - primary_support: One skill leads, others provide supporting data
      
      Usage:
          python orchestrate.py --skills web-scraper,whatsapp-cloud-api --query "monitorar precos e enviar alerta"
          python orchestrate.py --match-result '{"skills": [...]}' --query "query"
      """
      
      import json
      import sys
      from pathlib import Path
      
      # ── Configuration ──────────────────────────────────────────────────────────
      
      # Resolve paths relative to this script's location
      _SCRIPT_DIR = Path(__file__).resolve().parent
      ORCHESTRATOR_DIR = _SCRIPT_DIR.parent
      SKILLS_ROOT = ORCHESTRATOR_DIR.parent
      DATA_DIR = ORCHESTRATOR_DIR / "data"
      REGISTRY_PATH = DATA_DIR / "registry.json"
      
      # Define which capabilities are typically "producers" vs "consumers"
      # Producers generate data; consumers act on data
      PRODUCER_CAPABILITIES = {"data-extraction", "government-data", "analytics"}
      CONSUMER_CAPABILITIES = {"messaging", "social-media", "content-management"}
      HYBRID_CAPABILITIES = {"api-integration", "web-automation"}
      
      
      # ── Functions ──────────────────────────────────────────────────────────────
      
      def load_registry() -> dict[str, dict]:
          """Load registry as name->skill dict."""
          if not REGISTRY_PATH.exists():
              return {}
          try:
              data = json.loads(REGISTRY_PATH.read_text(encoding="utf-8"))
              return {s["name"]: s for s in data.get("skills", [])}
          except Exception:
              return {}
      
      
      def get_skill_role(skill: dict) -> str:
          """Determine if a skill is primarily a producer, consumer, or hybrid.
      
          Uses weighted scoring: more specific capabilities (data-extraction,
          messaging) outweigh generic ones (api-integration, content-management).
          """
          caps = set(skill.get("capabilities", []))
      
          producer_count = len(caps & PRODUCER_CAPABILITIES)
          consumer_count = len(caps & CONSUMER_CAPABILITIES)
      
          # If skill has both producer and consumer caps, use the dominant one
          if producer_count > consumer_count:
              return "producer"
          elif consumer_count > producer_count:
              return "consumer"
          elif producer_count > 0 and consumer_count > 0:
              # Equal weight - check if core name suggests a role
              name = skill.get("name", "").lower()
              if any(kw in name for kw in ["scraper", "extract", "collect", "data", "junta"]):
                  return "producer"
              if any(kw in name for kw in ["whatsapp", "instagram", "messenger", "notify"]):
                  return "consumer"
              return "hybrid"
          else:
              return "hybrid"
      
      
      def classify_pattern(skills: list[dict], query: str) -> str:
          """
          Determine the orchestration pattern based on skill roles and query.
      
          Rules:
          1. Single skill -> "single"
          2. Producer(s) + Consumer(s) -> "sequential" (data flows producer->consumer)
          3. All same role -> "parallel" (independent work)
          4. One high-score + others lower -> "primary_support"
          """
          if len(skills) <= 1:
              return "single"
      
          roles = [get_skill_role(s) for s in skills]
          has_producer = "producer" in roles
          has_consumer = "consumer" in roles
      
          # Producer -> Consumer pipeline
          if has_producer and has_consumer:
              return "sequential"
      
          # Check if one skill dominates by score
          scores = [s.get("score", 0) for s in skills]
          if len(scores) >= 2:
              scores_sorted = sorted(scores, reverse=True)
              if scores_sorted[0] >= scores_sorted[1] * 2:
                  return "primary_support"
      
          # All same role or no clear pipeline
          return "parallel"
      
      
      def generate_plan(skills: list[dict], query: str, pattern: str) -> dict:
          """Generate an execution plan based on the pattern."""
      
          if pattern == "single":
              skill = skills[0]
              return {
                  "pattern": "single",
                  "description": f"Use '{skill['name']}' to handle the entire request.",
                  "steps": [
                      {
                          "order": 1,
                          "skill": skill["name"],
                          "skill_md": skill.get("skill_md", skill.get("location", "")),
                          "action": f"Load SKILL.md and follow its workflow for: {query}",
                          "input": "user_query",
                          "output": "result",
                      }
                  ],
                  "data_flow": "user_query -> result",
              }
      
          elif pattern == "sequential":
              # Order: producers first, then consumers
              producers = [s for s in skills if get_skill_role(s) in ("producer", "hybrid")]
              consumers = [s for s in skills if get_skill_role(s) == "consumer"]
      
              # If no clear producers, use score order
              if not producers:
                  producers = [skills[0]]
                  consumers = skills[1:]
      
              ordered = producers + consumers
              steps = []
              for i, skill in enumerate(ordered):
                  role = get_skill_role(skill)
                  if i == 0:
                      input_src = "user_query"
                      action = f"Extract/collect data: {query}"
                  else:
                      prev = ordered[i - 1]["name"]
                      input_src = f"{prev}.output"
                      if role == "consumer":
                          action = f"Process/deliver data from {prev}"
                      else:
                          action = f"Continue processing with data from {prev}"
      
                  steps.append({
                      "order": i + 1,
                      "skill": skill["name"],
                      "skill_md": skill.get("skill_md", skill.get("location", "")),
                      "action": action,
                      "input": input_src,
                      "output": f"{skill['name']}.output",
                      "role": role,
                  })
      
              flow_parts = [s["skill"] for s in steps]
              data_flow = " -> ".join(["user_query"] + flow_parts + ["result"])
      
              return {
                  "pattern": "sequential",
                  "description": f"Pipeline: {' -> '.join(flow_parts)}",
                  "steps": steps,
                  "data_flow": data_flow,
              }
      
          elif pattern == "parallel":
              steps = []
              for i, skill in enumerate(skills):
                  steps.append({
                      "order": 1,  # All run at the same "order" level
                      "skill": skill["name"],
                      "skill_md": skill.get("skill_md", skill.get("location", "")),
                      "action": f"Handle independently: aspect of '{query}' related to {', '.join(skill.get('capabilities', []))}",
                      "input": "user_query",
                      "output": f"{skill['name']}.output",
                  })
      
              return {
                  "pattern": "parallel",
                  "description": f"Execute {len(skills)} skills in parallel, each handling their domain.",
                  "steps": steps,
                  "data_flow": "user_query -> [parallel] -> aggregated_result",
                  "aggregation": "Combine results from all skills into a unified response.",
              }
      
          elif pattern == "primary_support":
              primary = skills[0]  # Highest score
              support = skills[1:]
      
              steps = [
                  {
                      "order": 1,
                      "skill": primary["name"],
                      "skill_md": primary.get("skill_md", primary.get("location", "")),
                      "action": f"Primary: handle main request: {query}",
                      "input": "user_query",
                      "output": f"{primary['name']}.output",
                      "role": "primary",
                  }
              ]
      
              for i, skill in enumerate(support):
                  steps.append({
                      "order": 2,
                      "skill": skill["name"],
                      "skill_md": skill.get("skill_md", skill.get("location", "")),
                      "action": f"Support: provide {', '.join(skill.get('capabilities', []))} data if needed",
                      "input": "user_query",
                      "output": f"{skill['name']}.output",
                      "role": "support",
                  })
      
              return {
                  "pattern": "primary_support",
                  "description": f"Primary: '{primary['name']}'. Support: {', '.join(s['name'] for s in support)}.",
                  "steps": steps,
                  "data_flow": f"user_query -> {primary['name']} (primary) + support skills as needed -> result",
              }
      
          return {"pattern": "unknown", "steps": [], "data_flow": ""}
      
      
      # ── CLI Entry Point ────────────────────────────────────────────────────────
      
      def main():
          args = sys.argv[1:]
          skill_names = []
          query = ""
          match_result = None
      
          i = 0
          while i < len(args):
              if args[i] == "--skills" and i + 1 < len(args):
                  skill_names = [s.strip() for s in args[i + 1].split(",")]
                  i += 2
              elif args[i] == "--query" and i + 1 < len(args):
                  query = args[i + 1]
                  i += 2
              elif args[i] == "--match-result" and i + 1 < len(args):
                  match_result = json.loads(args[i + 1])
                  i += 2
              else:
                  # Treat as query if no flag
                  query = args[i]
                  i += 1
      
          # Get skill data from match result or registry
          skills = []
          if match_result:
              skills = match_result.get("skills", [])
          elif skill_names:
              registry = load_registry()
              for name in skill_names:
                  if name in registry:
                      skill_data = registry[name]
                      skill_data["score"] = 10  # default score
                      skills.append(skill_data)
      
          if not skills:
              print(json.dumps({
                  "error": "No skills provided",
                  "usage": 'python orchestrate.py --skills skill1,skill2 --query "your query"'
              }, indent=2))
              sys.exit(1)
      
          if not query:
              print(json.dumps({
                  "error": "No query provided",
                  "usage": 'python orchestrate.py --skills skill1,skill2 --query "your query"'
              }, indent=2))
              sys.exit(1)
      
          # Classify and generate plan
          pattern = classify_pattern(skills, query)
          plan = generate_plan(skills, query, pattern)
          plan["query"] = query
          plan["skill_count"] = len(skills)
      
          # Add instructions for Claude
          plan["instructions"] = []
          for step in plan.get("steps", []):
              skill_md = step.get("skill_md", "")
              if skill_md:
                  plan["instructions"].append(
                      f"Step {step['order']}: Read {skill_md} and follow its workflow for: {step['action']}"
                  )
      
          print(json.dumps(plan, indent=2, ensure_ascii=False))
      
      
      if __name__ == "__main__":
          main()
      
    • requirements.txt 12 B
      pyyaml>=6.0
      
    • scan_registry.py 17.7 KB
      #!/usr/bin/env python3
      """
      Auto-Discovery Engine for Agent Orchestrator.
      
      Scans the skills ecosystem for SKILL.md files, parses metadata,
      and maintains a centralized registry (registry.json).
      
      Features:
      - Runs automatically on every request (called by CLAUDE.md)
      - Ultra-fast via MD5 hash caching (~<100ms when nothing changed)
      - Auto-includes new skills, auto-removes deleted skills
      - Zero manual intervention required
      
      Usage:
          python scan_registry.py              # Quick scan (hash-based)
          python scan_registry.py --status     # Verbose status table
          python scan_registry.py --force      # Full re-scan ignoring hashes
      """
      
      import os
      import sys
      import json
      import hashlib
      import re
      from pathlib import Path
      from datetime import datetime
      
      # ── Configuration ──────────────────────────────────────────────────────────
      
      # Resolve paths relative to this script's location
      _SCRIPT_DIR = Path(__file__).resolve().parent
      ORCHESTRATOR_DIR = _SCRIPT_DIR.parent
      SKILLS_ROOT = ORCHESTRATOR_DIR.parent
      DATA_DIR = ORCHESTRATOR_DIR / "data"
      REGISTRY_PATH = DATA_DIR / "registry.json"
      HASHES_PATH = DATA_DIR / "registry_hashes.json"
      
      # Where to search for SKILL.md files
      SEARCH_PATHS = [
          SKILLS_ROOT / ".claude" / "skills",   # registered skills
          SKILLS_ROOT,                           # top-level standalone
      ]
      MAX_DEPTH = 3  # max directory depth for SKILL.md search
      
      # Capability keyword mapping (PT + EN)
      CAPABILITY_MAP = {
          "data-extraction": [
              "scrape", "extract", "crawl", "parse", "harvest", "collect",
              "raspar", "extrair", "coletar", "dados",
          ],
          "messaging": [
              "whatsapp", "message", "send", "chat", "notification", "sms",
              "mensagem", "enviar", "notificacao", "atendimento",
          ],
          "social-media": [
              "instagram", "facebook", "twitter", "post", "stories", "reels",
              "social", "engagement", "feed", "follower",
          ],
          "government-data": [
              "junta", "leiloeiro", "cadastro", "governo", "comercial",
              "tribunal", "diario oficial", "certidao", "registro",
          ],
          "web-automation": [
              "browser", "selenium", "playwright", "automate", "click",
              "navegador", "automatizar", "automacao",
          ],
          "api-integration": [
              "api", "endpoint", "webhook", "rest", "graph", "oauth",
              "integracao", "integrar",
          ],
          "analytics": [
              "insight", "analytics", "metrics", "dashboard", "report",
              "relatorio", "metricas", "analise",
          ],
          "content-management": [
              "publish", "schedule", "template", "content", "media",
              "publicar", "agendar", "conteudo", "midia",
          ],
          "legal": [
              "advogado", "direito", "juridico", "lei", "processo",
              "acao", "peticao", "recurso", "sentenca", "juiz",
              "divorcio", "guarda", "alimentos", "pensao", "alimenticia", "inventario", "heranca", "partilha",
              "acidente de trabalho", "acidente",
              "familia", "criminal", "penal", "crime", "feminicidio", "maria da penha",
              "violencia domestica", "medida protetiva", "stalking",
              "danos morais", "responsabilidade civil", "indenizacao", "dano",
              "consumidor", "cdc", "plano de saude",
              "trabalhista", "clt", "rescisao", "fgts", "horas extras",
              "previdenciario", "aposentadoria", "aposentar", "inss",
              "imobiliario", "usucapiao", "despejo", "inquilinato",
              "alienacao fiduciaria", "bem de familia",
              "tributario", "imposto", "icms", "execucao fiscal",
              "administrativo", "licitacao", "improbidade", "mandado de seguranca",
              "empresarial", "societario", "falencia", "recuperacao judicial",
              "empresa", "ltda", "cnpj", "mei", "eireli", "contrato social",
              "contrato", "clausula", "contestacao", "apelacao", "agravo",
              "habeas corpus", "mandado", "liminar", "tutela",
              "cpc", "stj", "stf", "sumula", "jurisprudencia",
              "oab", "honorarios", "custas",
          ],
          "auction": [
              "leilao", "leilao judicial", "leilao extrajudicial", "hasta publica",
              "arrematacao", "arrematar", "arrematante", "lance", "desagio",
              "edital leilao", "penhora", "adjudicacao", "praca",
              "imissao na posse", "carta arrematacao", "vil preco",
              "avaliacao imovel", "laudo", "perito", "matricula",
              "leiloeiro", "comissao leiloeiro",
          ],
          "security": [
              "seguranca", "security", "owasp", "vulnerability", "incident",
              "pentest", "firewall", "malware", "phishing", "cve",
              "autenticacao", "criptografia", "encryption",
          ],
          "image-generation": [
              "imagem", "image", "gerar imagem", "generate image",
              "stable diffusion", "comfyui", "midjourney", "dall-e",
              "foto", "ilustracao", "arte", "design",
          ],
          "monitoring": [
              "monitor", "monitorar", "health", "status",
              "audit", "auditoria", "sentinel", "check",
          ],
          "context-management": [
              "contexto", "context", "sessao", "session", "compactacao", "compaction",
              "comprimir", "compress", "snapshot", "checkpoint", "briefing",
              "continuidade", "continuity", "preservar", "preserve",
              "memoria", "memory", "resumo", "summary",
              "salvar estado", "save state", "context window", "janela de contexto",
              "perda de dados", "data loss", "backup",
          ],
      }
      
      # ── Utility Functions ──────────────────────────────────────────────────────
      
      def sha256_file(path: Path) -> str:
          """Compute SHA-256 hash of a file."""
          h = hashlib.sha256()
          with open(path, "rb") as f:
              for chunk in iter(lambda: f.read(8192), b""):
                  h.update(chunk)
          return h.hexdigest()
      
      
      def parse_yaml_frontmatter(path: Path) -> dict:
          """Extract YAML frontmatter from a SKILL.md file."""
          try:
              text = path.read_text(encoding="utf-8")
          except Exception:
              return {}
      
          match = re.match(r"^---\s*\n(.*?)\n---", text, re.DOTALL)
          if not match:
              return {}
      
          try:
              import yaml
              return yaml.safe_load(match.group(1)) or {}
          except Exception:
              # Fallback: manual parsing for name/description
              result = {}
              block = match.group(1)
              for key in ("name", "description", "version"):
                  m = re.search(rf'^{key}:\s*["\']?(.+?)["\']?\s*$', block, re.MULTILINE)
                  if m:
                      result[key] = m.group(1).strip()
                  else:
                      # Handle multi-line description with >- or >
                      m2 = re.search(rf'^{key}:\s*>-?\s*\n((?:\s+.+\n?)+)', block, re.MULTILINE)
                      if m2:
                          lines = m2.group(1).strip().split("\n")
                          result[key] = " ".join(line.strip() for line in lines)
              return result
      
      
      def find_skill_files() -> list[Path]:
          """Find all SKILL.md files in the ecosystem."""
          found = set()
      
          for base in SEARCH_PATHS:
              if not base.exists():
                  continue
              for root, dirs, files in os.walk(base):
                  depth = len(Path(root).relative_to(base).parts)
                  if depth > MAX_DEPTH:
                      dirs.clear()
                      continue
      
                  # Skip the orchestrator itself
                  if "agent-orchestrator" in Path(root).parts:
                      continue
      
                  if "SKILL.md" in files:
                      found.add(Path(root) / "SKILL.md")
      
          return sorted(found)
      
      
      def detect_language(skill_dir: Path) -> str:
          """Detect primary language from scripts/ directory."""
          scripts_dir = skill_dir / "scripts"
          if not scripts_dir.exists():
              return "none"
      
          extensions = set()
          for f in scripts_dir.rglob("*"):
              if f.is_file():
                  extensions.add(f.suffix.lower())
      
          if ".py" in extensions:
              return "python"
          if ".ts" in extensions or ".js" in extensions:
              return "nodejs"
          if ".sh" in extensions:
              return "bash"
          return "none"
      
      
      def extract_capabilities(description: str) -> list[str]:
          """Map description keywords to capability tags using word boundary matching."""
          if not description:
              return []
      
          desc_lower = description.lower()
          desc_words = set(re.findall(r'[a-zA-ZÀ-ÿ]+', desc_lower))
          caps = []
          for cap, keywords in CAPABILITY_MAP.items():
              for kw in keywords:
                  # Multi-word keywords: substring match. Single-word: exact word match.
                  if " " in kw:
                      if kw in desc_lower:
                          caps.append(cap)
                          break
                  elif kw in desc_words:
                      caps.append(cap)
                      break
          return sorted(caps)
      
      
      def extract_triggers(description: str) -> list[str]:
          """Extract trigger keywords from description text using word boundary matching."""
          if not description:
              return []
      
          # Collect all keywords from all capability categories
          all_keywords = set()
          for keywords in CAPABILITY_MAP.values():
              all_keywords.update(keywords)
      
          desc_lower = description.lower()
          desc_words = set(re.findall(r'[a-zA-ZÀ-ÿ]+', desc_lower))
          found = []
          for kw in sorted(all_keywords):
              if " " in kw:
                  if kw in desc_lower:
                      found.append(kw)
              elif kw in desc_words:
                  found.append(kw)
          return found
      
      
      def assess_status(skill_dir: Path) -> str:
          """Check if skill is complete (active) or incomplete."""
          skill_md = skill_dir / "SKILL.md"
          if not skill_md.exists():
              return "missing"
      
          has_scripts = (skill_dir / "scripts").exists()
          has_refs = (skill_dir / "references").exists()
      
          # Parse frontmatter to check for required fields
          meta = parse_yaml_frontmatter(skill_md)
          has_name = bool(meta.get("name"))
          has_desc = bool(meta.get("description"))
      
          if has_name and has_desc:
              return "active"
          return "incomplete"
      
      
      def is_registered(skill_dir: Path) -> bool:
          """Check if skill is in .claude/skills/."""
          claude_skills = SKILLS_ROOT / ".claude" / "skills"
          try:
              skill_dir.relative_to(claude_skills)
              return True
          except ValueError:
              return False
      
      
      # ── Main Logic ─────────────────────────────────────────────────────────────
      
      def load_hashes() -> dict:
          """Load stored hashes from registry_hashes.json."""
          if HASHES_PATH.exists():
              try:
                  return json.loads(HASHES_PATH.read_text(encoding="utf-8"))
              except Exception:
                  pass
          return {}
      
      
      def save_hashes(hashes: dict):
          """Save hashes to registry_hashes.json."""
          DATA_DIR.mkdir(parents=True, exist_ok=True)
          HASHES_PATH.write_text(json.dumps(hashes, indent=2), encoding="utf-8")
      
      
      def load_registry() -> dict:
          """Load existing registry.json."""
          if REGISTRY_PATH.exists():
              try:
                  return json.loads(REGISTRY_PATH.read_text(encoding="utf-8"))
              except Exception:
                  pass
          return {"generated_at": None, "skills_root": str(SKILLS_ROOT), "skills": []}
      
      
      def save_registry(registry: dict):
          """Save registry.json."""
          DATA_DIR.mkdir(parents=True, exist_ok=True)
          registry["generated_at"] = datetime.now().isoformat()
          REGISTRY_PATH.write_text(json.dumps(registry, indent=2, ensure_ascii=False), encoding="utf-8")
      
      
      def build_skill_entry(skill_md_path: Path) -> dict:
          """Build a registry entry from a SKILL.md file."""
          skill_dir = skill_md_path.parent
          meta = parse_yaml_frontmatter(skill_md_path)
          description = meta.get("description", "")
      
          # Support explicit capabilities in frontmatter
          explicit_caps = meta.get("capabilities", [])
          if isinstance(explicit_caps, str):
              explicit_caps = [c.strip() for c in explicit_caps.split(",")]
      
          auto_caps = extract_capabilities(description)
          all_caps = sorted(set(auto_caps + explicit_caps))
      
          return {
              "name": meta.get("name", skill_dir.name),
              "description": description,
              "version": meta.get("version", ""),
              "location": str(skill_dir),
              "skill_md": str(skill_md_path),
              "registered": is_registered(skill_dir),
              "has_scripts": (skill_dir / "scripts").exists(),
              "has_references": (skill_dir / "references").exists(),
              "has_data": (skill_dir / "data").exists(),
              "capabilities": all_caps,
              "triggers": extract_triggers(description),
              "language": detect_language(skill_dir),
              "status": assess_status(skill_dir),
              "last_modified": datetime.fromtimestamp(
                  skill_md_path.stat().st_mtime
              ).isoformat(),
          }
      
      
      def scan(force: bool = False) -> dict:
          """
          Main scan function.
      
          With hash caching:
          1. Find all SKILL.md files
          2. Compare MD5 hashes with stored values
          3. Only re-parse files that changed, were added, or removed
          4. Update registry incrementally
          """
          current_files = find_skill_files()
          current_paths = {str(f): f for f in current_files}
      
          stored_hashes = load_hashes()
          registry = load_registry()
      
          # Build lookup of existing registry entries by skill_md path
          existing_by_path = {}
          for entry in registry.get("skills", []):
              existing_by_path[entry.get("skill_md", "")] = entry
      
          # Compute current hashes
          new_hashes = {}
          changed = False
      
          for path_str, path_obj in current_paths.items():
              current_hash = sha256_file(path_obj)
              new_hashes[path_str] = current_hash
      
              if force or path_str not in stored_hashes or stored_hashes[path_str] != current_hash:
                  # New or modified - rebuild entry
                  entry = build_skill_entry(path_obj)
                  existing_by_path[path_str] = entry
                  changed = True
      
          # Detect removed skills
          for old_path in list(existing_by_path.keys()):
              if old_path not in current_paths and old_path != "":
                  del existing_by_path[old_path]
                  changed = True
      
          # Check if file set changed (additions/removals)
          if set(new_hashes.keys()) != set(stored_hashes.keys()):
              changed = True
      
          # Deduplicate by skill name (case-insensitive).
          # When the same skill exists in both skills/ and .claude/skills/,
          # prefer the primary location (skills/) over the registered copy.
          if changed or not REGISTRY_PATH.exists():
              by_name = {}
              for entry in existing_by_path.values():
                  name = entry.get("name", "").lower()
                  if not name:
                      continue
                  if name not in by_name:
                      by_name[name] = entry
                  else:
                      # Prefer the version NOT in .claude/skills/ (the primary source)
                      existing = by_name[name]
                      existing_is_registered = existing.get("registered", False)
                      new_is_registered = entry.get("registered", False)
                      if existing_is_registered and not new_is_registered:
                          by_name[name] = entry
                      # If both are primary or both registered, keep first found
      
              registry["skills"] = sorted(by_name.values(), key=lambda s: s.get("name", ""))
              save_registry(registry)
              save_hashes(new_hashes)
              return registry
          else:
              # Nothing changed, return existing
              return registry
      
      
      def print_status(registry: dict):
          """Print a formatted status table."""
          skills = registry.get("skills", [])
      
          if not skills:
              print("No skills found in the ecosystem.")
              return
      
          print(f"\n{'='*80}")
          print(f"  Agent Orchestrator - Skill Registry Status")
          print(f"  Scanned at: {registry.get('generated_at', 'N/A')}")
          print(f"  Root: {registry.get('skills_root', 'N/A')}")
          print(f"{'='*80}\n")
      
          # Header
          print(f"  {'Name':<22} {'Status':<12} {'Lang':<10} {'Registered':<12} {'Capabilities'}")
          print(f"  {'-'*22} {'-'*12} {'-'*10} {'-'*12} {'-'*30}")
      
          for s in sorted(skills, key=lambda x: x.get("name", "")):
              name = s.get("name", "?")[:20]
              status = s.get("status", "?")
              lang = s.get("language", "none")
              reg = "Yes" if s.get("registered") else "No"
              caps = ", ".join(s.get("capabilities", []))[:30]
              print(f"  {name:<22} {status:<12} {lang:<10} {reg:<12} {caps}")
      
          print(f"\n  Total: {len(skills)} skills")
      
          # Recommendations
          unregistered = [s for s in skills if not s.get("registered")]
          incomplete = [s for s in skills if s.get("status") == "incomplete"]
      
          if unregistered:
              print(f"\n  [!] {len(unregistered)} skill(s) not registered in .claude/skills/:")
              for s in unregistered:
                  print(f"      - {s['name']} ({s['location']})")
      
          if incomplete:
              print(f"\n  [!] {len(incomplete)} skill(s) with incomplete status:")
              for s in incomplete:
                  print(f"      - {s['name']} ({s['location']})")
      
          print()
      
      
      # ── CLI Entry Point ────────────────────────────────────────────────────────
      
      def main():
          force = "--force" in sys.argv
          show_status = "--status" in sys.argv
      
          registry = scan(force=force)
      
          if show_status:
              print_status(registry)
          else:
              # Default: output JSON summary for Claude to parse
              skills = registry.get("skills", [])
              summary = {
                  "total": len(skills),
                  "active": len([s for s in skills if s.get("status") == "active"]),
                  "incomplete": len([s for s in skills if s.get("status") == "incomplete"]),
                  "skills": [
                      {
                          "name": s.get("name"),
                          "status": s.get("status"),
                          "capabilities": s.get("capabilities", []),
                      }
                      for s in skills
                  ],
              }
              print(json.dumps(summary, indent=2, ensure_ascii=False))
      
      
      if __name__ == "__main__":
          main()
      
  • SKILL.md 9.6 KB
    ---
    name: agent-orchestrator
    description: Meta-skill que orquestra todos os agentes do ecossistema. Scan automatico de skills, match por capacidades, coordenacao de workflows multi-skill e registry management.
    risk: safe
    source: community
    date_added: '2026-03-06'
    author: renat
    tags:
    - orchestration
    - multi-agent
    - workflow
    - automation
    tools:
    - claude-code
    - antigravity
    - cursor
    - gemini-cli
    - codex-cli
    ---
    
    # Agent Orchestrator
    
    ## Overview
    
    Meta-skill que orquestra todos os agentes do ecossistema. Scan automatico de skills, match por capacidades, coordenacao de workflows multi-skill e registry management.
    
    ## When to Use This Skill
    
    - When you need specialized assistance with this domain
    
    ## Do Not Use This Skill When
    
    - The task is unrelated to agent orchestrator
    - A simpler, more specific tool can handle the request
    - The user needs general-purpose assistance without domain expertise
    
    ## How It Works
    
    Meta-skill que funciona como camada central de decisao e coordenacao para todo
    o ecossistema de skills. Faz varredura automatica, identifica agentes relevantes
    e orquestra multiplos skills para tarefas complexas.
    
    ## Principio: Zero Intervencao Manual
    
    - **SEMPRE faz varredura** antes de processar qualquer solicitacao
    - Novas skills sao **auto-detectadas e incluidas** ao criar SKILL.md em qualquer subpasta
    - Skills removidas sao **auto-excluidas** do registry
    - Nenhum comando manual e necessario para registrar novas skills
    
    ---
    
    ## Workflow Obrigatorio (Toda Solicitacao)
    
    Execute estes passos ANTES de processar qualquer request do usuario.
    Os scripts usam paths relativos automaticamente - funciona de qualquer diretorio.
    
    ## Passo 1: Auto-Discovery (Varredura)
    
    ```bash
    python agent-orchestrator/scripts/scan_registry.py
    ```
    
    Ultra-rapido (<100ms) via cache de hashes MD5. So re-processa arquivos alterados.
    Retorna JSON com resumo de todos os skills encontrados.
    
    ## Passo 2: Match De Skills
    
    ```bash
    python agent-orchestrator/scripts/match_skills.py "<solicitacao do usuario>"
    ```
    
    Retorna JSON com skills ranqueadas por relevancia. Interpretar o resultado:
    
    | Resultado              | Acao                                                    |
    |:-----------------------|:--------------------------------------------------------|
    | `matched: 0`          | Nenhum skill relevante. Operar normalmente sem skills.  |
    | `matched: 1`          | Um skill relevante. Carregar seu SKILL.md e seguir.     |
    | `matched: 2+`         | Multiplos skills. Executar Passo 3 (orquestracao).      |
    
    ## Passo 3: Orquestracao (Se Matched >= 2)
    
    ```bash
    python agent-orchestrator/scripts/orchestrate.py --skills skill1,skill2 --query "<solicitacao>"
    ```
    
    Retorna plano de execucao com padrao, ordem dos steps e data flow entre skills.
    
    ## Passo Rapido (Atalho)
    
    Para queries simples, os passos 1+2 podem ser combinados em sequencia:
    ```bash
    python agent-orchestrator/scripts/scan_registry.py && python agent-orchestrator/scripts/match_skills.py "<solicitacao>"
    ```
    
    ---
    
    ## Skill Registry
    
    O registry vive em:
    ```
    agent-orchestrator/data/registry.json
    ```
    
    ## Locais De Busca
    
    O scanner procura SKILL.md em:
    1. `.claude/skills/*/` (skills registradas no Claude Code)
    2. `*/` (skills standalone no top-level)
    3. `*/*\` (skills em subpastas, ate profundidade 3)
    
    ## Metadata Por Skill
    
    Cada entrada no registry contem:
    
    | Campo          | Descricao                                          |
    |:---------------|:---------------------------------------------------|
    | name           | Nome da skill (do frontmatter YAML)                |
    | description    | Descricao completa (triggers inclusos)             |
    | location       | Caminho absoluto do diretorio                      |
    | skill_md       | Caminho absoluto do SKILL.md                       |
    | registered     | Se esta em .claude/skills/ (true/false)            |
    | capabilities   | Tags de capacidade (auto-extraidas + explicitas)   |
    | triggers       | Keywords de ativacao extraidas da description      |
    | language       | Linguagem principal (python/nodejs/bash/none)      |
    | status         | active / incomplete / missing                      |
    
    ## Comandos Do Registry
    
    ```bash
    
    ## Scan Rapido (Usa Cache De Hashes)
    
    python agent-orchestrator/scripts/scan_registry.py
    
    ## Tabela De Status Detalhada
    
    python agent-orchestrator/scripts/scan_registry.py --status
    
    ## Re-Scan Completo (Ignora Cache)
    
    python agent-orchestrator/scripts/scan_registry.py --force
    ```
    
    ---
    
    ## Algoritmo De Matching
    
    Para cada solicitacao, o matcher pontua skills usando:
    
    | Criterio                     | Pontos | Exemplo                               |
    |:-----------------------------|:-------|:--------------------------------------|
    | Nome do skill na query       | +15    | "use web-scraper" -> web-scraper      |
    | Keyword trigger exata        | +10    | "scrape" -> web-scraper               |
    | Categoria de capacidade      | +5     | data-extraction -> web-scraper        |
    | Sobreposicao de palavras     | +1     | Palavras da query na description      |
    | Boost de projeto             | +20    | Skill atribuida ao projeto ativo      |
    
    Threshold minimo: 5 pontos. Skills abaixo disso sao ignoradas.
    
    ## Match Com Projeto
    
    ```bash
    python agent-orchestrator/scripts/match_skills.py --project meu-projeto "query aqui"
    ```
    
    Skills atribuidas ao projeto recebem +20 de boost automatico.
    
    ---
    
    ## Padroes De Orquestracao
    
    Quando multiplos skills sao relevantes, o orchestrator classifica o padrao:
    
    ## 1. Pipeline Sequencial
    
    Skills formam uma cadeia onde o output de uma alimenta a proxima.
    
    **Quando:** Mix de skills "produtoras" (data-extraction, government-data) e "consumidoras" (messaging, social-media).
    
    **Exemplo:** web-scraper coleta precos -> whatsapp-cloud-api envia alerta
    
    ```
    user_query -> web-scraper -> whatsapp-cloud-api -> result
    ```
    
    ## 2. Execucao Paralela
    
    Skills trabalham independentemente em aspectos diferentes da solicitacao.
    
    **Quando:** Todas as skills tem o mesmo papel (todas produtoras ou todas consumidoras).
    
    **Exemplo:** instagram publica post + whatsapp envia notificacao (ambos recebem o mesmo conteudo)
    
    ```
    user_query -> [instagram, whatsapp-cloud-api] -> aggregated_result
    ```
    
    ## 3. Primario + Suporte
    
    Uma skill principal lidera; outras fornecem dados de apoio.
    
    **Quando:** Uma skill tem score muito superior as demais (>= 2x).
    
    **Exemplo:** whatsapp-cloud-api envia mensagem (primario) + web-scraper fornece dados (suporte)
    
    ```
    user_query -> whatsapp-cloud-api (primary) + web-scraper (support) -> result
    ```
    
    ## Detalhes Em `References/Orchestration-Patterns.Md`
    
    ---
    
    ## Gerenciamento De Projetos
    
    Atribuir skills a projetos permite boost de relevancia e contexto persistente.
    
    ## Arquivo De Projetos
    
    ```
    agent-orchestrator/data/projects.json
    ```
    
    ## Operacoes
    
    **Criar projeto:**
    Adicionar entrada ao projects.json:
    ```json
    {
      "name": "nome-do-projeto",
      "created_at": "2026-02-25T12:00:00",
      "skills": ["web-scraper", "whatsapp-cloud-api"],
      "description": "Descricao do projeto"
    }
    ```
    
    **Adicionar skill a projeto:** Atualizar o array `skills` do projeto.
    
    **Remover skill de projeto:** Remover do array `skills`.
    
    **Consultar skills do projeto:** Ler o projects.json e listar skills atribuidas.
    
    ---
    
    ## Adicionando Novas Skills
    
    Para adicionar uma nova skill ao ecossistema:
    
    1. Criar uma pasta em qualquer lugar sob `skills root:`
    2. Criar um `SKILL.md` com frontmatter YAML:
    ```yaml
    ---
    name: minha-nova-skill
    description: "Descricao com keywords de ativacao..."
    ---
    
    ## Documentacao Da Skill
    
    ```
    3. **Pronto!** O auto-discovery detecta automaticamente na proxima solicitacao.
    
    Opcionalmente, para discovery nativo do Claude Code:
    4. Copiar o SKILL.md para `.claude/skills/<nome>/SKILL.md`
    
    ## Tags De Capacidade Explicitas (Opcional)
    
    Adicionar ao frontmatter para matching mais preciso:
    ```yaml
    capabilities: [data-extraction, web-automation]
    ```
    
    ---
    
    ## Ver Status De Todos Os Skills
    
    ```bash
    python agent-orchestrator/scripts/scan_registry.py --status
    ```
    
    ## Interpretar Status
    
    | Status     | Significado                                        |
    |:-----------|:---------------------------------------------------|
    | active     | SKILL.md com name + description presentes          |
    | incomplete | SKILL.md existe mas falta name ou description      |
    | missing    | Diretorio existe mas sem SKILL.md                  |
    
    ---
    
    ## Skills Atuais Do Ecossistema
    
    | Skill              | Capacidades                           | Status  |
    |:-------------------|:--------------------------------------|:--------|
    | web-scraper        | data-extraction, web-automation       | active  |
    | junta-leiloeiros   | government-data, data-extraction      | active  |
    | whatsapp-cloud-api | messaging, api-integration            | active  |
    | instagram          | social-media, api-integration         | partial |
    
    *Esta tabela e atualizada automaticamente via `scan_registry.py --status`.*
    
    ## Best Practices
    
    - Provide clear, specific context about your project and requirements
    - Review all suggestions before applying them to production code
    - Combine with other complementary skills for comprehensive analysis
    
    ## Common Pitfalls
    
    - Using this skill for tasks outside its domain expertise
    - Applying recommendations without understanding your specific context
    - Not providing enough project context for accurate analysis
    
    ## Related Skills
    
    - `multi-advisor` - Complementary skill for enhanced analysis
    - `task-intelligence` - Complementary skill for enhanced analysis
    
    ## Limitations
    - Use this skill only when the task clearly matches the scope described above.
    - Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
    - Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related