Files
claude-plugins/gestao/skills/clip-agent/SKILL.md
T

16 KiB

name, description, context
name description context
clip-agent Gerir agente Paperclip individual — estado, config, AGENTS.md, histórico runs, issues. Aceita nome como argumento. Usar quando "clip agent", "agente clip", "ver agente", "estado do CTO". fork

/clip-agent — Gerir Agente Paperclip

Aceita argumento: nome do agente (ex: /clip-agent CTO).

Constantes

BD: PGPASSWORD="paperclip" psql -h localhost -p 54329 -U paperclip -d paperclip
COMPANY_ID: ebe10308-efd7-453f-86ab-13e6fe84004f
AGENTS_PATH: /media/ealmeida/Dados/Hub/04-Stack/02.06-Clip/agents/

Procedimento

Passo 1: Contexto completo do agente

Invocar tool MCP: mcp__paperclip__diag_agent_full_context(agent_name="{{NOME}}")

Esta tool retorna config, últimas runs, membership, permissões e tokens — cobre os Passos 1, 3, 6, 7 e 7b de uma só vez. Se multiplos resultados, mostrar lista e pedir clarificacao.

Passo 2: Hierarquia

Quem lhe reporta:

SELECT name, role, status FROM agents
WHERE reports_to = '{{AGENT_ID}}'
AND company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
ORDER BY role, name;

A quem reporta:

SELECT name, role FROM agents WHERE id = '{{REPORTS_TO_ID}}';

Passo 3: Ultimas 5 runs

SELECT hr.status, hr.started_at, hr.finished_at, LEFT(hr.error, 80) as erro
FROM heartbeat_runs hr
WHERE hr.agent_id = '{{AGENT_ID}}'
ORDER BY hr.started_at DESC LIMIT 5;

Passo 4: Issues atribuidas

SELECT title, status, priority FROM issues
WHERE assignee_agent_id = '{{AGENT_ID}}'
AND status NOT IN ('done','cancelled')
ORDER BY priority, status;

Passo 4b: Verificar falsos blockers (INC-07)

Se o agente tem issues blocked, invocar tool MCP: mcp__paperclip__diag_false_blockers e filtrar pelo agente. TODO: criar diag_* tool per-agent se uso recorrente.

Se sub_activas > 0 e o agente está operacional → falso blocker. Alertar: "Issue {{ID}} marcada como blocked mas tem sub-tasks activas — deveria ser in_progress."

Passo 5: AGENTS.md

Procurar em AGENTS_PATH:

find /media/ealmeida/Dados/Hub/04-Stack/02.06-Clip/agents/ -name "AGENTS.md" -exec grep -l "{{NOME}}" {} \;

Se encontrado, ler e apresentar resumo (primeiras 30 linhas).

Formato de output

## Agente: {{NOME}}

**Role:** {{role}} | **Status:** {{status}} | **Ultimo heartbeat:** {{last_heartbeat_at}}
**Reporta a:** {{reports_to_name}} | **Budget:** {{spent}}/{{budget}} ({{pct}}%)
**Tokens cached:** {{tokens_M}}M | **CWD:** {{cwd}} | **Model:** {{model}}

### Equipa (reportam a este agente)
| Nome | Role | Status |
...

### Ultimas 5 runs
| Status | Início | Fim | Erro |
...

### Issues atribuidas
| Titulo | Status | Prioridade |
...

### Skills atribuidas
[lista de desiredSkills ou "Sem skills (apenas built-in paperclip)"]

### AGENTS.md
[resumo ou path]

Passo 6: Skills do agente

SELECT
  adapter_config->'paperclipSkillSync'->'desiredSkills' as desired_skills
FROM agents
WHERE id = '{{AGENT_ID}}';

Se nao NULL, listar as skills. Se NULL, indicar "Sem skills atribuidas (apenas built-in paperclip)".

Para ver detalhes das skills disponiveis na empresa:

SELECT name, slug, key, source_type FROM company_skills
WHERE company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
ORDER BY name;

Skills atribuídas ao agente

-- Ver skills desejadas (configuradas via API)
SELECT
  a.name as agente,
  a.adapter_config->'paperclipSkillSync'->'desiredSkills' as desired_skills
FROM agents a
WHERE a.id = '{{AGENT_ID}}';

Acções disponíveis:

  • Ver skills da empresa: SELECT name, slug FROM company_skills WHERE company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
  • Atribuir skills via API (fetch no browser): POST /api/agents/{{AGENT_ID}}/skills/sync com { "desiredSkills": ["slug1", "slug2"] }

Membership (OBRIGATÓRIO — sem isto, permissões não funcionam)

A função hasPermission() verifica primeiro se o agente tem membership activa em company_memberships. Sem membership → permissão negada mesmo com grants correctos.

-- Verificar membership
SELECT status, membership_role FROM company_memberships
WHERE company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
AND principal_id = '{{AGENT_ID}}';

Se 0 rows → agente não tem membership. Criar:

INSERT INTO company_memberships (id, company_id, principal_type, principal_id, status, membership_role, created_at, updated_at)
VALUES (gen_random_uuid(), 'ebe10308-efd7-453f-86ab-13e6fe84004f', 'agent', '{{AGENT_ID}}', 'active', 'member', NOW(), NOW());

Nota: Agentes criados via SQL directo NÃO recebem membership automaticamente — apenas os onboarded via fluxo OpenClaw/hiring. Sempre criar membership ao adicionar agentes manualmente.

Permissões do agente

SELECT permission_key FROM principal_permission_grants
WHERE company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
AND principal_id = '{{AGENT_ID}}';

Para adicionar tasks:assign (necessário para delegação):

INSERT INTO principal_permission_grants (company_id, principal_type, principal_id, permission_key, granted_by_user_id)
VALUES ('ebe10308-efd7-453f-86ab-13e6fe84004f', 'agent', '{{AGENT_ID}}', 'tasks:assign', 'local-board');

ATENÇÃO: Este grant só funciona se o agente tiver membership activa (ver secção acima).

Passo 7: Verificar adapter_config (CRITICO)

Agentes sem dangerouslySkipPermissions: true ficam bloqueados — todos os comandos bash sao rejeitados.

SELECT name,
  adapter_config->>'dangerouslySkipPermissions' as skip_perms,
  adapter_config->>'cwd' as cwd,
  adapter_config->>'model' as model,
  adapter_config->>'timeoutSec' as timeout,
  adapter_config->>'maxTurnsPerRun' as max_turns
FROM agents WHERE id = '{{AGENT_ID}}';

Alertar se:

  • skip_perms != true → agente vai ficar bloqueado em todos os comandos bash
  • cwd = NULL → agente corre em directório temporário sem acesso ao Hub
  • cwd = /media/ealmeida/Dados/HubCRITICO: CWD aponta para Hub raiz (6.4GB), Claude Code indexa tudo, causa token burn massivo — usar /media/ealmeida/Dados/Hub/04-Stack/02.06-Clip
  • model = NULL → usa modelo default (pode não ser o desejado)

Corrigir agente (CWD correcto para Clip):

UPDATE agents SET adapter_config = adapter_config || '{"dangerouslySkipPermissions": true, "cwd": "/media/ealmeida/Dados/Hub/04-Stack/02.06-Clip", "model": "gemini-2.5-flash", "graceSec": 20, "timeoutSec": 900}'::jsonb
WHERE id = '{{AGENT_ID}}';

Política de adapters por nível (actualizado 07-04-2026): opencode_local foi completamente removido (modelos indisponíveis). Distribuição actual:

  • C-Level (5) — CEO, CDO, CFO, CGO, CTO: claude_local com claude-sonnet-4-6 (decisão estratégica, exige raciocínio premium)
  • Especialistas/Directores (59): gemini_local com gemini-2.5-flash (execução, custo baixo)
  • Analyst on-demand (1) — Reality Checker: process com claude-sonnet-4-6 (sem heartbeat)

Configurar claude_local (template canónico — C-Level):

{
  "cwd": "/media/ealmeida/Dados/Hub/04-Stack/02.06-Clip",
  "model": "claude-sonnet-4-6",
  "command": "claude",
  "dangerouslySkipPermissions": true,
  "timeoutSec": 900,
  "graceSec": 20,
  "instructionsFilePath": "<path absoluto AGENTS.md>"
}

Modelos válidos: claude-opus-4-6, claude-sonnet-4-6, claude-haiku-4-6, claude-sonnet-4-5-20250929, claude-haiku-4-5-20251001.

Configurar gemini_local (template canónico):

{
  "cwd": "/media/ealmeida/Dados/Hub/04-Stack/02.06-Clip",
  "yolo": true,
  "model": "gemini-2.5-flash",
  "command": "/home/ealmeida/.paperclip/gemini-wrapper.sh",
  "sandbox": false,
  "newSession": true,
  "instructionsFilePath": "<path absoluto AGENTS.md>"
}

Campos:

  • command — wrapper Gemini CLI local em /home/ealmeida/.paperclip/gemini-wrapper.sh
  • modelgemini-2.5-flash (default) ou gemini-2.5-pro (CEO/tarefas pesadas)
  • yolo: true — auto-aprovar comandos (equivalente a dangerouslySkipPermissions do claude_local)
  • sandbox: false — sem sandbox de filesystem
  • newSession: true — sessão nova por run
  • cwd/media/ealmeida/Dados/Hub/04-Stack/02.06-Clip (NUNCA usar Hub raiz)
  • instructionsFilePath — path absoluto para AGENTS.md

Migrar agente para gemini_local (SQL):

UPDATE agents
SET adapter_type = 'gemini_local',
    adapter_config = jsonb_build_object(
      'cwd', COALESCE(adapter_config->>'cwd', '/media/ealmeida/Dados/Hub/04-Stack/02.06-Clip'),
      'yolo', true,
      'model', 'gemini-2.5-flash',
      'command', '/home/ealmeida/.paperclip/gemini-wrapper.sh',
      'sandbox', false,
      'newSession', true,
      'instructionsFilePath', adapter_config->>'instructionsFilePath'
    ),
    updated_at = NOW()
WHERE id = '{{AGENT_ID}}';

Nota sobre API REST vs SQL: O endpoint PATCH /api/agents/:id valida adapterType contra enum desactualizada que não inclui gemini_local (devolve HTTP 400). Workaround actual: SQL directo na BD Paperclip (postgres://paperclip:paperclip@localhost:54329/paperclip). Quando a validação for actualizada, preferir sempre API com header User-Agent: node (Cloudflare bloqueia curl/*).

Agentes analyst/read-only (ex: Reality Checker): usar adapter_type: process com adapter_config.model: "claude-sonnet-4-6" e instructionsBundleMode: "external". Estes agentes são invocados manualmente (heartbeat desactivado por design), não consomem budget em modo autónomo.

Passo 7b: Sessão e tokens (INC-12)

agent_task_sessions não tem campo de tokens — o consumo está em heartbeat_runs.usage_json. Verificar erros "Prompt is too long" e consumo nas últimas runs.

-- Erros "Prompt is too long" recentes
SELECT hr.status, LEFT(hr.error, 80) as erro, hr.started_at::timestamp(0)
FROM heartbeat_runs hr
WHERE hr.agent_id = '{{AGENT_ID}}'
AND hr.status IN ('failed','error')
AND hr.error ILIKE '%too long%'
AND hr.started_at > NOW() - INTERVAL '48 hours'
ORDER BY hr.started_at DESC LIMIT 5;

-- Consumo token nas últimas runs bem-sucedidas
SELECT ROUND(COALESCE((hr.usage_json->>'cache_read_input_tokens')::numeric,0)/1000,0) as cache_read_k,
  ROUND(COALESCE((hr.usage_json->>'input_tokens')::numeric,0)/1000,0) as input_k,
  ROUND(COALESCE((hr.usage_json->>'output_tokens')::numeric,0)/1000,0) as output_k,
  hr.started_at::timestamp(0)
FROM heartbeat_runs hr
WHERE hr.agent_id = '{{AGENT_ID}}'
AND hr.status = 'succeeded'
AND hr.usage_json IS NOT NULL
ORDER BY hr.started_at DESC LIMIT 3;

Se existirem erros "too long" → forçar rotação:

DELETE FROM agent_task_sessions
WHERE agent_id = '{{AGENT_ID}}'
AND company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f';

Passo 7c: Validar instructionsFilePath (INC-07)

O Paperclip não usa cwd para resolver instructionsFilePath — o path é sempre relativo ao processo do servidor, não ao CWD do agente. Deve ser sempre absoluto.

SELECT adapter_config->>'instructionsFilePath' as instructions_path
FROM agents WHERE id = '{{AGENT_ID}}';

Alertar se:

  • instructionsFilePath não começa com /CRITICO: path relativo, AGENTS.md não carrega
  • Path absoluto mas ficheiro não existe → CRITICO

Corrigir path relativo:

UPDATE agents
SET adapter_config = jsonb_set(
    adapter_config,
    '{instructionsFilePath}',
    to_jsonb('/media/ealmeida/Dados/Hub/04-Stack/02.06-Clip/' || (adapter_config->>'instructionsFilePath'))
  )
WHERE id = '{{AGENT_ID}}'
AND adapter_config->>'instructionsFilePath' NOT LIKE '/%';

Wakeup manual de agente

Para forcar um agente a acordar imediatamente (ex: testar, desbloquear):

DO $$
DECLARE wakeup_id uuid;
BEGIN
  INSERT INTO agent_wakeup_requests (company_id, agent_id, source, trigger_detail, reason, payload, status, requested_at, created_at, updated_at)
  VALUES ('ebe10308-efd7-453f-86ab-13e6fe84004f', '{{AGENT_ID}}', 'on_demand', 'manual', '{{RAZAO}}', '{}'::jsonb, 'queued', NOW(), NOW(), NOW())
  RETURNING id INTO wakeup_id;

  INSERT INTO heartbeat_runs (company_id, agent_id, invocation_source, trigger_detail, status, wakeup_request_id, context_snapshot, created_at, updated_at)
  VALUES ('ebe10308-efd7-453f-86ab-13e6fe84004f', '{{AGENT_ID}}', 'on_demand', 'manual', 'queued', wakeup_id, '{"wakeReason": "manual_wakeup"}'::jsonb, NOW(), NOW());
END $$;

Nota: UPDATE directo em issues nao dispara wakeOnDemand — e preciso o par wakeup_request + heartbeat_run.

Criar agente novo via SQL (checklist OBRIGATÓRIA)

Ao criar agente directamente na BD (fora do fluxo OpenClaw), executar TODOS os passos:

  1. INSERT INTO agents (...) — dados do agente, incluindo:
    • adapter_type: 'process'
    • adapter_config: campos obrigatórios: model, instructionsFilePath (absoluto!), instructionsRootPath, instructionsEntryFile: "AGENTS.md", instructionsBundleMode: "external"
    • Para agentes autónomos: adicionar dangerouslySkipPermissions: true, cwd, timeoutSec
  2. INSERT INTO company_memberships (...)sem isto, permissões não funcionam (agentes criados via SQL não recebem membership automática)
  3. INSERT INTO principal_permission_grants (...) — se precisa de tasks:assign (C-Level, Directores)
  4. Criar AGENTS.md no path absoluto definido em instructionsFilePath
  5. Verificar: instructionsFilePath não começa com / → AGENTS.md não carrega (ver Passo 7c)

Acções disponíveis

Se o utilizador pedir:

  • Editar AGENTS.md: Abrir ficheiro com Read/Edit
  • Alterar config: UPDATE agents SET runtime_config = '...' WHERE id = '{{AGENT_ID}}';
  • Pausar: UPDATE agents SET status = 'paused', pause_reason = '...', paused_at = NOW() WHERE id = '{{AGENT_ID}}';
  • Despausar: UPDATE agents SET status = 'idle', pause_reason = NULL, paused_at = NULL WHERE id = '{{AGENT_ID}}';
  • Wakeup: Usar bloco SQL acima (wakeup_request + heartbeat_run)
  • Corrigir permissoes: Usar UPDATE adapter_config acima
  • Atribuir skill: curl -s -X POST http://localhost:3100/api/agents/{{AGENT_ID}}/skills/sync -H "Content-Type: application/json" -H "Authorization: Bearer $PAPERCLIP_API_KEY" -d '{"desiredSkills": ["key1", "key2"]}'
  • Ver skills empresa: query company_skills acima

Safety gate: verificacao de dependencias (OBRIGATORIO antes de DELETE/remocao)

Antes de apagar ou remover qualquer agente, executar SEMPRE:

SELECT 'budget_policies' as tabela, COUNT(*) as refs FROM budget_policies WHERE scope_type='agent' AND scope_id='{{AGENT_ID}}'
UNION ALL
SELECT 'budget_incidents', COUNT(*) FROM budget_incidents WHERE scope_type='agent' AND scope_id='{{AGENT_ID}}'
UNION ALL
SELECT 'heartbeat_runs', COUNT(*) FROM heartbeat_runs WHERE agent_id='{{AGENT_ID}}'
UNION ALL
SELECT 'issues', COUNT(*) FROM issues WHERE assignee_agent_id='{{AGENT_ID}}'
UNION ALL
SELECT 'issue_comments', COUNT(*) FROM issue_comments WHERE author_agent_id='{{AGENT_ID}}';
  • Se refs > 0 em budget_policies ou budget_incidents: apagar essas refs PRIMEIRO, senao o dashboard fica com "Agent not found"
  • Se refs > 0 em issues: reatribuir ou fechar as issues antes
  • Mostrar resultado ao utilizador e confirmar antes de prosseguir
  • NUNCA apagar agente sem verificar e limpar dependencias

Confirmar sempre antes de executar accoes destrutivas.


Healing Log

Registo de erros conhecidos e como evitá-los. Lido automaticamente antes de executar.

{"date":"2026-04-07","issue":"API skill attribution sem Authorization header — chamada rejeitada com 401","fix":"Adicionar -H 'Authorization: Bearer $PAPERCLIP_API_KEY' ao curl POST /api/agents/:id/skills/sync","source":"auto"}
{"date":"2026-04-07","issue":"Agente criado via SQL sem membership ficou com Board access required em todos os endpoints","fix":"Sempre inserir em company_memberships após INSERT em agents. Agentes via SQL não recebem membership automática — só via fluxo OpenClaw/hiring","source":"auto"}
{"date":"2026-04-07","issue":"instructionsBundleMode em falta na adapter_config — AGENTS.md não carregava","fix":"Incluir instructionsBundleMode: 'external' + instructionsRootPath + instructionsEntryFile: 'AGENTS.md' na adapter_config","source":"auto"}

Adicionar nova linha após cada erro corrigido.