Compare commits
8 Commits
6b3a6f2698
...
6285be6c2e
| Author | SHA1 | Date | |
|---|---|---|---|
| 6285be6c2e | |||
| 2252e1c29c | |||
| 19f89e0d69 | |||
| 19da1bed48 | |||
| 8bf46bcaf0 | |||
| b1d31ef152 | |||
| e2b086acbf | |||
| 24b0b68ed0 |
@@ -0,0 +1,28 @@
|
||||
# Plugin: acidaos
|
||||
|
||||
Skills especializadas para desenvolvimento do **AcidaOS** — o sistema operativo de negócio para PMEs da Descomplicar®.
|
||||
|
||||
## Arquitectura
|
||||
|
||||
O AcidaOS usa uma arquitectura Hub-and-Spoke:
|
||||
- **Core (Hub):** Rust + Axum — motor de orquestração de agentes, API gRPC/REST interna
|
||||
- **Spokes:** Next.js/TypeScript — dashboards, UI, aplicações de negócio
|
||||
|
||||
## Skills disponíveis
|
||||
|
||||
| Skill | Trigger | Descrição |
|
||||
|-------|---------|-----------|
|
||||
| `acidaos:rust-dev` | `/rust-dev`, desenvolvimento Core | Desenvolvimento do Core em Rust (Axum, Tokio) |
|
||||
| `acidaos:spoke-dev` | `/spoke-dev`, desenvolvimento Spokes | Aplicações Next.js e componentes React |
|
||||
| `acidaos:devops` | `/acidaos-pipeline`, CI/CD | Pipelines Gitea Actions para o AcidaOS |
|
||||
| `acidaos:docs` | `/acidaos-docs`, documentação técnica | Gerar documentação a partir do código |
|
||||
|
||||
## Referências
|
||||
|
||||
- Arquitectura: `Hub/01-Planeamento/AcidaOS/Relatorio_Arquitetura_Core.md`
|
||||
- SPECs: `Hub/01-Planeamento/AcidaOS/acidaos-core/SPECs.md`
|
||||
- Visão estratégica: `Hub/01-Planeamento/AcidaOS/AcidaOS_Visao-Estrategica.md`
|
||||
- Repositório: Gitea `acidaos-core`, `acidaos-dashboard`
|
||||
|
||||
---
|
||||
**Versão**: 1.0.0 | **Autor**: Descomplicar® | **Data**: 2026-03-12
|
||||
@@ -0,0 +1,316 @@
|
||||
---
|
||||
name: devops
|
||||
description: CI/CD pipelines para o AcidaOS via Gitea Actions — build, test, deploy para acidaos-core (Rust) e acidaos-dashboard (Next.js). Usar quando "pipeline acidaos", "gitea actions acidaos", "ci cd acidaos", "deploy acidaos", "workflow acidaos".
|
||||
allowed-tools: Read, Write, Edit, Bash, mcp__gitea__list_repo_action_workflows, mcp__gitea__get_repo_action_workflow, mcp__gitea__create_file, mcp__gitea__update_file, mcp__memory-supabase__search_memories
|
||||
---
|
||||
|
||||
# AcidaOS DevOps — Gitea Actions
|
||||
|
||||
Skill para criar e gerir pipelines CI/CD do AcidaOS via **Gitea Actions**.
|
||||
|
||||
> **Atenção:** O AcidaOS usa **Gitea Actions**, não GitHub Actions. A sintaxe é compatível mas o runner é self-hosted em `mcp-hub.descomplicar.pt`.
|
||||
|
||||
## Contexto
|
||||
|
||||
```
|
||||
Repositórios Gitea:
|
||||
acidaos-core → Rust Core
|
||||
acidaos-dashboard → Next.js Dashboard
|
||||
|
||||
Runners:
|
||||
self-hosted @ mcp-hub.descomplicar.pt
|
||||
Labels: [self-hosted, linux, rust, node]
|
||||
|
||||
Deploy target:
|
||||
EasyPanel (projectName: "descomplicar")
|
||||
acidaos-core → serviceName: "acidaos-core"
|
||||
acidaos-dashboard → serviceName: "acidaos-dashboard"
|
||||
```
|
||||
|
||||
## Protocolo Inicial
|
||||
|
||||
```
|
||||
mcp__memory-supabase__search_memories "acidaos devops pipeline"
|
||||
mcp__gitea__list_repo_action_workflows <repo> # ver workflows existentes
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pipelines Disponíveis
|
||||
|
||||
### 1. Core Rust — CI Pipeline
|
||||
|
||||
**Ficheiro:** `.gitea/workflows/ci.yml`
|
||||
|
||||
```yaml
|
||||
name: AcidaOS Core CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, develop]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Verificar código
|
||||
runs-on: [self-hosted, linux, rust]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Cache Cargo
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
target/
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- name: Cargo check
|
||||
run: cargo check --all-features
|
||||
|
||||
- name: Clippy
|
||||
run: cargo clippy --all-features -- -D warnings
|
||||
|
||||
- name: Formatação
|
||||
run: cargo fmt --all -- --check
|
||||
|
||||
test:
|
||||
name: Testes
|
||||
needs: check
|
||||
runs-on: [self-hosted, linux, rust]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Cache Cargo
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
target/
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- name: Testes unitários
|
||||
run: cargo test --all-features
|
||||
|
||||
- name: Testes de integração
|
||||
run: cargo test --test '*' --all-features
|
||||
env:
|
||||
ACIDAOS_ENV: test
|
||||
DATABASE_URL: ${{ secrets.TEST_DATABASE_URL }}
|
||||
|
||||
security:
|
||||
name: Auditoria de segurança
|
||||
runs-on: [self-hosted, linux, rust]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: cargo-audit
|
||||
run: |
|
||||
cargo install cargo-audit --quiet
|
||||
cargo audit
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. Core Rust — Deploy Pipeline
|
||||
|
||||
**Ficheiro:** `.gitea/workflows/deploy.yml`
|
||||
|
||||
```yaml
|
||||
name: AcidaOS Core Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
tags: ['v*']
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
name: Deploy para EasyPanel
|
||||
runs-on: [self-hosted, linux, rust]
|
||||
environment: production
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build release
|
||||
run: cargo build --release
|
||||
|
||||
- name: Build Docker image
|
||||
run: |
|
||||
docker build \
|
||||
-t acidaos-core:${{ gitea.sha }} \
|
||||
-t acidaos-core:latest \
|
||||
.
|
||||
|
||||
- name: Push para registry
|
||||
run: |
|
||||
docker tag acidaos-core:latest \
|
||||
registry.descomplicar.pt/acidaos/core:latest
|
||||
docker push registry.descomplicar.pt/acidaos/core:latest
|
||||
env:
|
||||
DOCKER_REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
||||
|
||||
- name: Deploy via EasyPanel API
|
||||
run: |
|
||||
curl -X POST \
|
||||
"https://easypanel.descomplicar.pt/api/deploy" \
|
||||
-H "Authorization: Bearer ${{ secrets.EASYPANEL_TOKEN }}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"projectName": "descomplicar",
|
||||
"serviceName": "acidaos-core",
|
||||
"image": "registry.descomplicar.pt/acidaos/core:latest"
|
||||
}'
|
||||
|
||||
- name: Verificar health
|
||||
run: |
|
||||
sleep 10
|
||||
curl -f http://acidaos-core.descomplicar.pt/health || exit 1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. Dashboard Next.js — CI Pipeline
|
||||
|
||||
**Ficheiro:** `.gitea/workflows/ci.yml` (no repo acidaos-dashboard)
|
||||
|
||||
```yaml
|
||||
name: AcidaOS Dashboard CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, develop]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
lint-typecheck:
|
||||
name: Lint e TypeCheck
|
||||
runs-on: [self-hosted, linux, node]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Instalar dependências
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: TypeScript check
|
||||
run: pnpm tsc --noEmit
|
||||
|
||||
- name: ESLint
|
||||
run: pnpm lint
|
||||
|
||||
test:
|
||||
name: Testes
|
||||
needs: lint-typecheck
|
||||
runs-on: [self-hosted, linux, node]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Instalar dependências
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Testes unitários
|
||||
run: pnpm test
|
||||
|
||||
- name: Build de verificação
|
||||
run: pnpm build
|
||||
env:
|
||||
ACIDAOS_CORE_URL: http://localhost:3001
|
||||
|
||||
e2e:
|
||||
name: Testes E2E
|
||||
needs: test
|
||||
runs-on: [self-hosted, linux, node]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Instalar dependências
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Instalar Playwright
|
||||
run: pnpm exec playwright install --with-deps chromium
|
||||
|
||||
- name: Testes E2E
|
||||
run: pnpm test:e2e
|
||||
env:
|
||||
BASE_URL: http://localhost:3000
|
||||
ACIDAOS_CORE_URL: http://localhost:3001
|
||||
|
||||
- name: Upload relatório E2E
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: playwright-report
|
||||
path: playwright-report/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Gestão via MCP Gitea
|
||||
|
||||
```javascript
|
||||
// Listar workflows de um repo
|
||||
mcp__gitea__list_repo_action_workflows({ owner: "descomplicar", repo: "acidaos-core" })
|
||||
|
||||
// Ver runs recentes
|
||||
mcp__gitea__list_repo_action_runs({ owner: "descomplicar", repo: "acidaos-core" })
|
||||
|
||||
// Desencadear workflow manualmente
|
||||
mcp__gitea__dispatch_repo_action_workflow({
|
||||
owner: "descomplicar",
|
||||
repo: "acidaos-core",
|
||||
workflow_id: "deploy.yml",
|
||||
ref: "main"
|
||||
})
|
||||
|
||||
// Ver logs de uma job
|
||||
mcp__gitea__get_repo_action_job_log_preview({ owner: "descomplicar", repo: "acidaos-core", job_id: <id> })
|
||||
```
|
||||
|
||||
## Segredos necessários (Gitea Secrets)
|
||||
|
||||
| Segredo | Usado em | Descrição |
|
||||
|---------|----------|-----------|
|
||||
| `TEST_DATABASE_URL` | Core CI | PostgreSQL de teste |
|
||||
| `REGISTRY_TOKEN` | Core Deploy | Token registry Docker |
|
||||
| `EASYPANEL_TOKEN` | Core/Dashboard Deploy | Token API EasyPanel |
|
||||
|
||||
## Checklist Pipeline Nova
|
||||
|
||||
- [ ] Workflow criado em `.gitea/workflows/`
|
||||
- [ ] Runner labels correctas (`self-hosted, linux, rust` ou `node`)
|
||||
- [ ] Cache Cargo/pnpm configurado
|
||||
- [ ] Segredos referenciados (não hardcoded)
|
||||
- [ ] Health check no deploy
|
||||
- [ ] Tested com `mcp__gitea__dispatch_repo_action_workflow`
|
||||
|
||||
---
|
||||
**Versão**: 1.0.0 | **Autor**: Descomplicar® | **Plugin**: acidaos
|
||||
@@ -0,0 +1,191 @@
|
||||
---
|
||||
name: docs
|
||||
description: Gerar documentação técnica do AcidaOS a partir do código — Rust (rustdoc), TypeScript (TypeDoc), e publicar no Outline. Usar quando "documentação acidaos", "gerar docs", "rustdoc", "typedoc", "acidaos-docs", documentar código do projecto.
|
||||
allowed-tools: Read, Write, Bash, mcp__gitea__get_file_content, mcp__gitea__get_dir_content, mcp__outline-api__create_document, mcp__outline-api__update_document, mcp__outline-api__search_documents, mcp__memory-supabase__search_memories
|
||||
---
|
||||
|
||||
# AcidaOS Docs
|
||||
|
||||
Skill para gerar e publicar documentação técnica do AcidaOS.
|
||||
|
||||
## Estratégia de Documentação
|
||||
|
||||
| Camada | Ferramenta | Destino |
|
||||
|--------|-----------|---------|
|
||||
| API interna Rust | `rustdoc` + comentários `///` | `/docs` no repo |
|
||||
| Componentes React | TypeDoc + Storybook | Storybook deployed |
|
||||
| Arquitectura | Mermaid.js diagramas | Outline (Wiki) |
|
||||
| Endpoints API | OpenAPI/Swagger | Outline (Wiki) |
|
||||
| Decisões técnicas | ADRs em Markdown | Outline (Wiki) |
|
||||
|
||||
## Protocolo
|
||||
|
||||
```
|
||||
mcp__memory-supabase__search_memories "acidaos documentação"
|
||||
mcp__outline-api__search_documents "acidaos" # ver docs existentes
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Operações
|
||||
|
||||
### 1. Gerar documentação Rust (`rustdoc`)
|
||||
|
||||
**Verificar comentários existentes:**
|
||||
```
|
||||
mcp__gitea__get_dir_content acidaos-core/src/
|
||||
```
|
||||
|
||||
**Gerar localmente (no container dev):**
|
||||
```bash
|
||||
cd /root/Dev/acidaos-core
|
||||
cargo doc --all-features --no-deps --open
|
||||
```
|
||||
|
||||
**Verificar cobertura de documentação:**
|
||||
```bash
|
||||
cargo doc --all-features 2>&1 | grep "warning: missing documentation"
|
||||
```
|
||||
|
||||
**Template comentários Rust:**
|
||||
```rust
|
||||
/// # ComponenteX
|
||||
///
|
||||
/// Breve descrição em uma linha.
|
||||
///
|
||||
/// ## Descrição detalhada
|
||||
///
|
||||
/// Explicação mais longa do comportamento e propósito.
|
||||
///
|
||||
/// ## Exemplos
|
||||
///
|
||||
/// ```rust
|
||||
/// use acidaos_core::ComponenteX;
|
||||
///
|
||||
/// let c = ComponenteX::new("id");
|
||||
/// assert!(c.is_valid());
|
||||
/// ```
|
||||
///
|
||||
/// ## Erros
|
||||
///
|
||||
/// Retorna [`Error::Init`] se a inicialização falhar.
|
||||
pub struct ComponenteX { ... }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. Gerar documentação TypeScript (TypeDoc)
|
||||
|
||||
**Verificar configuração:**
|
||||
```
|
||||
mcp__gitea__get_file_content acidaos-dashboard/typedoc.json
|
||||
```
|
||||
|
||||
**Gerar (no container dev):**
|
||||
```bash
|
||||
cd /root/Dev/acidaos-dashboard
|
||||
pnpm typedoc --out docs/api src/
|
||||
```
|
||||
|
||||
**Template JSDoc para componentes:**
|
||||
```typescript
|
||||
/**
|
||||
* AgentStatusCard — Mostra o estado de um agente AcidaOS
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <AgentStatusCard
|
||||
* agentId="agent-123"
|
||||
* onRefresh={() => refetch()}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export interface AgentStatusCardProps {
|
||||
/** ID único do agente no Core */
|
||||
agentId: string;
|
||||
/** Callback chamado ao clicar em refrescar */
|
||||
onRefresh?: () => void;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. Publicar arquitectura no Outline
|
||||
|
||||
**Criar/actualizar diagrama de arquitectura:**
|
||||
|
||||
```javascript
|
||||
mcp__outline-api__create_document({
|
||||
title: "AcidaOS — Arquitectura Core",
|
||||
text: `
|
||||
# Arquitectura AcidaOS Core
|
||||
|
||||
## Hub-and-Spoke
|
||||
|
||||
\`\`\`mermaid
|
||||
graph TD
|
||||
A[acidaos-dashboard] -->|API REST| B[AcidaOS Core]
|
||||
B --> C[Agent Kernel]
|
||||
B --> D[Security & Sandboxing]
|
||||
B --> E[Memory Engine]
|
||||
B --> F[Observability]
|
||||
\`\`\`
|
||||
|
||||
## Componentes
|
||||
|
||||
| Componente | Linguagem | Responsabilidade |
|
||||
|-----------|-----------|-----------------|
|
||||
| Core | Rust | Orquestração de agentes |
|
||||
| Dashboard | Next.js | Interface de utilizador |
|
||||
`,
|
||||
collectionId: "<acidaos-collection-id>"
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. Criar ADR (Architecture Decision Record)
|
||||
|
||||
**Template ADR:**
|
||||
```markdown
|
||||
# ADR-<NNN>: <Título da Decisão>
|
||||
|
||||
**Data:** YYYY-MM-DD
|
||||
**Estado:** [Proposto | Aceite | Depreciado | Substituído por ADR-NNN]
|
||||
|
||||
## Contexto
|
||||
|
||||
[Situação que levou à necessidade de tomar esta decisão]
|
||||
|
||||
## Opções Consideradas
|
||||
|
||||
1. **Opção A** — descrição breve
|
||||
2. **Opção B** — descrição breve
|
||||
|
||||
## Decisão
|
||||
|
||||
[Opção escolhida e porquê]
|
||||
|
||||
## Consequências
|
||||
|
||||
**Positivas:**
|
||||
- [Consequência positiva]
|
||||
|
||||
**Negativas / Trade-offs:**
|
||||
- [Limitação ou custo]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quality Gate de Documentação
|
||||
|
||||
Antes de marcar tarefa como concluída:
|
||||
|
||||
- [ ] Rust: zero `missing documentation` warnings em `cargo doc`
|
||||
- [ ] TypeScript: TypeDoc gera sem erros
|
||||
- [ ] Novos módulos têm exemplos de uso nos comentários
|
||||
- [ ] Diagramas Mermaid actualizado se arquitectura mudou
|
||||
- [ ] ADR criado para decisões técnicas significativas
|
||||
|
||||
---
|
||||
**Versão**: 1.0.0 | **Autor**: Descomplicar® | **Plugin**: acidaos
|
||||
@@ -0,0 +1,232 @@
|
||||
---
|
||||
name: rust-dev
|
||||
description: Desenvolvimento do AcidaOS Core em Rust — criar crates, definir endpoints Axum, debugar erros do compilador. Usar quando "rust", "axum", "cargo", "crate", "core", "acidaos core", "rust error", "borrow checker", desenvolvimento do motor central do AcidaOS.
|
||||
allowed-tools: Read, Write, Edit, Bash, mcp__memory-supabase__search_memories, mcp__gitea__get_file_content, mcp__gitea__create_file, mcp__gitea__update_file
|
||||
---
|
||||
|
||||
# AcidaOS Rust Dev
|
||||
|
||||
Skill para desenvolvimento do **Core do AcidaOS** em Rust.
|
||||
|
||||
## Contexto do Projecto
|
||||
|
||||
```
|
||||
acidaos-core (Rust)
|
||||
├── src/
|
||||
│ ├── api/ ← endpoints Axum (REST/gRPC)
|
||||
│ ├── kernel/ ← Agent Kernel & Scheduler
|
||||
│ ├── security/ ← Sandboxing, isolamento WASM
|
||||
│ ├── memory/ ← Context & Memory Engine
|
||||
│ ├── registry/ ← Parsers de skills/plugins
|
||||
│ ├── observability/← Logging, tracing
|
||||
│ └── ethics/ ← Constitution Engine
|
||||
├── crates/ ← bibliotecas internas
|
||||
└── Cargo.toml
|
||||
```
|
||||
|
||||
**Stack:** Rust stable | Axum | Tokio | Serde | Tracing | SQLx | wasmtime
|
||||
|
||||
## Protocolo Inicial
|
||||
|
||||
```
|
||||
mcp__memory-supabase__search_memories "acidaos core [componente]"
|
||||
mcp__gitea__get_file_content acidaos-core/Cargo.toml # verificar dependências actuais
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Operações
|
||||
|
||||
### 1. Criar novo crate (`/rust-create-crate`)
|
||||
|
||||
**Input:** Nome do crate e propósito
|
||||
|
||||
**Passos:**
|
||||
1. Criar directório `crates/<nome>/`
|
||||
2. Gerar `Cargo.toml` com dependências mínimas
|
||||
3. Criar `src/lib.rs` com estrutura base
|
||||
4. Adicionar ao workspace `Cargo.toml`
|
||||
5. Criar `src/error.rs` com tipo de erro custom usando `thiserror`
|
||||
|
||||
**Template `Cargo.toml`:**
|
||||
```toml
|
||||
[package]
|
||||
name = "acidaos-<nome>"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "<descrição>"
|
||||
|
||||
[dependencies]
|
||||
tracing = "0.1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
thiserror = "1"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
```
|
||||
|
||||
**Template `src/lib.rs`:**
|
||||
```rust
|
||||
//! # acidaos-<nome>
|
||||
//!
|
||||
//! <Descrição do módulo>
|
||||
//!
|
||||
//! ## Exemplo
|
||||
//! ```rust
|
||||
//! use acidaos_<nome>::<TipoPrincipal>;
|
||||
//! ```
|
||||
|
||||
mod error;
|
||||
pub use error::Error;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
```
|
||||
|
||||
**Template `src/error.rs`:**
|
||||
```rust
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error("Erro de inicialização: {0}")]
|
||||
Init(String),
|
||||
|
||||
#[error("Operação não suportada: {0}")]
|
||||
Unsupported(String),
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. Definir endpoint Axum (`/rust-define-api-endpoint`)
|
||||
|
||||
**Input:** Definição do endpoint (método, path, request/response types) de uma SPEC.md
|
||||
|
||||
**Template de endpoint:**
|
||||
```rust
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
http::StatusCode,
|
||||
response::Json,
|
||||
routing::{get, post},
|
||||
Router,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::instrument;
|
||||
|
||||
// --- Tipos ---
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct <NomeRequest> {
|
||||
// campos do request
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct <NomeResponse> {
|
||||
// campos do response
|
||||
}
|
||||
|
||||
// --- Handler ---
|
||||
|
||||
#[instrument(skip(state))]
|
||||
pub async fn <nome_handler>(
|
||||
State(state): State<AppState>,
|
||||
Json(req): Json<<NomeRequest>>,
|
||||
) -> Result<Json<<NomeResponse>>, StatusCode> {
|
||||
tracing::info!("Processando <nome>");
|
||||
|
||||
// lógica do handler
|
||||
|
||||
Ok(Json(<NomeResponse> {
|
||||
// preencher response
|
||||
}))
|
||||
}
|
||||
|
||||
// --- Router ---
|
||||
|
||||
pub fn <nome>_router() -> Router<AppState> {
|
||||
Router::new()
|
||||
.route("/<path>", post(<nome_handler>))
|
||||
}
|
||||
```
|
||||
|
||||
**Princípios obrigatórios:**
|
||||
- `#[instrument]` em todos os handlers (observabilidade)
|
||||
- Erros retornam `StatusCode` ou tipo de erro custom (nunca `.unwrap()`)
|
||||
- Estado partilhado via `State<AppState>` (não globals)
|
||||
- Validação de input antes de processamento
|
||||
|
||||
---
|
||||
|
||||
### 3. Debugar erro do compilador (`/rust-debug-compiler-error`)
|
||||
|
||||
**Input:** Output completo de `cargo build` ou `cargo check`
|
||||
|
||||
**Protocolo de análise:**
|
||||
|
||||
1. **Identificar categoria do erro:**
|
||||
| Código | Categoria | Solução típica |
|
||||
|--------|-----------|----------------|
|
||||
| E0382 | Move após uso | Usar `clone()` ou referências `&` |
|
||||
| E0502 | Borrow conflict | Reordenar operações, usar `Arc<Mutex<>>` |
|
||||
| E0308 | Type mismatch | Verificar tipos, usar `.into()` ou cast explícito |
|
||||
| E0277 | Trait not impl | Implementar trait ou usar tipo diferente |
|
||||
| E0499 | Multiple mut borrows | Usar `RefCell` ou reestruturar lógica |
|
||||
|
||||
2. **Analisar mensagem de erro completa** (incluindo `help:` e `note:`)
|
||||
|
||||
3. **Gerar código corrigido** com explicação da causa raiz
|
||||
|
||||
4. **Verificar:** `cargo check` passa antes de `cargo build`
|
||||
|
||||
**Padrões comuns no AcidaOS Core:**
|
||||
|
||||
```rust
|
||||
// ❌ Move não intencional
|
||||
let data = fetch_data();
|
||||
process(data);
|
||||
log(data); // ERRO: data foi moved
|
||||
|
||||
// ✅ Correcto
|
||||
let data = fetch_data();
|
||||
process(&data); // ou process(data.clone())
|
||||
log(&data);
|
||||
|
||||
// ❌ Async + lifetime
|
||||
async fn handler(req: &Request) -> Response { ... } // lifetime issue
|
||||
|
||||
// ✅ Correcto — owned ou Arc
|
||||
async fn handler(req: Arc<Request>) -> Response { ... }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quality Gate
|
||||
|
||||
Antes de fazer commit:
|
||||
|
||||
```bash
|
||||
cargo check # zero erros
|
||||
cargo clippy # zero warnings
|
||||
cargo test # todos os testes passam
|
||||
cargo fmt --check # formatação correcta
|
||||
```
|
||||
|
||||
**Standards:**
|
||||
- Zero `unwrap()` em código de produção — usar `?` ou `expect("mensagem descritiva")`
|
||||
- Funções async < 50 linhas
|
||||
- `tracing::instrument` em todas as funções públicas async
|
||||
- Documentação `///` em todos os tipos e funções públicas
|
||||
|
||||
---
|
||||
|
||||
## Checklist Entrega
|
||||
|
||||
- [ ] `cargo check` sem erros
|
||||
- [ ] `cargo clippy -- -D warnings` limpo
|
||||
- [ ] `cargo test` 100% pass
|
||||
- [ ] Documentação `///` completa
|
||||
- [ ] Observabilidade: `#[instrument]` nos handlers
|
||||
- [ ] Sem `.unwrap()` em paths críticos
|
||||
- [ ] CHANGELOG.md actualizado
|
||||
|
||||
---
|
||||
**Versão**: 1.0.0 | **Autor**: Descomplicar® | **Plugin**: acidaos
|
||||
@@ -0,0 +1,239 @@
|
||||
---
|
||||
name: spoke-dev
|
||||
description: Desenvolvimento de Spokes do AcidaOS em Next.js/TypeScript — criar aplicações Spoke, componentes React com testes e Storybook. Usar quando "spoke", "dashboard acidaos", "next.js acidaos", "componente react acidaos", "ts-create-spoke", "acidaos ui", "acidaos frontend".
|
||||
allowed-tools: Read, Write, Edit, Bash, mcp__memory-supabase__search_memories, mcp__gitea__get_file_content, mcp__gitea__create_file, mcp__gitea__update_file
|
||||
---
|
||||
|
||||
# AcidaOS Spoke Dev
|
||||
|
||||
Skill para desenvolvimento dos **Spokes do AcidaOS** em Next.js/TypeScript.
|
||||
|
||||
## Contexto do Projecto
|
||||
|
||||
Os Spokes são as aplicações de interface que comunicam com o Core via API interna:
|
||||
|
||||
```
|
||||
acidaos-dashboard (Next.js 15 + App Router)
|
||||
├── src/
|
||||
│ ├── app/ ← App Router pages
|
||||
│ ├── components/ ← Componentes React
|
||||
│ │ ├── ui/ ← Componentes base (shadcn/ui)
|
||||
│ │ └── features/ ← Componentes de funcionalidade
|
||||
│ ├── lib/
|
||||
│ │ ├── api/ ← Client para AcidaOS Core API
|
||||
│ │ └── hooks/ ← React hooks custom
|
||||
│ └── types/ ← TypeScript types partilhados
|
||||
├── __tests__/ ← Jest + Testing Library
|
||||
└── .storybook/ ← Storybook
|
||||
```
|
||||
|
||||
**Stack:** Next.js 15 | TypeScript 5 | React 19 | Tailwind CSS | shadcn/ui | Vercel AI SDK | Vitest | Playwright
|
||||
|
||||
## Protocolo Inicial
|
||||
|
||||
```
|
||||
mcp__memory-supabase__search_memories "acidaos spoke dashboard [componente]"
|
||||
mcp__gitea__get_file_content acidaos-dashboard/package.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Operações
|
||||
|
||||
### 1. Criar nova aplicação Spoke (`/ts-create-spoke-app`)
|
||||
|
||||
**Input:** Nome do Spoke e propósito
|
||||
|
||||
**Estrutura a criar:**
|
||||
```bash
|
||||
# No container dev (Regra #48)
|
||||
cd /root/Dev
|
||||
npx create-next-app@latest acidaos-<nome> \
|
||||
--typescript \
|
||||
--tailwind \
|
||||
--app \
|
||||
--src-dir \
|
||||
--import-alias "@/*" \
|
||||
--no-git
|
||||
```
|
||||
|
||||
**Ficheiros obrigatórios após scaffold:**
|
||||
|
||||
`src/lib/api/core-client.ts` — cliente para o Core:
|
||||
```typescript
|
||||
/**
|
||||
* AcidaOS Core API Client
|
||||
*
|
||||
* @author Descomplicar® Crescimento Digital
|
||||
* @link https://descomplicar.pt
|
||||
*/
|
||||
|
||||
const CORE_API_URL = process.env.ACIDAOS_CORE_URL ?? 'http://localhost:3001';
|
||||
|
||||
export async function coreRequest<T>(
|
||||
endpoint: string,
|
||||
options?: RequestInit
|
||||
): Promise<T> {
|
||||
const res = await fetch(`${CORE_API_URL}${endpoint}`, {
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-AcidaOS-Version': '1',
|
||||
...options?.headers,
|
||||
},
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`Core API error: ${res.status} ${res.statusText}`);
|
||||
}
|
||||
|
||||
return res.json() as Promise<T>;
|
||||
}
|
||||
```
|
||||
|
||||
`src/types/index.ts` — tipos base:
|
||||
```typescript
|
||||
export interface AgentTask {
|
||||
id: string;
|
||||
status: 'pending' | 'running' | 'completed' | 'failed';
|
||||
createdAt: string;
|
||||
completedAt?: string;
|
||||
}
|
||||
|
||||
export interface CoreHealth {
|
||||
status: 'healthy' | 'degraded' | 'down';
|
||||
version: string;
|
||||
uptime: number;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. Criar componente React (`/ts-create-react-component`)
|
||||
|
||||
**Input:** Descrição do componente (nome, props, comportamento)
|
||||
|
||||
**Template base — componente:**
|
||||
|
||||
`src/components/features/<Nome>/<Nome>.tsx`:
|
||||
```typescript
|
||||
/**
|
||||
* <Nome> — <Descrição curta>
|
||||
*
|
||||
* @author Descomplicar® Crescimento Digital
|
||||
*/
|
||||
|
||||
import { type FC } from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
interface <Nome>Props {
|
||||
// props do componente
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const <Nome>: FC<<Nome>Props> = ({ className }) => {
|
||||
return (
|
||||
<div className={cn('', className)}>
|
||||
{/* conteúdo */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
<Nome>.displayName = '<Nome>';
|
||||
```
|
||||
|
||||
**Template — teste (Vitest + Testing Library):**
|
||||
|
||||
`src/components/features/<Nome>/<Nome>.test.tsx`:
|
||||
```typescript
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { <Nome> } from './<Nome>';
|
||||
|
||||
describe('<Nome>', () => {
|
||||
it('renderiza sem erros', () => {
|
||||
render(<<Nome> />);
|
||||
// asserção específica ao componente
|
||||
});
|
||||
|
||||
it('aceita className personalizada', () => {
|
||||
const { container } = render(<<Nome> className="test-class" />);
|
||||
expect(container.firstChild).toHaveClass('test-class');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**Template — Storybook:**
|
||||
|
||||
`src/components/features/<Nome>/<Nome>.stories.tsx`:
|
||||
```typescript
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { <Nome> } from './<Nome>';
|
||||
|
||||
const meta: Meta<typeof <Nome>> = {
|
||||
title: 'Features/<Nome>',
|
||||
component: <Nome>,
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof <Nome>>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {},
|
||||
};
|
||||
```
|
||||
|
||||
**Criar index de exportação:**
|
||||
|
||||
`src/components/features/<Nome>/index.ts`:
|
||||
```typescript
|
||||
export { <Nome> } from './<Nome>';
|
||||
export type { <Nome>Props } from './<Nome>';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Padrões Obrigatórios
|
||||
|
||||
### TypeScript
|
||||
- `strict: true` em `tsconfig.json` — sem `any` implícito
|
||||
- Props sempre tipadas com interface exportada
|
||||
- Hooks custom em `src/lib/hooks/use<Nome>.ts`
|
||||
- Server Components por defeito; `'use client'` apenas quando necessário
|
||||
|
||||
### Performance
|
||||
- `next/image` para todas as imagens
|
||||
- `loading="lazy"` em componentes pesados
|
||||
- `Suspense` boundaries em torno de data-fetching
|
||||
- Cache de API calls com `unstable_cache` ou React cache
|
||||
|
||||
### Segurança
|
||||
- Nunca expor `ACIDAOS_CORE_URL` no cliente (apenas server-side)
|
||||
- Sanitizar input do utilizador antes de enviar ao Core
|
||||
- Usar Next.js Server Actions para mutações
|
||||
|
||||
---
|
||||
|
||||
## Quality Gate
|
||||
|
||||
```bash
|
||||
pnpm build # zero erros TypeScript
|
||||
pnpm test # todos os testes passam
|
||||
pnpm lint # zero erros ESLint
|
||||
```
|
||||
|
||||
## Checklist Entrega
|
||||
|
||||
- [ ] Tipos TypeScript sem `any`
|
||||
- [ ] Testes criados (>80% cobertura)
|
||||
- [ ] Story Storybook criada
|
||||
- [ ] `displayName` definido no componente
|
||||
- [ ] `pnpm build` sem erros
|
||||
- [ ] Client/Server components correctamente marcados
|
||||
- [ ] CHANGELOG.md actualizado
|
||||
|
||||
---
|
||||
**Versão**: 1.0.0 | **Autor**: Descomplicar® | **Plugin**: acidaos
|
||||
@@ -18,30 +18,30 @@ Esta skill deve ser activada quando:
|
||||
|
||||
## Capabilities
|
||||
|
||||
### 1. Discovery
|
||||
### 1. Gap Analysis
|
||||
- Mapear funcionalidades existentes
|
||||
- Identificar áreas sem cobertura
|
||||
- Sugerir plugins ou skills a desenvolver
|
||||
- Priorizar baseado em uso real
|
||||
|
||||
### 2. Discovery
|
||||
- Pesquisar marketplaces oficiais e comunitários
|
||||
- Avaliar relevância baseada no contexto actual
|
||||
- Identificar plugins com funcionalidades sobrepostas
|
||||
- Detectar plugins desactualizados ou abandonados
|
||||
|
||||
### 2. Evaluation
|
||||
### 3. Evaluation
|
||||
- Analisar qualidade do código (se open source)
|
||||
- Verificar compatibilidade com sistema actual
|
||||
- Avaliar segurança (hooks, permissões)
|
||||
- Medir popularidade e manutenção activa
|
||||
|
||||
### 3. Installation Management
|
||||
### 4. Installation Management
|
||||
- Instalar plugins recomendados
|
||||
- Configurar hooks e MCPs do plugin
|
||||
- Resolver conflitos com plugins existentes
|
||||
- Gerir actualizações e rollbacks
|
||||
|
||||
### 4. Gap Analysis
|
||||
- Mapear funcionalidades existentes
|
||||
- Identificar áreas sem cobertura
|
||||
- Sugerir plugins ou skills a desenvolver
|
||||
- Priorizar baseado em uso real
|
||||
|
||||
## Marketplaces Conhecidos
|
||||
|
||||
| Marketplace | URL | Tipo |
|
||||
@@ -56,12 +56,12 @@ Esta skill deve ser activada quando:
|
||||
|
||||
```
|
||||
score = 0
|
||||
score += keyword_match * 3 # Max 3
|
||||
score += category_align * 2 # Max 2
|
||||
score += popularity # Max 2 (>1k stars)
|
||||
score += recent_update # Max 1 (<30 days)
|
||||
score += no_conflicts * 2 # Max 2
|
||||
# Total max: 10
|
||||
score += keyword_match * 3 # Max 3 (bool: 0|1)
|
||||
score += category_align * 2 # Max 2 (bool: 0|1)
|
||||
score += popularity # Max 2 (tiered: 0=<100 stars, 1=100-1k, 2=>1k)
|
||||
score += recent_update # Max 1 (bool: 1 se último commit <30 dias)
|
||||
score += no_conflicts * 2 # Max 2 (bool: 0|1)
|
||||
# Total max: 3 + 2 + 2 + 1 + 2 = 10
|
||||
```
|
||||
|
||||
## Workflow
|
||||
@@ -97,3 +97,16 @@ Assistant: [Activa plugin-curator]
|
||||
- NUNCA instalar plugins de fontes não verificadas
|
||||
- Verificar SEMPRE conflitos antes de instalar
|
||||
- Manter registo de todos os plugins avaliados
|
||||
|
||||
---
|
||||
|
||||
## Healing Log
|
||||
|
||||
Registo de erros conhecidos e como evitá-los. Lido automaticamente antes de executar.
|
||||
|
||||
```jsonl
|
||||
{"date":"2026-04-06","issue":"Capabilities ordenadas por ordem diferente do Workflow (Discovery 1º, Gap Analysis 4º) — incoerência com fluxo ANALYSE GAPS → SEARCH → EVALUATE → RECOMMEND → INSTALL","fix":"Reordenadas Capabilities para seguir ordem do Workflow: 1.Gap Analysis 2.Discovery 3.Evaluation 4.Installation Management","source":"auto"}
|
||||
{"date":"2026-04-06","issue":"Scoring algorithm: score += popularity sem multiplicador e sem documentar escala — ambíguo se max é 1 (bool) ou 2 (tiered)","fix":"Adicionados comentários inline a todos os campos: tipo (bool/tiered), escala e critério. Confirmada soma: 3+2+2+1+2=10.","source":"auto"}
|
||||
```
|
||||
|
||||
*Adicionar nova linha após cada erro corrigido.*
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "crm-ops",
|
||||
"description": "CRM operations, sales management, leads, customers, estimates, invoices, tickets and expense tracking with Desk CRM. Backed by NotebookLM notebooks.",
|
||||
"version": "1.1.0",
|
||||
"version": "1.2.0",
|
||||
"author": {
|
||||
"name": "Descomplicar - Crescimento Digital",
|
||||
"url": "https://descomplicar.pt"
|
||||
@@ -14,6 +14,8 @@
|
||||
"leads",
|
||||
"facturacao",
|
||||
"tickets",
|
||||
"orcamento"
|
||||
"orcamento",
|
||||
"suporte",
|
||||
"sla"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
---
|
||||
name: support-specialist
|
||||
description: >
|
||||
Especialista de suporte D2 para triagem, atribuição e follow-up de tickets. Executor
|
||||
autónomo baseado nos PROCs D2-SUP-001/002/003. Usar para: triagem tickets, atribuição
|
||||
por prioridade SLA, follow-up clientes, verificação SLA, escalação automática, relatório
|
||||
suporte, gestão tickets Desk CRM.
|
||||
role: Especialista de Suporte D2
|
||||
domain: Support, CRM
|
||||
model: sonnet
|
||||
tools: Read, Glob, Grep, ToolSearch
|
||||
|
||||
primary_mcps:
|
||||
- desk-crm-v3
|
||||
recommended_mcps:
|
||||
- memory-supabase
|
||||
- mcp-time
|
||||
|
||||
primary_skills:
|
||||
- ticket-manage
|
||||
- ticket
|
||||
- ticket-triage
|
||||
- crm
|
||||
recommended_skills:
|
||||
- today
|
||||
- worklog
|
||||
|
||||
desk_project: 65
|
||||
tags:
|
||||
- agent
|
||||
- suporte
|
||||
- d2
|
||||
- tickets
|
||||
- sla
|
||||
- desk-crm
|
||||
version: "1.0"
|
||||
status: active
|
||||
quality_score: 75
|
||||
compliance:
|
||||
sacred_rules: true
|
||||
data_sources: true
|
||||
|
||||
reports_to: Emanuel Almeida
|
||||
collaborates_with:
|
||||
- sales-manager
|
||||
- lead-qualifier
|
||||
escalates_to:
|
||||
- Emanuel Almeida (P1 crítico, SLA violado)
|
||||
- D7 Tecnologia (problemas técnicos complexos)
|
||||
|
||||
created: "2026-04-07"
|
||||
updated: "2026-04-07"
|
||||
author: "Descomplicar®"
|
||||
---
|
||||
|
||||
# Support Specialist Descomplicar
|
||||
|
||||
Especialista de suporte D2 responsável pela triagem, atribuição e follow-up de tickets no Desk CRM. Opera de forma autónoma seguindo os PROCs D2 Suporte (SUP-001, SUP-002, SUP-003).
|
||||
|
||||
## System Prompt
|
||||
|
||||
Você é o especialista de suporte D2 da Descomplicar®. A sua missão é garantir que todos os tickets de suporte são tratados com profissionalismo, dentro dos SLAs definidos, e que os clientes recebem respostas claras e soluções eficazes.
|
||||
|
||||
### Regras Obrigatórias (checklist antes de agir)
|
||||
|
||||
- [ ] **SLA Primeiro**: Verificar sempre o estado SLA antes de responder (`/ticket-manage sla`)
|
||||
- [ ] **Contexto Cliente**: Consultar histórico do cliente via `/crm` antes de responder
|
||||
- [ ] **Checklist Fecho**: NUNCA fechar ticket sem checklist 100% completa (`/ticket-manage close`)
|
||||
- [ ] **Causa Raiz**: Identificar causa real, não só sintoma
|
||||
- [ ] **Confirmar Resolução**: Aguardar confirmação do cliente antes de fechar
|
||||
- [ ] **Registar Lições**: Documentar novos problemas na KB
|
||||
|
||||
### Prioridades de Actuação
|
||||
|
||||
1. **P1 Crítico** — Acção imediata (<30min). Notificar Emanuel se não resolvido em 1h
|
||||
2. **P2 Funcional** — Resposta em 2h. Escalar para D7 se não resolvido em 12h
|
||||
3. **P3 Dúvida** — Resposta em 4h. Consultar KB/WikiJS antes de responder
|
||||
4. **P4 Melhoria** — Registar para sprint. Confirmar ao cliente o registo
|
||||
|
||||
### Output Format Padrão
|
||||
|
||||
```markdown
|
||||
## Ticket #X — [Assunto]
|
||||
|
||||
### Estado
|
||||
- **Prioridade:** P[N] [Nome]
|
||||
- **Cliente:** [Nome] ([email])
|
||||
- **Aberto há:** Xh
|
||||
- **SLA:** [OK/EM RISCO/VIOLADO]
|
||||
|
||||
### Acção Tomada
|
||||
[Descrição do que foi feito]
|
||||
|
||||
### Próximo Passo
|
||||
[Acção seguinte + responsável + prazo]
|
||||
```
|
||||
|
||||
## Responsabilidades
|
||||
|
||||
- Triagem diária de tickets novos (classificar P1-P4 por impacto)
|
||||
- Atribuição correcta por área: técnica → D7, billing → D3, comercial → D1
|
||||
- Primeira resposta dentro do SLA em todos os tickets
|
||||
- Follow-up proactivo em tickets P1/P2 sem resposta do cliente >24h
|
||||
- Verificação semanal de cumprimento SLA e relatório (`/ticket-manage report`)
|
||||
- Fecho com checklist obrigatória e registo de lições aprendidas
|
||||
|
||||
## Workflows
|
||||
|
||||
### Workflow 1: Triagem Diária
|
||||
```
|
||||
1. /ticket-manage → Ver dashboard SLA + tickets em risco
|
||||
2. Para cada ticket NOVO:
|
||||
a. Classificar prioridade (P1-P4) por impacto
|
||||
b. /ticket-manage assign <id> → Atribuir a responsável
|
||||
c. Enviar confirmação de recepção ao cliente
|
||||
3. Para cada ticket EM RISCO:
|
||||
a. Avaliar estado de resolução
|
||||
b. Escalar se necessário: /ticket-manage escalate <id>
|
||||
4. Para cada ticket VIOLADO:
|
||||
a. Notificar Emanuel imediatamente (P1) ou criar alerta (P2/P3)
|
||||
```
|
||||
|
||||
### Workflow 2: Resolver Ticket
|
||||
```
|
||||
1. /ticket view <id> → Ler histórico completo
|
||||
2. /crm → Verificar contexto do cliente (projectos, histórico)
|
||||
3. Diagnosticar: reproduzir problema, consultar KB
|
||||
4. Se técnico WP/servidor: delegar para D7 (/ticket-manage escalate)
|
||||
5. Se billing: verificar /billing-check
|
||||
6. Aplicar solução e testar
|
||||
7. Responder ao cliente com solução + passos prevenção
|
||||
8. Aguardar confirmação → /ticket-manage close <id>
|
||||
```
|
||||
|
||||
### Workflow 3: Follow-up Semanal
|
||||
```
|
||||
1. Tickets no estado "Resolvido" há >3 dias → Contactar cliente
|
||||
2. Verificar NPS respostas dos últimos 7 dias
|
||||
3. /ticket-manage report → Gerar relatório SLA semanal
|
||||
4. Se cumprimento <90% em qualquer prioridade → Criar tarefa melhoria no Desk
|
||||
5. Registar acções correctivas como comentário no ticket #65
|
||||
```
|
||||
|
||||
### Workflow 4: Escalação P1
|
||||
```
|
||||
1. Ticket P1 identificado → Acção imediata
|
||||
2. Tentar resolução directa (máx 30min)
|
||||
3. Se não resolvido: /ticket-manage escalate para D7
|
||||
4. Notificar Emanuel: comentário interno + urgência
|
||||
5. Acompanhar até resolução — actualizações cada 30min
|
||||
```
|
||||
|
||||
## Knowledge Sources
|
||||
|
||||
### Referências PROCs (Consultar SEMPRE)
|
||||
```
|
||||
Read: /media/ealmeida/Dados/Hub/06-Operacoes/Procedimentos/D2-Suporte/PROC-D2-SUP-001-Atendimento-Cliente.md
|
||||
Read: /media/ealmeida/Dados/Hub/06-Operacoes/Procedimentos/D2-Suporte/PROC-D2-SUP-002-Ticketing-Workflow.md
|
||||
Read: /media/ealmeida/Dados/Hub/06-Operacoes/Procedimentos/D2-Suporte/PROC-D2-SUP-003-SLA-Management.md
|
||||
```
|
||||
|
||||
### Desk CRM (Dados Reais)
|
||||
```
|
||||
mcp__desk-crm-v3__get_tickets({ status: [1,2,3] })
|
||||
mcp__desk-crm-v3__get_ticket({ ticket_id: id })
|
||||
mcp__desk-crm-v3__reply_ticket({ ticket_id: id, message: html, internal: false })
|
||||
mcp__desk-crm-v3__close_ticket({ ticket_id: id })
|
||||
```
|
||||
|
||||
## Métricas de Sucesso
|
||||
|
||||
| Métrica | Meta |
|
||||
|---------|------|
|
||||
| SLA resposta cumprido | >95% |
|
||||
| SLA resolução cumprido | >90% |
|
||||
| Resolução 1º contacto | >60% |
|
||||
| NPS médio | >8/10 |
|
||||
| Tickets P1 violados | 0/semana |
|
||||
|
||||
## Colaboração
|
||||
|
||||
- **Reporta a**: Emanuel Almeida
|
||||
- **Colabora com**: sales-manager (tickets pós-venda), lead-qualifier (clientes novos)
|
||||
- **Escalar para**: Emanuel (P1 crítico, SLA violado), D7 Tecnologia (tech complexo)
|
||||
@@ -0,0 +1,319 @@
|
||||
---
|
||||
name: ticket-manage
|
||||
description: >
|
||||
Gestão completa do ciclo de vida de tickets D2 Suporte: criar com campos correctos, atribuir
|
||||
por prioridade e SLA, escalar quando SLA em risco, fechar com checklist obrigatória. Baseado
|
||||
em PROC-D2-SUP-001 (Atendimento), SUP-002 (Ticketing Workflow) e SUP-003 (SLA Management).
|
||||
Usar para: gestão ticket, SLA check, escalar suporte, fechar ticket, atribuir prioridade.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# /ticket-manage v1.0 — Gestão Integrada de Suporte D2
|
||||
|
||||
Workflow completo de gestão de tickets segundo os PROCs D2 Suporte (SUP-001/002/003).
|
||||
Complementa `/ticket` (operações básicas) com gestão de SLA, checklist e escalação automática.
|
||||
|
||||
---
|
||||
|
||||
## Comandos
|
||||
|
||||
| Comando | Função |
|
||||
|---------|--------|
|
||||
| `/ticket-manage` | Dashboard SLA + tickets em risco |
|
||||
| `/ticket-manage new` | Criar ticket com campos correctos |
|
||||
| `/ticket-manage assign <id>` | Atribuir por prioridade e SLA |
|
||||
| `/ticket-manage sla` | Ver estado SLA todos os tickets abertos |
|
||||
| `/ticket-manage escalate <id>` | Escalar com protocolo SUP-001 |
|
||||
| `/ticket-manage close <id>` | Fechar com checklist obrigatória |
|
||||
| `/ticket-manage report` | Relatório semanal SLA (SUP-003) |
|
||||
|
||||
---
|
||||
|
||||
## Prioridades e SLAs (SUP-003)
|
||||
|
||||
| Prioridade | Código | Primeira Resposta | Resolução | Escalação |
|
||||
|------------|--------|-------------------|-----------|-----------|
|
||||
| Crítico | P1 | <1h | <4h | Imediata D7 |
|
||||
| Funcional | P2 | <2h | <24h | Após 12h sem resolução |
|
||||
| Dúvida | P3 | <4h | <48h | Após 24h sem resolução |
|
||||
| Melhoria | P4 | <24h | Próximo sprint | N/A |
|
||||
|
||||
**Mapping Desk CRM:**
|
||||
| Desk Priority ID | Correspondência |
|
||||
|-----------------|----------------|
|
||||
| 4 — Urgente | P1 Crítico |
|
||||
| 3 — Alta | P2 Funcional |
|
||||
| 2 — Normal | P3 Dúvida |
|
||||
| 1 — Baixa | P4 Melhoria |
|
||||
|
||||
---
|
||||
|
||||
## `/ticket-manage` — Dashboard SLA
|
||||
|
||||
```
|
||||
1. mcp__desk-crm-v3__get_tickets({ status: [1,2,3] })
|
||||
2. Para cada ticket:
|
||||
a. Calcular tempo decorrido desde criação/última resposta
|
||||
b. Comparar com SLA da prioridade
|
||||
c. Classificar: OK / EM RISCO (<25% SLA restante) / VIOLADO (SLA excedido)
|
||||
3. Mostrar dashboard agrupado por estado SLA
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```markdown
|
||||
## 🎫 Dashboard Suporte — [DATA]
|
||||
|
||||
### 🔴 SLA Violado (N)
|
||||
| # | Prioridade | Cliente | Assunto | Há | SLA |
|
||||
|---|-----------|---------|---------|-----|-----|
|
||||
| #234 | P1 Crítico | ClienteA | Downtime servidor | 6h | <4h ❌ |
|
||||
|
||||
### 🟠 Em Risco (<25% restante)
|
||||
| # | Prioridade | Cliente | Assunto | Há | Restante |
|
||||
|---|-----------|---------|---------|-----|---------|
|
||||
| #236 | P2 Funcional | ClienteB | Erro login | 20h | 4h ⚠️ |
|
||||
|
||||
### 🟢 Dentro do SLA
|
||||
| # | Prioridade | Cliente | Assunto | Há |
|
||||
|---|-----------|---------|---------|-----|
|
||||
| #238 | P3 Dúvida | ClienteC | Como configurar X | 2h |
|
||||
|
||||
---
|
||||
**Resumo:** X violados | Y em risco | Z OK | Total: W tickets abertos
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `/ticket-manage new` — Criar Ticket Correcto
|
||||
|
||||
```
|
||||
1. Recolher campos obrigatórios:
|
||||
- Cliente: pesquisar mcp__desk-crm-v3__get_customers({ search: nome }) OU perguntar ID
|
||||
- Assunto: descrever o problema
|
||||
- Descrição completa
|
||||
- Prioridade: perguntar (P1/P2/P3/P4) ou sugerir baseado no assunto
|
||||
- Departamento: 1=Suporte Técnico, 2=Vendas, 3=Facturação, 4=Geral
|
||||
- Origem: Desk CRM, email, WhatsApp
|
||||
|
||||
2. Confirmar antes de criar:
|
||||
"Criar ticket P[X] para [Cliente] — [Assunto]? [Sim/Cancelar]"
|
||||
|
||||
3. mcp__desk-crm-v3__create_ticket({
|
||||
client_id: id,
|
||||
department_id: dept_id,
|
||||
priority: priority_id, // 4=Urgente, 3=Alta, 2=Normal, 1=Baixa
|
||||
subject: assunto,
|
||||
message: "<p>[descrição detalhada]</p><p><strong>Origem:</strong> [origem]</p>"
|
||||
})
|
||||
|
||||
4. Enviar confirmação de recepção ao cliente (se externo):
|
||||
- P1/P2: "Prioridade máxima. Analisando agora. Actualização em 30min/2h."
|
||||
- P3: "Recebemos. Resposta em 4h."
|
||||
- P4: "Registado para próximo sprint."
|
||||
|
||||
5. Output: "Ticket #X criado [P1-Crítico] | SLA: 1h resposta / 4h resolução"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `/ticket-manage assign <id>` — Atribuir por Prioridade
|
||||
|
||||
```
|
||||
1. mcp__desk-crm-v3__get_ticket({ ticket_id: id })
|
||||
2. Determinar responsável baseado em categoria:
|
||||
- Técnico (WP, servidor, código) → D7 Tecnologia
|
||||
- Billing, factura → D3 / Emanuel
|
||||
- Comercial, proposta → D1
|
||||
- Geral → Support Specialist (staff_id: 25)
|
||||
3. Se P1: notificar imediatamente (comentário interno + /task urgente)
|
||||
4. Registar atribuição como comentário interno:
|
||||
mcp__desk-crm-v3__reply_ticket({
|
||||
ticket_id: id,
|
||||
message: "<p>Atribuído: [Responsável] | SLA: [prazo] | Prioridade: [P1-P4]</p>",
|
||||
internal: true
|
||||
})
|
||||
5. Output: "Ticket #X atribuído a [Responsável] | SLA expira: [data/hora]"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `/ticket-manage sla` — Estado SLA Completo
|
||||
|
||||
```
|
||||
1. Recolher todos tickets abertos (status 1, 2, 3)
|
||||
2. Para cada ticket calcular:
|
||||
- tempo_decorrido = agora - created_at
|
||||
- sla_limite = por prioridade (1h/2h/4h/24h para primeira resposta)
|
||||
- percentagem_usada = tempo_decorrido / sla_limite × 100
|
||||
3. Agrupar: VIOLADO (>100%), EM RISCO (75-100%), OK (<75%)
|
||||
4. Highlight P1 e P2 com aviso especial
|
||||
```
|
||||
|
||||
**Alertas automáticos a gerar:**
|
||||
```
|
||||
P1 sem resposta >30min → "⚠️ CRÍTICO: Ticket #X P1 excede 30min. Acção imediata."
|
||||
P2 sem resposta >2h → "⚠️ ALERTA: Ticket #X P2 em risco. Re-atribuir?"
|
||||
Qualquer ticket >48h → "⏰ Ticket #X aberto há +48h. Revisar."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `/ticket-manage escalate <id>` — Escalação com Protocolo
|
||||
|
||||
```
|
||||
1. mcp__desk-crm-v3__get_ticket({ ticket_id: id })
|
||||
2. Determinar destino de escalação:
|
||||
- Tech complexo (WP, servidor) → D7 Tecnologia
|
||||
- Billing → D3 Facturação
|
||||
- Dev customizado → Project Manager
|
||||
3. Criar tarefa Desk CRM com contexto completo:
|
||||
mcp__desk-crm-v3__create_task({
|
||||
name: "[ESCALADO] Ticket #X: [Assunto]",
|
||||
description: "<h4>Escalado de Ticket #X</h4>
|
||||
<p><strong>Cliente:</strong> [nome]</p>
|
||||
<p><strong>Problema:</strong> [descrição]</p>
|
||||
<p><strong>Tentativas anteriores:</strong> [o que foi tentado]</p>
|
||||
<p><strong>SLA original:</strong> [prazo]</p>
|
||||
<p><strong>Motivo escalação:</strong> [razão]</p>",
|
||||
priority: 3,
|
||||
assignees: [1]
|
||||
})
|
||||
4. Informar cliente:
|
||||
mcp__desk-crm-v3__reply_ticket({
|
||||
ticket_id: id,
|
||||
message: "<p>Olá [Nome],</p>
|
||||
<p>A situação é mais complexa e foi escalada para a equipa especialista.
|
||||
Prazo estimado: 24-48h.</p>
|
||||
<p>Pedimos desculpa pela demora.</p>"
|
||||
})
|
||||
5. Nota interna com motivo detalhado
|
||||
6. Output: "Ticket #X escalado → Tarefa #Y | Dept: [destino] | ETA: [prazo]"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `/ticket-manage close <id>` — Fecho com Checklist Obrigatória
|
||||
|
||||
```
|
||||
1. mcp__desk-crm-v3__get_ticket({ ticket_id: id })
|
||||
2. Executar checklist SUP-001 (OBRIGATÓRIO — 100% antes de fechar):
|
||||
```
|
||||
|
||||
### Checklist de Fecho (SUP-001)
|
||||
|
||||
- [ ] Solução aplicada e testada
|
||||
- [ ] Explicação clara da solução enviada ao cliente
|
||||
- [ ] Causa raiz identificada (não só sintoma)
|
||||
- [ ] Passos de prevenção comunicados
|
||||
- [ ] Cliente confirmou resolução ("Resolvido? Feedback?")
|
||||
- [ ] Lições aprendidas registadas (se novo problema)
|
||||
- [ ] Follow-up 3 dias agendado (se P1/P2)
|
||||
|
||||
```
|
||||
3. Se checklist incompleta → NÃO fechar, listar pendentes
|
||||
4. Se 100% → fechar:
|
||||
mcp__desk-crm-v3__reply_ticket({
|
||||
ticket_id: id,
|
||||
message: "<p>Olá [Nome],</p>
|
||||
<p>Confirmamos que a situação foi resolvida.</p>
|
||||
<p><strong>Solução:</strong> [explicação]</p>
|
||||
<p><strong>Prevenção:</strong> [dica]</p>
|
||||
<p>Obrigado pelo contacto. Se precisar de algo, estamos disponíveis.</p>
|
||||
<p>Nota 1-10 sobre o suporte?</p>"
|
||||
})
|
||||
mcp__desk-crm-v3__close_ticket({ ticket_id: id })
|
||||
5. Output: "Ticket #X fechado ✓ | Checklist: 7/7 | [Data fecho]"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `/ticket-manage report` — Relatório SLA Semanal (SUP-003)
|
||||
|
||||
```
|
||||
1. Recolher tickets dos últimos 7 dias
|
||||
2. Calcular por prioridade:
|
||||
- % tickets que cumpriram SLA de resposta
|
||||
- % tickets que cumpriram SLA de resolução
|
||||
- Tempo médio de resposta e resolução
|
||||
3. Identificar violações e categorizar causas
|
||||
4. Comparar com semana anterior
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```markdown
|
||||
## Relatório SLA Semanal — [DD-MM-YYYY a DD-MM-YYYY]
|
||||
|
||||
### Cumprimento SLA (Meta: >90%)
|
||||
| Prioridade | Tickets | SLA Resposta | SLA Resolução | Violações |
|
||||
|-----------|---------|-------------|--------------|-----------|
|
||||
| P1 Crítico | 2 | 100% ✅ | 50% ⚠️ | 1 |
|
||||
| P2 Funcional | 8 | 87.5% ⚠️ | 100% ✅ | 1 |
|
||||
| P3 Dúvida | 15 | 93.3% ✅ | 86.7% ⚠️ | 2 |
|
||||
| P4 Melhoria | 3 | 100% ✅ | N/A | 0 |
|
||||
|
||||
### Top 3 Tickets Mais Lentos
|
||||
1. #X — [Assunto] — P2 — 36h — causa: dependência externa D7
|
||||
2. #Y — [Assunto] — P3 — 52h — causa: complexidade técnica
|
||||
3. #Z — [Assunto] — P1 — 5h30 — causa: falta de informação cliente
|
||||
|
||||
### Acções Correctivas
|
||||
- [ ] P2 cumprimento <90% → Revisão processo atribuição automática
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Referências PROCs
|
||||
|
||||
| PROC | Ficheiro Hub |
|
||||
|------|-------------|
|
||||
| SUP-001 Atendimento | `Hub/06-Operacoes/Procedimentos/D2-Suporte/PROC-D2-SUP-001-Atendimento-Cliente.md` |
|
||||
| SUP-002 Ticketing | `Hub/06-Operacoes/Procedimentos/D2-Suporte/PROC-D2-SUP-002-Ticketing-Workflow.md` |
|
||||
| SUP-003 SLA | `Hub/06-Operacoes/Procedimentos/D2-Suporte/PROC-D2-SUP-003-SLA-Management.md` |
|
||||
|
||||
---
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
- **NUNCA** fechar sem checklist 100% completa
|
||||
- **NUNCA** escalar sem informar o cliente
|
||||
- **NUNCA** ignorar tickets P1 >30min
|
||||
- **NUNCA** resolver sintoma sem identificar causa raiz
|
||||
|
||||
---
|
||||
|
||||
## Integração com Outras Skills
|
||||
|
||||
| Skill | Quando usar |
|
||||
|-------|-------------|
|
||||
| `/ticket` | Operações básicas (view, reply) |
|
||||
| `/ticket-triage` | Triagem automática SPAM/facturas |
|
||||
| `/crm` | Contexto cliente 360° |
|
||||
| `/today` | Dashboard diário com SLA alerts |
|
||||
|
||||
---
|
||||
|
||||
## Changelog
|
||||
|
||||
### v1.0.0 (07-04-2026)
|
||||
- Versão inicial baseada em SUP-001, SUP-002, SUP-003
|
||||
- Dashboard SLA com alertas automáticos
|
||||
- Criação com campos obrigatórios e confirmação recepção
|
||||
- Checklist fecho obrigatória 7 pontos
|
||||
- Protocolo escalação com contexto completo
|
||||
- Relatório semanal SLA
|
||||
|
||||
---
|
||||
|
||||
*Skill v1.0.0 | 07-04-2026 | Descomplicar® | Baseado em PROCs D2-SUP-001/002/003*
|
||||
|
||||
---
|
||||
|
||||
## Healing Log
|
||||
|
||||
Registo de erros conhecidos e como evitá-los. Lido automaticamente antes de executar.
|
||||
|
||||
```jsonl
|
||||
{"date":"","issue":"","fix":"","source":"user|auto"}
|
||||
```
|
||||
|
||||
*Adicionar nova linha após cada erro corrigido.*
|
||||
@@ -0,0 +1,132 @@
|
||||
---
|
||||
name: prompt-refine
|
||||
description: Transforma um pedido vago em prompt de excelência — especificação detalhada, inequívoca e estruturada para agentes de IA. Usar quando "refinar prompt", "melhorar prompt", "engenharia de prompts", "prompt para agente", "estruturar instrução IA", "prompt engineering".
|
||||
allowed-tools: Read, Write, mcp__memory-supabase__search_memories
|
||||
---
|
||||
|
||||
# /prompt-refine — Engenharia de Prompts
|
||||
|
||||
Transforma um pedido em linguagem natural num **prompt de excelência** pronto para ser executado por outro agente de IA.
|
||||
|
||||
## Quando Usar
|
||||
|
||||
- Pedido do utilizador é vago ou incompleto
|
||||
- Preparar instrução para um agente especializado
|
||||
- Criar template reutilizável para uma tarefa recorrente
|
||||
- Garantir qualidade máxima de input antes de executar
|
||||
|
||||
## Protocolo
|
||||
|
||||
### 1. Pesquisar contexto relevante
|
||||
|
||||
```
|
||||
mcp__memory-supabase__search_memories "<palavras-chave do pedido>"
|
||||
```
|
||||
|
||||
Verificar se há padrões ou decisões anteriores relacionadas.
|
||||
|
||||
### 2. Analisar o pedido original
|
||||
|
||||
Identificar:
|
||||
- **O quê** — resultado final esperado
|
||||
- **Para quê** — objectivo ou problema a resolver
|
||||
- **Quem** — agente ou utilizador que vai executar
|
||||
- **Restrições** — limitações técnicas, de formato ou de negócio
|
||||
- **Lacunas** — o que está implícito mas não dito
|
||||
|
||||
Se faltar informação crítica → perguntar antes de estruturar.
|
||||
|
||||
### 3. Estruturar o prompt de excelência
|
||||
|
||||
Usar este template como base:
|
||||
|
||||
```markdown
|
||||
## Função
|
||||
[Quem é / que papel assume o agente executante]
|
||||
|
||||
## Objectivo
|
||||
[O que deve ser produzido — resultado concreto e mensurável]
|
||||
|
||||
## Contexto
|
||||
[Informação de fundo necessária para compreender o pedido]
|
||||
|
||||
## Requisitos
|
||||
- [Requisito 1 — obrigatório]
|
||||
- [Requisito 2 — obrigatório]
|
||||
- [Restrição ou critério de qualidade]
|
||||
|
||||
## Input
|
||||
[Descrição do que o agente recebe como entrada]
|
||||
|
||||
## Output esperado
|
||||
[Formato, estrutura e conteúdo exacto do resultado]
|
||||
|
||||
## Exemplos
|
||||
### Exemplo positivo (fazer)
|
||||
[Exemplo concreto do resultado desejado]
|
||||
|
||||
### Exemplo negativo (não fazer)
|
||||
[Exemplo do que deve ser evitado]
|
||||
|
||||
## Critérios de sucesso
|
||||
- [ ] Critério 1
|
||||
- [ ] Critério 2
|
||||
```
|
||||
|
||||
### 4. Rever antes de entregar
|
||||
|
||||
Verificar checklist:
|
||||
- [ ] Sem ambiguidades — cada instrução tem um único significado possível
|
||||
- [ ] Completo — nenhuma informação crítica em falta
|
||||
- [ ] Accionável — o agente pode executar sem fazer suposições
|
||||
- [ ] Testável — é possível verificar se o output é correcto
|
||||
- [ ] Conciso — sem redundâncias ou instruções contraditórias
|
||||
|
||||
### 5. Entregar com explicação
|
||||
|
||||
Apresentar:
|
||||
1. **Prompt refinado** (em bloco de código copiável)
|
||||
2. **Decisões tomadas** — o que foi inferido vs. o que estava explícito
|
||||
3. **Questões em aberto** — se houver ambiguidades que o utilizador deve resolver
|
||||
|
||||
## Exemplos de Transformação
|
||||
|
||||
### Input vago
|
||||
> "Cria um agente que analisa clientes"
|
||||
|
||||
### Output refinado
|
||||
```markdown
|
||||
## Função
|
||||
Analista de CRM especializado em classificação de clientes
|
||||
|
||||
## Objectivo
|
||||
Gerar um relatório de segmentação de clientes com base em histórico de compras
|
||||
e interacções, identificando os segmentos de alto, médio e baixo valor.
|
||||
|
||||
## Contexto
|
||||
Base de dados Desk CRM com clientes activos nos últimos 12 meses.
|
||||
Acesso via mcp__desk-crm-v3__get_customers e mcp__desk-crm-v3__get_payments.
|
||||
|
||||
## Requisitos
|
||||
- Segmentar em 3 níveis: Alto Valor (>5000EUR/ano), Médio (1000-5000EUR), Baixo (<1000EUR)
|
||||
- Incluir número de projectos activos por cliente
|
||||
- Formato: tabela Markdown ordenada por valor decrescente
|
||||
|
||||
## Input
|
||||
Lista de IDs de clientes ou "todos" para analisar base completa
|
||||
|
||||
## Output esperado
|
||||
Tabela com: ID, Nome, Segmento, Volume Anual (EUR), Projectos Activos, Última Interacção
|
||||
```
|
||||
|
||||
## Padrões de Qualidade
|
||||
|
||||
| Característica | Mau prompt | Bom prompt |
|
||||
|---|---|---|
|
||||
| Especificidade | "Analisa dados" | "Calcula receita mensal por cliente em EUR para Q1 2026" |
|
||||
| Formato output | "Devolve resultados" | "Tabela Markdown com colunas: ID, Nome, Valor, Delta%" |
|
||||
| Critério sucesso | "Deve ser bom" | "Score de satisfação >85% em teste com 3 utilizadores reais" |
|
||||
| Contexto | Nenhum | "Sistema usa Desk CRM v3, moeda EUR, fuso Europe/Lisbon" |
|
||||
|
||||
---
|
||||
**Versão**: 1.0.0 | **Autor**: Descomplicar® | **Plugin**: dev-tools
|
||||
@@ -0,0 +1,340 @@
|
||||
---
|
||||
name: clip-agent
|
||||
description: 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".
|
||||
context: 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:
|
||||
```sql
|
||||
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:
|
||||
```sql
|
||||
SELECT name, role FROM agents WHERE id = '{{REPORTS_TO_ID}}';
|
||||
```
|
||||
|
||||
### Passo 3: Ultimas 5 runs
|
||||
|
||||
```sql
|
||||
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
|
||||
|
||||
```sql
|
||||
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`:
|
||||
```bash
|
||||
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
|
||||
|
||||
```sql
|
||||
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:
|
||||
```sql
|
||||
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
|
||||
|
||||
```sql
|
||||
-- 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.
|
||||
|
||||
```sql
|
||||
-- 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:
|
||||
```sql
|
||||
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
|
||||
|
||||
```sql
|
||||
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):
|
||||
```sql
|
||||
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.
|
||||
|
||||
```sql
|
||||
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/Hub` → **CRITICO: 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):**
|
||||
```sql
|
||||
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}}';
|
||||
```
|
||||
|
||||
**Nota sobre modelos e adapters:** O adapter `claude_local` já não é usado para agentes heartbeat. Distribuição actual:
|
||||
- 1 agente (CEO) usa `gemini_local` com `gemini-2.5-pro`
|
||||
- 50 agentes usam `gemini_local` com `gemini-2.5-flash`
|
||||
- 13 agentes usam `opencode_local` com `openrouter/x-ai/grok-4.1-fast`
|
||||
|
||||
**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), 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.
|
||||
|
||||
```sql
|
||||
-- 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:
|
||||
```sql
|
||||
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**.
|
||||
|
||||
```sql
|
||||
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:**
|
||||
```sql
|
||||
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):
|
||||
|
||||
```sql
|
||||
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:
|
||||
```sql
|
||||
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.
|
||||
|
||||
```jsonl
|
||||
{"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.*
|
||||
@@ -0,0 +1,398 @@
|
||||
---
|
||||
name: clip-health
|
||||
description: Diagnóstico rápido Paperclip — serviço, BD, heartbeats falhados, budget, disco. Usar quando "clip health", "saúde clip", "diagnóstico paperclip", "clip problemas".
|
||||
context: fork
|
||||
---
|
||||
|
||||
# /clip-health — Diagnostico Paperclip
|
||||
|
||||
Verificacao rapida da saude do sistema Clip.
|
||||
|
||||
## Constantes
|
||||
|
||||
```
|
||||
BD: PGPASSWORD="paperclip" psql -h localhost -p 54329 -U paperclip -d paperclip
|
||||
PAPERCLIP_DIR: /home/ealmeida/paperclip
|
||||
INSTANCE_DIR: /home/ealmeida/.paperclip/instances/default
|
||||
```
|
||||
|
||||
## Procedimento
|
||||
|
||||
Executar todas as verificacoes em paralelo, depois apresentar relatorio.
|
||||
|
||||
### Check 1: Serviço
|
||||
|
||||
```bash
|
||||
ps aux | grep -E "paperclip.*src/index|pnpm.*paperclipai" | grep -v grep | head -5
|
||||
```
|
||||
|
||||
- Processos encontrados = OK
|
||||
- Nenhum processo = CRITICO (iniciar com: `cd /home/ealmeida/paperclip && pnpm --filter @paperclipai/server dev`)
|
||||
|
||||
Verificar também memória partilhada PostgreSQL (causa do bug POSIX shared memory):
|
||||
```bash
|
||||
ls /dev/shm/PostgreSQL.* 2>/dev/null && echo "POSIX OK" || echo "POSIX em falta — BD pode falhar ao ligar"
|
||||
```
|
||||
|
||||
- POSIX OK = OK
|
||||
- POSIX em falta + processos activos = AVISO (PostgreSQL perdeu shared memory — reiniciar Paperclip)
|
||||
- POSIX em falta + sem processos = CRITICO
|
||||
|
||||
### Check 2: Base de dados
|
||||
|
||||
Conexao:
|
||||
```sql
|
||||
SELECT 1 as connected;
|
||||
```
|
||||
|
||||
Tamanho:
|
||||
```sql
|
||||
SELECT pg_size_pretty(pg_database_size('paperclip')) as db_size;
|
||||
```
|
||||
|
||||
Contagens:
|
||||
```sql
|
||||
SELECT
|
||||
(SELECT COUNT(*) FROM agents WHERE company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f') as agentes,
|
||||
(SELECT COUNT(*) FROM issues WHERE company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f') as issues,
|
||||
(SELECT COUNT(*) FROM heartbeat_runs hr JOIN agents a ON hr.agent_id = a.id WHERE a.company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f') as heartbeats;
|
||||
```
|
||||
|
||||
- Conexao OK = OK
|
||||
- Conexao falha = CRITICO
|
||||
|
||||
### Check 3: Heartbeats falhados (24h)
|
||||
|
||||
```sql
|
||||
SELECT a.name, hr.status, LEFT(hr.error, 70) as erro, hr.started_at::timestamp(0)
|
||||
FROM heartbeat_runs hr JOIN agents a ON hr.agent_id = a.id
|
||||
WHERE a.company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
AND hr.status IN ('failed','error')
|
||||
AND hr.started_at > NOW() - INTERVAL '24 hours'
|
||||
ORDER BY hr.started_at DESC
|
||||
LIMIT 20;
|
||||
```
|
||||
|
||||
- 0 falhas = OK
|
||||
- 1-2 falhas = AVISO
|
||||
- 3+ falhas = CRITICO
|
||||
|
||||
### Check 4: Budget
|
||||
|
||||
```sql
|
||||
SELECT name, budget_monthly_cents, spent_monthly_cents,
|
||||
CASE WHEN budget_monthly_cents > 0
|
||||
THEN ROUND(spent_monthly_cents::numeric / budget_monthly_cents * 100, 1)
|
||||
ELSE 0 END as pct
|
||||
FROM agents
|
||||
WHERE company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
AND budget_monthly_cents > 0
|
||||
AND spent_monthly_cents > 0
|
||||
ORDER BY pct DESC;
|
||||
```
|
||||
|
||||
- <80% = OK
|
||||
- 80-95% = AVISO
|
||||
- >95% = CRITICO
|
||||
|
||||
### Check 5: Disco
|
||||
|
||||
```bash
|
||||
df -h /home/ealmeida/.paperclip/ | tail -1
|
||||
```
|
||||
|
||||
```bash
|
||||
du -sh /home/ealmeida/.paperclip/instances/default/db/ 2>/dev/null
|
||||
du -sh /home/ealmeida/.paperclip/instances/default/logs/ 2>/dev/null
|
||||
du -sh /home/ealmeida/.paperclip/instances/default/data/ 2>/dev/null
|
||||
```
|
||||
|
||||
- >30% livre = OK
|
||||
- 15-30% livre = AVISO
|
||||
- <15% livre = CRITICO
|
||||
|
||||
### Check 6: API Health
|
||||
|
||||
```bash
|
||||
curl -s --max-time 5 http://localhost:3100/health 2>/dev/null || echo "FALHA"
|
||||
```
|
||||
|
||||
Esperado: `{"status":"ok","version":"...","deploymentMode":"authenticated","bootstrapStatus":"ready"}`
|
||||
|
||||
- `status: ok` = OK
|
||||
- `{"error":"Board access required"}` = AVISO (API responde mas sem auth — BD operacional)
|
||||
- FALHA/timeout = CRITICO (servidor não responde)
|
||||
|
||||
### Check 7: Gateway MCPs
|
||||
|
||||
```bash
|
||||
curl -s --max-time 5 https://gateway.descomplicar.pt/health 2>/dev/null || echo "FALHA"
|
||||
```
|
||||
|
||||
- Responde = OK
|
||||
- Timeout/falha = AVISO
|
||||
|
||||
### Check 8: Referencias orfas na BD
|
||||
|
||||
Budget policies e incidents a referenciar entidades inexistentes:
|
||||
|
||||
Invocar tool MCP: `mcp__paperclip__diag_budget_orphans`
|
||||
|
||||
- 0 orfaos = OK
|
||||
- 1+ orfaos = CRITICO (causa "Agent not found" no dashboard, apagar com DELETE)
|
||||
|
||||
### Check 9: Instancias duplicadas
|
||||
|
||||
```bash
|
||||
ss -tlnp | grep -E "310[0-9]" | wc -l
|
||||
```
|
||||
|
||||
- 1 instancia = OK
|
||||
- 2+ instancias = CRITICO (processos orfaos do pnpm, matar com `kill -9` os PIDs com PPID=1)
|
||||
|
||||
Detalhe dos processos:
|
||||
```bash
|
||||
ss -tlnp | grep -E "310[0-9]"
|
||||
```
|
||||
|
||||
### Check 10: Porta vs Cloudflare Tunnel
|
||||
|
||||
```bash
|
||||
TUNNEL_PORT=$(grep -A1 "clip.descomplicar.pt" ~/.cloudflared/config.yml | grep service | grep -oP ':\K[0-9]+')
|
||||
ACTUAL_PORT=$(ss -tlnp | grep -E "310[0-9]" | head -1 | grep -oP ':310[0-9]' | tr -d ':')
|
||||
echo "Tunnel: $TUNNEL_PORT | Servidor: $ACTUAL_PORT"
|
||||
```
|
||||
|
||||
- Iguais = OK
|
||||
- Diferentes = CRITICO (tunnel a apontar para porta errada, dashboard inacessivel)
|
||||
|
||||
### Check 11: Agentes sem permissoes bash
|
||||
|
||||
Invocar tool MCP: `mcp__paperclip__diag_agents_missing_permissions`
|
||||
|
||||
- 0 = OK
|
||||
- 1+ = CRITICO (agentes vao ficar bloqueados ao executar bash — corrigir com `/clip-agent`)
|
||||
|
||||
### Check 12: Agentes sem heartbeat
|
||||
|
||||
Invocar tool MCP: `mcp__paperclip__diag_agents_missing_heartbeat`
|
||||
|
||||
- 0 = OK
|
||||
- 1+ = AVISO (agentes nao acordam — podem nao processar issues)
|
||||
|
||||
### Check 13: Routine triggers com kind errado ou next_run_at NULL
|
||||
|
||||
Invocar tool MCP: `mcp__paperclip__diag_routine_triggers_broken`
|
||||
|
||||
- 0 = OK
|
||||
- 1+ = CRITICO (routines nao vao disparar — kind deve ser 'schedule' e next_run_at populado)
|
||||
|
||||
### Check 14: Issues PUBLICAR NOTICIA sem assignee
|
||||
|
||||
```sql
|
||||
SELECT COUNT(*) FROM issues
|
||||
WHERE company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
AND title LIKE 'PUBLICAR NOTICIA%'
|
||||
AND status = 'todo'
|
||||
AND assignee_agent_id IS NULL;
|
||||
```
|
||||
|
||||
- 0 = OK
|
||||
- 1+ = AVISO (cron assign-copywriter.sh devia atribuir a cada 5 min — verificar crontab)
|
||||
|
||||
### Check 15: Qualidade artigos publicados (ultimas 24h)
|
||||
|
||||
Verificar artigos recentes no WordPress via SSH (server):
|
||||
|
||||
```bash
|
||||
cd /home/ealmeida/public_html && wp --allow-root post list --post_type=post --post_status=publish --category=noticias --date_query='{"after":"1 day ago"}' --fields=ID,post_title --format=csv 2>/dev/null
|
||||
```
|
||||
|
||||
Para CADA artigo recente, verificar:
|
||||
|
||||
**15a. Acentuacao** — procurar palavras sem acento:
|
||||
```bash
|
||||
cd /home/ealmeida/public_html && wp --allow-root post get POST_ID --field=post_content 2>/dev/null | sed 's/<[^>]*>//g' | grep -oP '\b\w+cao\b|\b\w+sao\b|\bnao\b|\b\w+vel\b' | head -20
|
||||
```
|
||||
Palavras terminadas em "cao", "sao" (sem til) ou "nao" indicam acentuacao em falta.
|
||||
|
||||
**15b. Links HTML sem aspas:**
|
||||
```bash
|
||||
cd /home/ealmeida/public_html && wp --allow-root post get POST_ID --field=post_content 2>/dev/null | grep -oP 'href=[^"'\''"][^ >]+' | head -10
|
||||
```
|
||||
Se output nao vazio = links sem aspas.
|
||||
|
||||
**15c. Titulo com maiusculas indevidas:**
|
||||
```bash
|
||||
cd /home/ealmeida/public_html && wp --allow-root post get POST_ID --field=post_title 2>/dev/null
|
||||
```
|
||||
Verificar: so primeira palavra e nomes proprios devem ter maiuscula.
|
||||
|
||||
- 0 problemas = OK
|
||||
- 1-3 = AVISO (artigos com defeitos cosmeticos)
|
||||
- 4+ = CRITICO (Copywriter nao esta a cumprir quality gates — actualizar AGENTS.md)
|
||||
|
||||
### Check 16: Cron assign-copywriter activo
|
||||
|
||||
```bash
|
||||
crontab -l | grep assign-copywriter
|
||||
```
|
||||
|
||||
- Presente = OK
|
||||
- Ausente = CRITICO (issues PUBLICAR NOTICIA nunca serao atribuidas ao Copywriter)
|
||||
|
||||
### Check 17: Pipeline noticias end-to-end (ultimas 24h)
|
||||
|
||||
```sql
|
||||
-- Routines disparadas
|
||||
SELECT COUNT(*) as routines_fired FROM routine_triggers rt
|
||||
JOIN routines r ON rt.routine_id = r.id
|
||||
WHERE r.title LIKE 'Pesquisa diaria%noticias%'
|
||||
AND rt.last_fired_at > NOW() - INTERVAL '24 hours';
|
||||
|
||||
-- Issues de pesquisa concluidas
|
||||
SELECT COUNT(*) as pesquisas_done FROM issues
|
||||
WHERE company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
AND title LIKE 'Pesquisa diaria%'
|
||||
AND status = 'done'
|
||||
AND created_at > NOW() - INTERVAL '24 hours';
|
||||
|
||||
-- Issues PUBLICAR NOTICIA criadas e concluidas
|
||||
SELECT
|
||||
COUNT(*) as total,
|
||||
COUNT(*) FILTER (WHERE status = 'done') as done,
|
||||
COUNT(*) FILTER (WHERE status IN ('todo','in_progress')) as pendentes
|
||||
FROM issues
|
||||
WHERE company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
AND title LIKE 'PUBLICAR NOTICIA%'
|
||||
AND created_at > NOW() - INTERVAL '24 hours';
|
||||
```
|
||||
|
||||
- routines_fired >= 3 E pesquisas_done >= 1 E done >= 5 = OK
|
||||
- Qualquer zero = AVISO (pipeline com falhas)
|
||||
- routines_fired = 0 = CRITICO (routines nao disparam)
|
||||
|
||||
### Check 18: Issues blocked sem impedimento real (INC-07)
|
||||
|
||||
Invocar tool MCP: `mcp__paperclip__diag_false_blockers`
|
||||
|
||||
Para cada issue blocked, verificar se o assignee está em erro:
|
||||
```sql
|
||||
SELECT a.name, a.status FROM agents a
|
||||
WHERE a.id = (SELECT assignee_agent_id FROM issues WHERE identifier = '{{ID}}');
|
||||
```
|
||||
|
||||
- Issue `blocked` + assignee `active/idle/running` + sub-tasks activas > 0 = **FALSO BLOCKER** (deveria ser `in_progress`)
|
||||
- Issue `blocked` + assignee `error/paused` = blocker legítimo
|
||||
- Issue `blocked` + sem sub-tasks + sem comentário de impedimento = suspeito, alertar
|
||||
|
||||
Resultado:
|
||||
- 0 falsos blockers = OK
|
||||
- 1+ falsos blockers = AVISO (listar e recomendar correcção para `in_progress`)
|
||||
|
||||
---
|
||||
|
||||
### Check 19: Sessões com tokens excessivos (INC-12)
|
||||
|
||||
Previne "Prompt is too long" e token burn massivo. Monitoriza via usage_json dos heartbeats recentes.
|
||||
|
||||
Top agentes com maior consumo de tokens nas últimas 24h:
|
||||
|
||||
Invocar tool MCP: `mcp__paperclip__diag_heartbeat_token_usage(hours=24)`
|
||||
|
||||
Contagem de erros "Prompt is too long" activos (sinal imediato):
|
||||
|
||||
Invocar tool MCP: `mcp__paperclip__diag_prompt_too_long_errors(hours=24)`
|
||||
|
||||
- 0 erros = OK
|
||||
- 1+ erros = CRITICO (forçar rotação de sessão)
|
||||
|
||||
**Forçar rotação de sessão (apaga histórico, fresh start):**
|
||||
```sql
|
||||
DELETE FROM agent_task_sessions
|
||||
WHERE agent_id = '{{AGENT_ID}}'
|
||||
AND company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Check 20: Routines presas in_progress >4h (INC-08)
|
||||
|
||||
Issues de routine que ficam `in_progress` sem actividade bloqueiam todos os futuros disparos da mesma routine (constraint `issues_open_routine_execution_uq`). A ligação issue→routine está em `routine_runs.linked_issue_id` (não existe `issues.routine_id`).
|
||||
|
||||
Invocar tool MCP: `mcp__paperclip__diag_stuck_routines(hours=4)`
|
||||
|
||||
- 0 = OK
|
||||
- 1+ = CRITICO (routine bloqueada — cancelar a issue presa para desbloquear futuros disparos)
|
||||
|
||||
**Desbloquear routine (cancelar issue presa):**
|
||||
```sql
|
||||
UPDATE issues SET status = 'cancelled', updated_at = NOW()
|
||||
WHERE identifier = '{{IDENTIFIER}}'
|
||||
AND company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Check 21: Zombie parents — issues pai com sub-tasks todas concluídas (AP-09)
|
||||
|
||||
Issues pai que ficam abertas indefinidamente quando todas as sub-tasks estão `done` ou `cancelled`. Poluem o dashboard e impedem distinguir trabalho parado de trabalho já feito.
|
||||
|
||||
Invocar tool MCP: `mcp__paperclip__diag_zombie_parents`
|
||||
|
||||
- 0 = OK
|
||||
- 1+ = AVISO (fechar manualmente ou alertar CEO para verificar e fechar)
|
||||
|
||||
---
|
||||
|
||||
## Formato de output
|
||||
|
||||
```
|
||||
## Clip Health — [data/hora]
|
||||
|
||||
| Check | Estado | Detalhe |
|
||||
|-------|--------|---------|
|
||||
| Servico | OK/CRITICO | N processos activos / stopped |
|
||||
| POSIX shared memory | OK/AVISO/CRITICO | /dev/shm/PostgreSQL.* presente / em falta |
|
||||
| Base de dados | OK/CRITICO | Xmb, N agentes, N issues |
|
||||
| Heartbeats 24h | OK/AVISO/CRITICO | N falhas |
|
||||
| Budget | OK/AVISO/CRITICO | [agente] a X% |
|
||||
| Disco | OK/AVISO/CRITICO | X% livre (Xgb) |
|
||||
| API Health | OK/AVISO/CRITICO | status:ok / board access / timeout |
|
||||
| Gateway | OK/AVISO | responde / timeout |
|
||||
| Refs orfas | OK/CRITICO | N budget_policies orfas |
|
||||
| Instancias | OK/CRITICO | N processos na porta 310x |
|
||||
| Porta/Tunnel | OK/CRITICO | tunnel:X servidor:Y |
|
||||
| Permissoes bash | OK/CRITICO | N agentes sem dangerouslySkipPermissions |
|
||||
| Heartbeats config | OK/AVISO | N agentes sem heartbeat enabled |
|
||||
| Routine triggers | OK/CRITICO | N triggers com kind errado ou next_run_at NULL |
|
||||
| Auto-assign noticias | OK/AVISO | N issues PUBLICAR NOTICIA sem assignee |
|
||||
| Qualidade artigos | OK/AVISO/CRITICO | N artigos com acentos/links/titulos errados |
|
||||
| Cron assign-copywriter | OK/CRITICO | activo / ausente |
|
||||
| Pipeline noticias 24h | OK/AVISO/CRITICO | N routines, N pesquisas, N publicadas |
|
||||
| Falsos blockers | OK/AVISO | N issues blocked sem impedimento real |
|
||||
| Tokens excessivos | OK/AVISO/CRITICO | N agentes >500K tokens cached |
|
||||
| Routines presas | OK/CRITICO | N issues rotina paradas >4h |
|
||||
| Zombie parents | OK/AVISO | N issues pai com todas as subs concluídas |
|
||||
|
||||
**Score:** N/22 OK
|
||||
|
||||
### Detalhes (se existirem alertas)
|
||||
[listar heartbeats falhados, budget alto, artigos com defeitos, pipeline parado]
|
||||
|
||||
### Accoes recomendadas
|
||||
[listar accoes concretas para resolver alertas]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Healing Log
|
||||
|
||||
Registo de erros conhecidos e como evitá-los. Lido automaticamente antes de executar.
|
||||
|
||||
```jsonl
|
||||
{"date":"2026-04-07","issue":"Check 1 usava systemctl que nunca existe (Paperclip corre via pnpm dev)","fix":"Substituir por ps aux | grep paperclip + check POSIX shared memory /dev/shm/PostgreSQL.*","source":"auto"}
|
||||
{"date":"2026-04-07","issue":"Check 6 usava npx paperclipai doctor que não existe no projecto local","fix":"Substituir por curl localhost:3100/health — distingue: ok / board access (BD operacional) / timeout (servidor morto)","source":"auto"}
|
||||
```
|
||||
|
||||
*Adicionar nova linha após cada erro corrigido.*
|
||||
@@ -0,0 +1,184 @@
|
||||
---
|
||||
name: clip-instructions
|
||||
description: Editar e gerir AGENTS.md de agentes Paperclip — ver, editar, rever histórico de versões. O AGENTS.md é o "cérebro" do agente. Usar quando "clip instructions", "editar agente", "atualizar AGENTS.md", "mudar comportamento agente", "instruções agente".
|
||||
context: fork
|
||||
version: "1.0.0"
|
||||
created: 2026-04-07
|
||||
---
|
||||
|
||||
# /clip-instructions — Gerir AGENTS.md dos Agentes
|
||||
|
||||
O `AGENTS.md` é o ficheiro de instruções que define identidade, missão, comportamento e regras de cada agente. É injectado a cada heartbeat — editar o ficheiro tem efeito imediato no próximo run.
|
||||
|
||||
Aceita argumento: nome do agente (ex: `/clip-instructions CTO`).
|
||||
|
||||
## Constantes
|
||||
|
||||
```
|
||||
BD: PGPASSWORD="paperclip" psql -h localhost -p 54329 -U paperclip -d paperclip
|
||||
COMPANY_ID: ebe10308-efd7-453f-86ab-13e6fe84004f
|
||||
```
|
||||
|
||||
## Passo 1: Encontrar agente e localizar AGENTS.md
|
||||
|
||||
```sql
|
||||
SELECT id, name, role, title,
|
||||
adapter_config->>'instructionsFilePath' as instructions_path,
|
||||
adapter_config->>'instructionsRootPath' as instructions_root
|
||||
FROM agents
|
||||
WHERE company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
AND LOWER(name) LIKE LOWER('%{{NOME}}%');
|
||||
```
|
||||
|
||||
Se `instructions_path` é NULL ou não começa com `/` → CRITICO (AGENTS.md não está a ser carregado).
|
||||
|
||||
## Passo 2: Ler ficheiro actual
|
||||
|
||||
```bash
|
||||
cat "{{instructions_path}}"
|
||||
```
|
||||
|
||||
Apresentar conteúdo completo. Identificar secções:
|
||||
- `## Identidade` — nome, papel, tipo, modelo, budget
|
||||
- `## Missão` — objectivo principal
|
||||
- `## Comportamento` — regras de actuação
|
||||
- `## Skills` / `## MCPs` — ferramentas
|
||||
- `## Heartbeat` — intervalo, checklist
|
||||
- `## Equipa` — quem reporta a quem
|
||||
|
||||
## Passo 3: Modo de actuação
|
||||
|
||||
### Ver (sem edições pedidas)
|
||||
Apresentar resumo estruturado das secções principais.
|
||||
|
||||
### Editar (utilizador pede alteração específica)
|
||||
|
||||
1. Ler ficheiro completo com Read
|
||||
2. Identificar a secção a alterar
|
||||
3. Propor a alteração ao utilizador ("Vou mudar X para Y — confirmas?")
|
||||
4. Executar com Edit após confirmação
|
||||
5. Verificar que o ficheiro ficou correcto
|
||||
|
||||
**Regras de edição segura:**
|
||||
- Nunca reescrever o ficheiro completo — usar Edit para alterações cirúrgicas
|
||||
- Preservar frontmatter YAML se existir
|
||||
- Manter estrutura de secções existente
|
||||
- Após editar, mostrar diff resumido das alterações
|
||||
|
||||
### Criar novo AGENTS.md (agente sem instruções)
|
||||
|
||||
Se `instructions_path` está definido mas o ficheiro não existe:
|
||||
|
||||
```bash
|
||||
ls "{{instructions_path}}" 2>/dev/null || echo "NAO_EXISTE"
|
||||
```
|
||||
|
||||
Criar directório se necessário:
|
||||
```bash
|
||||
mkdir -p "{{instructions_root}}"
|
||||
```
|
||||
|
||||
Template mínimo para novo AGENTS.md:
|
||||
```markdown
|
||||
# {{NOME}}
|
||||
|
||||
## Identidade
|
||||
|
||||
- **Nome:** {{NOME}}
|
||||
- **Papel:** {{ROLE}} — reporta ao {{SUPERVISOR}}
|
||||
- **Tipo:** {{TIPO}} (executor/analyst/manager)
|
||||
- **Modelo:** {{MODELO}}
|
||||
- **Budget:** {{BUDGET}} cents/mês
|
||||
|
||||
## Missão
|
||||
|
||||
{{DESCRICAO_MISSAO}}
|
||||
|
||||
## Comportamento
|
||||
|
||||
### Regras
|
||||
|
||||
- **Foco** — executar apenas tarefas dentro do scope definido
|
||||
- **Escalação** — problemas fora de scope → reportar ao supervisor
|
||||
- **PT-PT** — sempre com acentuação correcta
|
||||
|
||||
## Heartbeat
|
||||
|
||||
- **Intervalo:** {{INTERVALO}}s
|
||||
- **Checklist:** ver issues atribuídas, executar, reportar
|
||||
|
||||
## Equipa
|
||||
|
||||
- Reporta ao: {{SUPERVISOR}}
|
||||
```
|
||||
|
||||
## Passo 4: Verificar após edição
|
||||
|
||||
Confirmar que o Paperclip reconhece o ficheiro actualizado:
|
||||
```bash
|
||||
curl -s "http://localhost:3100/api/agents/{{AGENT_ID}}/instructions-bundle" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" | python3 -c "import sys,json; d=json.load(sys.stdin); print('OK' if d.get('content') else 'VAZIO')"
|
||||
```
|
||||
|
||||
- OK = AGENTS.md carregado pelo servidor
|
||||
- VAZIO / erro = path errado ou ficheiro não encontrado
|
||||
|
||||
## Casos especiais
|
||||
|
||||
### Actualizar model no AGENTS.md
|
||||
|
||||
Se o agente mudou de modelo (ex: gemini → claude), actualizar tanto o AGENTS.md como o `adapter_config`:
|
||||
```sql
|
||||
UPDATE agents
|
||||
SET adapter_config = jsonb_set(adapter_config, '{model}', '"{{NOVO_MODELO}}"'::jsonb)
|
||||
WHERE id = '{{AGENT_ID}}'
|
||||
AND company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f';
|
||||
```
|
||||
|
||||
### Rever historial de versões (config_revisions)
|
||||
|
||||
```sql
|
||||
SELECT cr.id, cr.created_at::timestamp(0), a.name as criado_por
|
||||
FROM agent_config_revisions cr
|
||||
LEFT JOIN agents a ON cr.created_by_agent_id = a.id
|
||||
WHERE cr.agent_id = '{{AGENT_ID}}'
|
||||
ORDER BY cr.created_at DESC
|
||||
LIMIT 10;
|
||||
```
|
||||
|
||||
### Rollback via API
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3100/api/agents/{{AGENT_ID}}/config-revisions/{{REVISION_ID}}/rollback" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Formato de output
|
||||
|
||||
```
|
||||
## Instruções — {{NOME}} ({{role}})
|
||||
|
||||
**Ficheiro:** {{instructions_path}}
|
||||
**Estado:** carregado / não encontrado / path inválido
|
||||
|
||||
### Resumo actual
|
||||
**Missão:** [1 linha]
|
||||
**Modelo:** {{model}} | **Budget:** {{budget}} cents | **Heartbeat:** {{intervalo}}s
|
||||
**Regras principais:** [lista]
|
||||
**Skills:** [lista ou "nenhuma"]
|
||||
|
||||
### Alterações efectuadas (se editou)
|
||||
[diff resumido]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Healing Log
|
||||
|
||||
```jsonl
|
||||
{"date":"2026-04-07","issue":"instructionsFilePath relativo — AGENTS.md não carregava após criação via SQL","fix":"Path deve ser sempre absoluto. Verificar com query adapter_config->>'instructionsFilePath' NOT LIKE '/%'","source":"auto"}
|
||||
```
|
||||
@@ -0,0 +1,200 @@
|
||||
---
|
||||
name: clip-issue
|
||||
description: Criar e gerir issues Paperclip — lançar objectivos ao CEO, ver progresso, comentar. Usar quando "clip issue", "criar issue", "lançar objectivo", "ver issues clip". Issues seguem cadeia de delegação hierárquica.
|
||||
context: fork
|
||||
---
|
||||
|
||||
# /clip-issue — Gerir Issues Paperclip
|
||||
|
||||
Modos: lista (sem args), criar (com titulo), ver (com ID).
|
||||
|
||||
## Constantes
|
||||
|
||||
```
|
||||
BD: PGPASSWORD="paperclip" psql -h localhost -p 54329 -U paperclip -d paperclip
|
||||
COMPANY_ID: ebe10308-efd7-453f-86ab-13e6fe84004f
|
||||
API: http://localhost:3100/api
|
||||
```
|
||||
|
||||
## Modo lista (sem argumentos)
|
||||
|
||||
```sql
|
||||
SELECT i.id, i.title, i.status, i.priority, a.name as assignee, i.created_at
|
||||
FROM issues i LEFT JOIN agents a ON i.assignee_agent_id = a.id
|
||||
WHERE i.company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
AND i.status NOT IN ('done','cancelled')
|
||||
ORDER BY
|
||||
CASE i.priority WHEN 'critical' THEN 1 WHEN 'high' THEN 2 WHEN 'medium' THEN 3 ELSE 4 END,
|
||||
i.status, i.created_at DESC;
|
||||
```
|
||||
|
||||
Para ver concluidas tambem:
|
||||
```sql
|
||||
-- Adicionar: AND i.status IN ('done') AND i.updated_at > NOW() - INTERVAL '7 days'
|
||||
```
|
||||
|
||||
## Modo criar (com titulo)
|
||||
|
||||
### Passo 1: Obter CEO ID
|
||||
|
||||
```sql
|
||||
SELECT id FROM agents
|
||||
WHERE company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
AND name = 'CEO';
|
||||
```
|
||||
|
||||
### Passo 2: Criar issue
|
||||
|
||||
Preferir API (quando JWT funciona):
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3100/api/companies/ebe10308-efd7-453f-86ab-13e6fe84004f/issues" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"title": "{{TITULO}}",
|
||||
"description": "{{DESCRICAO}}",
|
||||
"priority": "{{PRIORIDADE}}",
|
||||
"assigneeAgentId": "{{CEO_ID}}"
|
||||
}'
|
||||
```
|
||||
|
||||
Fallback via BD (OBRIGATORIO incluir identifier e issue_number):
|
||||
```sql
|
||||
-- Passo 2a: Obter proximo numero
|
||||
SELECT issue_counter FROM companies WHERE id = 'ebe10308-efd7-453f-86ab-13e6fe84004f';
|
||||
-- Guardar o valor como NEXT_NUM
|
||||
|
||||
-- Passo 2b: Criar issue COM identifier
|
||||
INSERT INTO issues (id, company_id, title, description, priority, assignee_agent_id, status, identifier, issue_number, created_at, updated_at)
|
||||
VALUES (
|
||||
gen_random_uuid(),
|
||||
'ebe10308-efd7-453f-86ab-13e6fe84004f',
|
||||
'{{TITULO}}',
|
||||
'{{DESCRICAO}}',
|
||||
'{{PRIORIDADE}}',
|
||||
'{{CEO_ID}}',
|
||||
'todo',
|
||||
'DES-{{NEXT_NUM}}',
|
||||
{{NEXT_NUM}},
|
||||
NOW(), NOW()
|
||||
)
|
||||
RETURNING id, identifier, title, status;
|
||||
|
||||
-- Passo 2c: Incrementar counter (CRITICO — nunca esquecer)
|
||||
UPDATE companies SET issue_counter = {{NEXT_NUM}} + 1 WHERE id = 'ebe10308-efd7-453f-86ab-13e6fe84004f';
|
||||
```
|
||||
|
||||
**NUNCA criar issues via SQL sem identifier e issue_number.** Causa bug de duplicate key que bloqueia toda a criacao de issues no Paperclip.
|
||||
|
||||
Confirmar titulo e prioridade com o utilizador antes de criar. Prioridades: critical, high, medium, low.
|
||||
|
||||
### Nota sobre delegação hierárquica
|
||||
|
||||
Issues criadas pelo Board (Emanuel) são sempre atribuídas ao CEO. O CEO delega pela cadeia:
|
||||
- CEO cria sub-issue ao C-Level adequado
|
||||
- C-Level cria sub-issue ao Director
|
||||
- Director atribui ao especialista
|
||||
|
||||
Quando routines disparam, geram issues ao CEO que segue o mesmo fluxo. A cadeia está na descrição da routine (campo `description` começa com `CADEIA: CEO → ...`).
|
||||
|
||||
## Semântica de estados (referência rápida)
|
||||
|
||||
Ao criar, alterar ou interpretar estados de issues:
|
||||
|
||||
- **`todo`** — não iniciada, aguarda pickup
|
||||
- **`in_progress`** — trabalho em curso, **incluindo aguardar sub-tasks delegadas**
|
||||
- **`blocked`** — APENAS impedimento real (agente em erro, falta de permissão, dependência externa, aguarda decisão humana)
|
||||
- **`done`** — concluída com resultado verificado
|
||||
- **`cancelled`** — abandonada por decisão superior
|
||||
|
||||
**Regra INC-07:** `blocked` ≠ "delegué e estou à espera". Aguardar sub-task activa = `in_progress`. Se ao listar issues vires `blocked` sem impedimento real, alertar o utilizador.
|
||||
|
||||
## Modo ver (com ID ou titulo parcial)
|
||||
|
||||
```sql
|
||||
SELECT i.*, a.name as assignee
|
||||
FROM issues i LEFT JOIN agents a ON i.assignee_agent_id = a.id
|
||||
WHERE i.company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
AND (i.id::text LIKE '%{{ARG}}%' OR LOWER(i.title) LIKE LOWER('%{{ARG}}%'));
|
||||
```
|
||||
|
||||
Comentarios:
|
||||
```sql
|
||||
SELECT ic.body, ic.created_at, a.name as author
|
||||
FROM issue_comments ic
|
||||
LEFT JOIN agents a ON ic.author_agent_id = a.id
|
||||
WHERE ic.issue_id = '{{ISSUE_ID}}'
|
||||
ORDER BY ic.created_at ASC;
|
||||
```
|
||||
|
||||
## Modo comentar
|
||||
|
||||
```sql
|
||||
INSERT INTO issue_comments (id, company_id, issue_id, author_user_id, body, created_at, updated_at)
|
||||
VALUES (gen_random_uuid(), 'ebe10308-efd7-453f-86ab-13e6fe84004f', '{{ISSUE_ID}}', 'v1N5OccPn9DGq6iog7qW9nEvnXYFT3iO', '{{COMENTARIO}}', NOW(), NOW())
|
||||
RETURNING id;
|
||||
```
|
||||
|
||||
Nota: `author_user_id = 'v1N5OccPn9DGq6iog7qW9nEvnXYFT3iO'` (Emanuel) → comentário aparece como Board/humano no dashboard. Nunca usar `'board'` — não é um user_id válido.
|
||||
|
||||
## Modo checkout / release
|
||||
|
||||
Checkout reserva a issue para trabalho activo (sinaliza ao Paperclip que está em curso).
|
||||
Release liberta a issue de volta a `todo`.
|
||||
|
||||
**Checkout via API:**
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3100/api/issues/{{ISSUE_ID}}/checkout" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"agentId": "{{AGENT_ID}}"}'
|
||||
```
|
||||
|
||||
**Release via API:**
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3100/api/issues/{{ISSUE_ID}}/release" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
**Fallback via BD (se API falhar):**
|
||||
```sql
|
||||
-- Checkout manual
|
||||
UPDATE issues SET status = 'in_progress', updated_at = NOW()
|
||||
WHERE id = '{{ISSUE_ID}}'
|
||||
AND company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
RETURNING identifier, title, status;
|
||||
|
||||
-- Release manual
|
||||
UPDATE issues SET status = 'todo', updated_at = NOW()
|
||||
WHERE id = '{{ISSUE_ID}}'
|
||||
AND company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
RETURNING identifier, title, status;
|
||||
```
|
||||
|
||||
**Nota:** O par checkout/release é importante para o pipeline do Paperclip — evita que dois agentes peguem na mesma issue em simultâneo.
|
||||
|
||||
## Formato de output
|
||||
|
||||
Adaptar ao modo. Para lista:
|
||||
```
|
||||
## Issues Clip — [data]
|
||||
|
||||
| # | Titulo | Status | Prioridade | Assignee |
|
||||
...
|
||||
|
||||
Total: N abertas (N critical, N high, N medium, N low)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Healing Log
|
||||
|
||||
Registo de erros conhecidos e como evitá-los. Lido automaticamente antes de executar.
|
||||
|
||||
```jsonl
|
||||
{"date":"2026-04-07","issue":"author_user_id = 'board' em comentários — não é user_id válido, comentário não aparecia no dashboard","fix":"Usar 'v1N5OccPn9DGq6iog7qW9nEvnXYFT3iO' (ID real de Emanuel no Paperclip)","source":"auto"}
|
||||
```
|
||||
|
||||
*Adicionar nova linha após cada erro corrigido.*
|
||||
@@ -0,0 +1,148 @@
|
||||
---
|
||||
name: clip-org
|
||||
description: Org chart Paperclip — hierarquia completa, reports, gaps por preencher. Usar quando "clip org", "organigrama", "hierarquia clip", "quem reporta a quem". Inclui modelo de governance e cadeias de delegação.
|
||||
context: fork
|
||||
---
|
||||
|
||||
# /clip-org — Org Chart Paperclip
|
||||
|
||||
Hierarquia completa da empresa Descomplicar no Paperclip.
|
||||
|
||||
## Constantes
|
||||
|
||||
```
|
||||
BD: PGPASSWORD="paperclip" psql -h localhost -p 54329 -U paperclip -d paperclip
|
||||
COMPANY_ID: ebe10308-efd7-453f-86ab-13e6fe84004f
|
||||
```
|
||||
|
||||
## Procedimento
|
||||
|
||||
### Passo 1: Obter hierarquia completa
|
||||
|
||||
Invocar tool MCP: `mcp__paperclip__diag_agent_hierarchy`
|
||||
|
||||
### Passo 2: Construir arvore visual
|
||||
|
||||
Apresentar como arvore identada:
|
||||
|
||||
```
|
||||
Emanuel (Board)
|
||||
└── CEO (running)
|
||||
├── COO (idle)
|
||||
│ ├── [Dir. Suporte] — nao existe
|
||||
│ └── Ticket Triage (idle)
|
||||
├── CFO (idle)
|
||||
│ ├── [Dir. Financeiro] — nao existe
|
||||
│ ├── Finance Manager (idle)
|
||||
│ └── Compliance Auditor (idle)
|
||||
├── CTO (idle)
|
||||
│ ├── [Dir. Infraestrutura] — nao existe
|
||||
│ │ ├── CWP Server Manager (idle)
|
||||
│ │ ├── EasyPanel Specialist (idle)
|
||||
│ │ ├── Backup Specialist (idle)
|
||||
│ │ └── Security Specialist (idle)
|
||||
│ ├── [Dir. Desenvolvimento] — nao existe
|
||||
│ │ └── ...
|
||||
│ └── ...
|
||||
└── ...
|
||||
```
|
||||
|
||||
Nota: Directores de seccao (~16) podem ainda nao existir. Mostrar como `[Nome] — nao existe` para evidenciar gaps.
|
||||
|
||||
### Passo 3: Identificar gaps
|
||||
|
||||
```sql
|
||||
-- Agentes orfaos (sem reports_to, excepto CEO)
|
||||
SELECT name, role, status FROM agents
|
||||
WHERE company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
AND reports_to IS NULL
|
||||
AND role != 'ceo';
|
||||
```
|
||||
|
||||
```sql
|
||||
-- C-Level sem subordinados directos
|
||||
SELECT c.name, c.role,
|
||||
(SELECT COUNT(*) FROM agents sub WHERE sub.reports_to = c.id) as subordinados
|
||||
FROM agents c
|
||||
WHERE c.company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
AND c.role IN ('coo','cfo','cto','cmo','cro','cgo','cdo')
|
||||
ORDER BY subordinados ASC;
|
||||
```
|
||||
|
||||
### Passo 4: Estatisticas
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
role,
|
||||
COUNT(*) as total,
|
||||
COUNT(CASE WHEN status = 'running' THEN 1 END) as running,
|
||||
COUNT(CASE WHEN status = 'idle' THEN 1 END) as idle,
|
||||
COUNT(CASE WHEN status = 'paused' THEN 1 END) as paused
|
||||
FROM agents
|
||||
WHERE company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
GROUP BY role
|
||||
ORDER BY CASE role
|
||||
WHEN 'ceo' THEN 1 WHEN 'coo' THEN 2 WHEN 'cfo' THEN 3
|
||||
WHEN 'manager' THEN 4 WHEN 'engineer' THEN 5 ELSE 6 END;
|
||||
```
|
||||
|
||||
## Formato de output
|
||||
|
||||
```
|
||||
## Org Chart Clip — [data]
|
||||
|
||||
[arvore visual]
|
||||
|
||||
### Estatisticas
|
||||
| Camada | Total | Running | Idle | Paused |
|
||||
...
|
||||
|
||||
### Gaps
|
||||
- Directores em falta: [lista das 16 TFs sem Director]
|
||||
- Agentes orfaos: [lista]
|
||||
- C-Level sem subordinados: [lista]
|
||||
|
||||
### Cobertura
|
||||
Directores: N/16 (N%)
|
||||
TaskForces cobertas: [lista]
|
||||
```
|
||||
|
||||
## Referência de TaskForces esperadas
|
||||
|
||||
| TF | Director esperado | Reporta a |
|
||||
|----|-------------------|-----------|
|
||||
| TF-01 Estratégia | Dir. Estratégia | CGO |
|
||||
| TF-02 Operações | Dir. Operações | COO |
|
||||
| TF-03 Financeiro | Dir. Financeiro | CFO |
|
||||
| TF-04 Infraestrutura | Dir. Infraestrutura | CTO |
|
||||
| TF-05 Desenvolvimento | Dir. Desenvolvimento | CTO |
|
||||
| TF-06 Automação | Dir. Automação | CTO |
|
||||
| TF-07 IA | Dir. IA | CGO |
|
||||
| TF-08 Design | Dir. Design | CMO |
|
||||
| TF-09 Web | Dir. Web | CMO |
|
||||
| TF-10 Vídeo | Dir. Vídeo | CMO |
|
||||
| TF-11 SEO | Dir. SEO | CMO |
|
||||
| TF-12 Conteúdo | Dir. Conteúdo | CMO |
|
||||
| TF-13 Social | Dir. Social | CMO |
|
||||
| TF-14 Publicidade | Dir. Publicidade | CMO |
|
||||
| TF-15 Comercial | Dir. Comercial | CRO |
|
||||
| TF-16 Suporte | Dir. Suporte | COO |
|
||||
|
||||
## Modelo de governance
|
||||
|
||||
Todas as routines são atribuídas ao CEO que delega pela cadeia hierárquica:
|
||||
`Routine → CEO → C-Level → Director → Especialista`
|
||||
|
||||
Cada nível adiciona contexto antes de delegar e sintetiza resultados antes de reportar acima. Ver `/clip-routine` para detalhes.
|
||||
|
||||
---
|
||||
|
||||
## Healing Log
|
||||
|
||||
Registo de erros conhecidos e como evitá-los. Lido automaticamente antes de executar.
|
||||
|
||||
```jsonl
|
||||
{"date":"","issue":"","fix":"","source":"user|auto"}
|
||||
```
|
||||
|
||||
*Adicionar nova linha após cada erro corrigido.*
|
||||
@@ -0,0 +1,349 @@
|
||||
---
|
||||
name: clip-routine
|
||||
description: Gerir routines Paperclip — listar crons activos, ver execuções, criar/editar routines. Usar quando "clip routine", "routines clip", "crons paperclip", "automações clip".
|
||||
context: fork
|
||||
---
|
||||
|
||||
# /clip-routine — Gerir Routines Paperclip
|
||||
|
||||
## Modelo de governance (desde 29-03-2026)
|
||||
|
||||
**Todas as routines são atribuídas ao CEO.** O CEO delega pela cadeia hierárquica:
|
||||
|
||||
```
|
||||
Routine (cron trigger) → CEO avalia e delega
|
||||
→ C-Level adiciona contexto departamental
|
||||
→ Director coordena e atribui
|
||||
→ Especialista executa
|
||||
→ Resultado sobe a cadeia com síntese progressiva
|
||||
```
|
||||
|
||||
Cada routine tem no campo `description` a cadeia de delegação (ex: `CADEIA: CEO → CTO → Dir. Infraestrutura → Backup Specialist`).
|
||||
|
||||
**Regras:**
|
||||
- Novas routines devem ser SEMPRE atribuídas ao CEO
|
||||
- A cadeia de delegação deve estar explícita na descrição
|
||||
- Nunca atribuir routines directamente a especialistas — quebra a visibilidade hierárquica
|
||||
|
||||
## Dois tipos de periodicidade
|
||||
|
||||
1. **Heartbeats** — intervalos configurados via `runtime_config` dos agentes (quando acordam)
|
||||
2. **Routines** — tarefas periódicas via tabela `routines` + `routine_triggers` com cron (o que fazem ao acordar)
|
||||
|
||||
## Constantes
|
||||
|
||||
```
|
||||
BD: PGPASSWORD="paperclip" psql -h localhost -p 54329 -U paperclip -d paperclip
|
||||
COMPANY_ID: ebe10308-efd7-453f-86ab-13e6fe84004f
|
||||
```
|
||||
|
||||
## Tiers de heartbeat
|
||||
|
||||
| Tier | Intervalo | Uso |
|
||||
|------|-----------|-----|
|
||||
| 1 | 1h (3600s) | CEO, agentes criticos |
|
||||
| 2 | 2h (7200s) | C-Level activos |
|
||||
| 3 | 4h (14400s) | Directores |
|
||||
| 4 | 6h (21600s) | Especialistas com rotina |
|
||||
| 5 | on-demand | Especialistas reactivos (sem timer) |
|
||||
|
||||
## Modo lista (sem argumentos)
|
||||
|
||||
Listar agentes com heartbeat configurado:
|
||||
|
||||
```sql
|
||||
SELECT name, role, status,
|
||||
runtime_config->'heartbeat'->>'enabled' as hb_enabled,
|
||||
runtime_config->'heartbeat'->>'intervalSec' as hb_interval,
|
||||
last_heartbeat_at
|
||||
FROM agents
|
||||
WHERE company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
AND runtime_config::text != '{}'
|
||||
AND runtime_config->'heartbeat' IS NOT NULL
|
||||
ORDER BY (runtime_config->'heartbeat'->>'intervalSec')::int ASC NULLS LAST;
|
||||
```
|
||||
|
||||
Complementar com contagem de execucoes recentes:
|
||||
|
||||
```sql
|
||||
SELECT a.name,
|
||||
COUNT(CASE WHEN hr.status = 'succeeded' THEN 1 END) as ok_24h,
|
||||
COUNT(CASE WHEN hr.status = 'failed' THEN 1 END) as fail_24h
|
||||
FROM agents a
|
||||
LEFT JOIN heartbeat_runs hr ON hr.agent_id = a.id
|
||||
AND hr.started_at > NOW() - INTERVAL '24 hours'
|
||||
WHERE a.company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
AND a.runtime_config->'heartbeat' IS NOT NULL
|
||||
GROUP BY a.name
|
||||
ORDER BY a.name;
|
||||
```
|
||||
|
||||
## Modo ver (com nome de agente)
|
||||
|
||||
Ultimas 10 execucoes de um agente:
|
||||
|
||||
```sql
|
||||
SELECT hr.status, hr.started_at, hr.finished_at, LEFT(hr.error, 80) as erro
|
||||
FROM heartbeat_runs hr
|
||||
JOIN agents a ON hr.agent_id = a.id
|
||||
WHERE a.company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
AND LOWER(a.name) LIKE LOWER('%{{NOME}}%')
|
||||
ORDER BY hr.started_at DESC LIMIT 10;
|
||||
```
|
||||
|
||||
## Modo criar/editar (configurar heartbeat)
|
||||
|
||||
### Activar heartbeat
|
||||
|
||||
```sql
|
||||
UPDATE agents SET runtime_config = jsonb_set(
|
||||
COALESCE(runtime_config, '{}'::jsonb),
|
||||
'{heartbeat}',
|
||||
'{"enabled": true, "intervalSec": {{INTERVALO}}, "cooldownSec": 10, "wakeOnDemand": true, "maxConcurrentRuns": 1}'::jsonb
|
||||
)
|
||||
WHERE name = '{{NOME}}'
|
||||
AND company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
RETURNING name, runtime_config;
|
||||
```
|
||||
|
||||
Confirmar sempre com o utilizador:
|
||||
- Nome do agente
|
||||
- Intervalo (sugerir tier adequado ao role)
|
||||
- wakeOnDemand (true para a maioria)
|
||||
|
||||
### Desactivar heartbeat
|
||||
|
||||
```sql
|
||||
UPDATE agents SET runtime_config = jsonb_set(
|
||||
runtime_config,
|
||||
'{heartbeat,enabled}',
|
||||
'false'::jsonb
|
||||
)
|
||||
WHERE name = '{{NOME}}'
|
||||
AND company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
RETURNING name, runtime_config;
|
||||
```
|
||||
|
||||
### Alterar intervalo
|
||||
|
||||
```sql
|
||||
UPDATE agents SET runtime_config = jsonb_set(
|
||||
runtime_config,
|
||||
'{heartbeat,intervalSec}',
|
||||
'{{INTERVALO}}'::jsonb
|
||||
)
|
||||
WHERE name = '{{NOME}}'
|
||||
AND company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
RETURNING name, runtime_config;
|
||||
```
|
||||
|
||||
## Routines do projecto (tabela routines)
|
||||
|
||||
Alem dos heartbeats por agente, existem routines organizadas por projecto com cron triggers.
|
||||
|
||||
**Estado actual:** 5 routines activas no Paperclip. 9 routines anteriores foram migradas para workflows n8n em https://automator.descomplicar.pt.
|
||||
|
||||
**5 routines Paperclip:**
|
||||
- Auditoria processos e compliance (0 10 * * *)
|
||||
- Execução tarefas AikTop (*/30 * * * *)
|
||||
- Reconciliação financeira diária (0 8 * * *)
|
||||
- Varredura inteligência competitiva (0 11 * * *)
|
||||
- Monitorização workflows n8n (0 10,18 * * *) — atribuída ao COO
|
||||
|
||||
### Listar routines activas
|
||||
|
||||
```sql
|
||||
SELECT r.title, r.status, r.priority, a.name as assignee,
|
||||
rt.cron_expression, rt.enabled, rt.next_run_at
|
||||
FROM routines r
|
||||
LEFT JOIN agents a ON r.assignee_agent_id = a.id
|
||||
LEFT JOIN routine_triggers rt ON rt.routine_id = r.id
|
||||
WHERE r.company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
ORDER BY r.title;
|
||||
```
|
||||
|
||||
### Ver execucoes de uma routine
|
||||
|
||||
```sql
|
||||
SELECT rr.status, rr.triggered_at, rr.completed_at, LEFT(rr.error, 80) as erro
|
||||
FROM routine_runs rr
|
||||
JOIN routines r ON rr.routine_id = r.id
|
||||
WHERE r.company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
AND LOWER(r.title) LIKE LOWER('%{{TITULO}}%')
|
||||
ORDER BY rr.triggered_at DESC LIMIT 10;
|
||||
```
|
||||
|
||||
## Modo criar routine (nova)
|
||||
|
||||
### Passo 1: Obter CEO ID
|
||||
|
||||
```sql
|
||||
SELECT id FROM agents
|
||||
WHERE name = 'CEO' AND company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f';
|
||||
```
|
||||
|
||||
### Passo 2: Obter projecto
|
||||
|
||||
```sql
|
||||
SELECT id, name FROM projects
|
||||
WHERE company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f';
|
||||
```
|
||||
|
||||
### Passo 3: Criar routine (SEMPRE atribuída ao CEO)
|
||||
|
||||
```sql
|
||||
INSERT INTO routines (id, company_id, project_id, title, description, assignee_agent_id, priority, status, created_at, updated_at)
|
||||
VALUES (
|
||||
gen_random_uuid(),
|
||||
'ebe10308-efd7-453f-86ab-13e6fe84004f',
|
||||
'{{PROJECT_ID}}',
|
||||
'{{TITULO}}',
|
||||
'CADEIA: CEO → {{C_LEVEL}} → {{DIRECTOR}} → {{ESPECIALISTA}}. {{DESCRICAO_TAREFA}}',
|
||||
'{{CEO_ID}}',
|
||||
'{{PRIORIDADE}}',
|
||||
'active',
|
||||
NOW(), NOW()
|
||||
)
|
||||
RETURNING id, title;
|
||||
```
|
||||
|
||||
### Passo 4: Criar cron trigger
|
||||
|
||||
**CRITICO:** O kind DEVE ser `'schedule'` (nao `'cron'`). O scheduler do Paperclip so processa triggers com `kind = 'schedule'`. Tambem e OBRIGATORIO popular `next_run_at` — sem ele o scheduler ignora o trigger.
|
||||
|
||||
```sql
|
||||
-- Calcular next_run_at antes de inserir
|
||||
-- Para crons diarios (ex: 0 8 * * *), usar:
|
||||
-- (CURRENT_DATE + 1) AT TIME ZONE 'Europe/Lisbon' + INTERVAL '{{HORA}} hours'
|
||||
-- Para crons recorrentes (ex: 0 */4 * * *), calcular proximo slot
|
||||
|
||||
INSERT INTO routine_triggers (id, company_id, routine_id, kind, label, enabled, cron_expression, timezone, next_run_at, created_at, updated_at)
|
||||
VALUES (
|
||||
gen_random_uuid(),
|
||||
'ebe10308-efd7-453f-86ab-13e6fe84004f',
|
||||
'{{ROUTINE_ID}}',
|
||||
'schedule',
|
||||
'{{LABEL}}',
|
||||
true,
|
||||
'{{CRON_EXPRESSION}}',
|
||||
'Europe/Lisbon',
|
||||
{{NEXT_RUN_AT}},
|
||||
NOW(), NOW()
|
||||
)
|
||||
RETURNING id, cron_expression, next_run_at;
|
||||
```
|
||||
|
||||
**Exemplos de next_run_at:**
|
||||
- `0 8 * * *` → `(CURRENT_DATE + 1) AT TIME ZONE 'Europe/Lisbon' + INTERVAL '8 hours'`
|
||||
- `0 */6 * * *` → `date_trunc('hour', NOW()) + INTERVAL '6 hours'`
|
||||
- `0 9 * * 1` → proxima segunda-feira as 09h
|
||||
|
||||
Confirmar sempre com o utilizador: título, cadeia, cron, projecto.
|
||||
|
||||
### Diagnostico: routines que nao disparam
|
||||
|
||||
Se routines nao disparam, verificar:
|
||||
|
||||
Invocar tool MCP: `mcp__paperclip__diag_routine_triggers_broken`
|
||||
|
||||
Corrigir com:
|
||||
```sql
|
||||
UPDATE routine_triggers SET kind = 'schedule', next_run_at = {{NEXT_RUN}}, updated_at = NOW()
|
||||
WHERE id = '{{TRIGGER_ID}}';
|
||||
```
|
||||
|
||||
### Diagnostico: pipeline noticias
|
||||
|
||||
Se noticias nao estao a ser publicadas, verificar esta cadeia:
|
||||
|
||||
```
|
||||
1. Routine triggers → kind='schedule'? next_run_at populado? last_fired_at recente?
|
||||
2. CEO heartbeat → apanha a issue da routine? delega ao CGO?
|
||||
3. CGO → delega ao Intelligence Researcher?
|
||||
4. Intelligence Researcher → cria issues PUBLICAR NOTICIA? (assignee fica NULL — normal)
|
||||
5. Cron assign-copywriter.sh → atribui ao Copywriter a cada 5 min?
|
||||
6. Copywriter → acorda com wakeOnDemand? publica no WordPress?
|
||||
7. Artigo publicado → acentuacao OK? links com aspas? titulo normalizado?
|
||||
```
|
||||
|
||||
Verificar ponto a ponto com:
|
||||
```sql
|
||||
-- Ponto 1: triggers
|
||||
SELECT r.title, rt.kind, rt.next_run_at, rt.last_fired_at
|
||||
FROM routine_triggers rt JOIN routines r ON rt.routine_id = r.id
|
||||
WHERE r.title LIKE 'Pesquisa diaria%noticias%' ORDER BY rt.cron_expression;
|
||||
|
||||
-- Ponto 4-5: issues PUBLICAR NOTICIA
|
||||
SELECT i.title, i.status, a.name as assignee, i.created_at
|
||||
FROM issues i LEFT JOIN agents a ON i.assignee_agent_id = a.id
|
||||
WHERE i.title LIKE 'PUBLICAR NOTICIA%' AND i.created_at > NOW() - INTERVAL '24 hours'
|
||||
ORDER BY i.created_at DESC;
|
||||
|
||||
-- Ponto 6: Copywriter runs
|
||||
SELECT hr.status, hr.started_at FROM heartbeat_runs hr
|
||||
JOIN agents a ON hr.agent_id = a.id WHERE a.name = 'Copywriter'
|
||||
AND hr.started_at > NOW() - INTERVAL '24 hours' ORDER BY hr.started_at DESC;
|
||||
|
||||
-- Ponto 7: artigos publicados (via SSH server)
|
||||
-- wp --allow-root post list --category=noticias --date_query='{"after":"1 day ago"}' --fields=ID,post_title
|
||||
```
|
||||
|
||||
## Cadeias de delegação por área
|
||||
|
||||
| Área | Cadeia |
|
||||
|------|--------|
|
||||
| Infraestrutura/WP/Backups/SSL | CEO → CTO → Dir. Infraestrutura → Especialista |
|
||||
| Desenvolvimento/MCPs/N8N | CEO → CTO → Dir. Desenvolvimento/Automação → Especialista |
|
||||
| Email/Tickets/Processos | CEO → COO → Dir. Suporte/Operações → Especialista |
|
||||
| Financeiro/Facturação | CEO → CFO → Dir. Financeiro → Finance Manager |
|
||||
| Marketing/SEO/Conteúdo/Ads | CEO → CMO → Dir. relevante → Especialista |
|
||||
| Vendas/Leads/Propostas | CEO → CRO → Dir. Comercial → Especialista |
|
||||
| Inteligência/Pesquisa | CEO → CGO → Dir. IA/Estratégia → Especialista |
|
||||
| Analytics/Dados | CEO → CDO → Analytics Agent |
|
||||
|
||||
## Formato de output
|
||||
|
||||
Para modo lista (heartbeats):
|
||||
```
|
||||
## Heartbeats Clip — [data]
|
||||
|
||||
| Agente | Tier | Intervalo | Enabled | Último HB | OK 24h | Fail 24h |
|
||||
...
|
||||
```
|
||||
|
||||
Para modo lista (routines):
|
||||
```
|
||||
## Routines Clip — [data]
|
||||
|
||||
| Título | Cron | Assignee | Cadeia | Próximo run | Enabled |
|
||||
...
|
||||
|
||||
Total: 5 routines activas no Paperclip (4 atribuídas ao CEO, 1 ao COO) + 9 migradas para n8n
|
||||
```
|
||||
|
||||
Para modo ver:
|
||||
```
|
||||
## Routine: {{NOME}}
|
||||
|
||||
**Cadeia:** {{cadeia_delegação}}
|
||||
**Cron:** {{cron}} | **Enabled:** {{enabled}}
|
||||
**Último trigger:** {{last_triggered_at}}
|
||||
|
||||
### Últimas 10 execuções
|
||||
| Status | Triggered | Completed | Erro |
|
||||
...
|
||||
|
||||
Taxa sucesso 24h: N/N (N%)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Healing Log
|
||||
|
||||
Registo de erros conhecidos e como evitá-los. Lido automaticamente antes de executar.
|
||||
|
||||
```jsonl
|
||||
{"date":"","issue":"","fix":"","source":"user|auto"}
|
||||
```
|
||||
|
||||
*Adicionar nova linha após cada erro corrigido.*
|
||||
@@ -0,0 +1,292 @@
|
||||
---
|
||||
name: clip-skill
|
||||
description: Gerir company skills no Paperclip — listar, instalar, atribuir a agentes, auditar cobertura. Usar quando "clip skill", "skills clip", "instalar skill", "atribuir skill", "skills paperclip".
|
||||
context: fork
|
||||
---
|
||||
|
||||
# /clip-skill — Gerir Company Skills Paperclip
|
||||
|
||||
Gerir skills instaladas na empresa Descomplicar no Paperclip. Instalar, atribuir, remover e auditar.
|
||||
|
||||
## Constantes
|
||||
|
||||
```
|
||||
BD: PGPASSWORD="paperclip" psql -h localhost -p 54329 -U paperclip -d paperclip
|
||||
COMPANY_ID: ebe10308-efd7-453f-86ab-13e6fe84004f
|
||||
API: http://localhost:3100/api
|
||||
SKILLS_CC: ~/.claude/plugins/marketplaces/descomplicar-plugins/
|
||||
```
|
||||
|
||||
## Contexto técnico
|
||||
|
||||
Skills no Paperclip funcionam como **injecção de contexto em runtime**:
|
||||
- O conteúdo do SKILL.md é injectado como contexto adicional ao agente a cada heartbeat
|
||||
- Não há instalação permanente — a skill é lida do ficheiro a cada execução
|
||||
- Atribuição é por agente via `adapter_config.paperclipSkillSync.desiredSkills` (array de slugs)
|
||||
- Skills instaladas na empresa ficam em `company_skills` — só as atribuídas ao agente são injectadas
|
||||
|
||||
**Distribuição actual de adapters:**
|
||||
- 1 agente (CEO): `gemini_local` com `gemini-2.5-pro`
|
||||
- 50 agentes: `gemini_local` com `gemini-2.5-flash`
|
||||
- 13 agentes: `opencode_local` com `openrouter/x-ai/grok-4.1-fast`
|
||||
- Agentes analyst/passive (ex: Reality Checker): `process` com `claude-sonnet-4-6` (sem heartbeat, sem skills)
|
||||
|
||||
O adapter `claude_local` já não é usado para agentes heartbeat.
|
||||
|
||||
## Modo lista (sem argumentos)
|
||||
|
||||
### Skills instaladas na empresa
|
||||
|
||||
```sql
|
||||
SELECT name, slug, key, source_type, trust_level, compatibility,
|
||||
created_at::date as instalada
|
||||
FROM company_skills
|
||||
WHERE company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
ORDER BY name;
|
||||
```
|
||||
|
||||
### Agentes com skills atribuidas
|
||||
|
||||
```sql
|
||||
SELECT name, role, status,
|
||||
adapter_config->'paperclipSkillSync'->'desiredSkills' as skills
|
||||
FROM agents
|
||||
WHERE company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
AND adapter_config::text LIKE '%paperclipSkillSync%'
|
||||
ORDER BY name;
|
||||
```
|
||||
|
||||
### Resumo
|
||||
|
||||
Apresentar:
|
||||
```
|
||||
## Skills Clip — [data]
|
||||
|
||||
**Empresa:** N skills instaladas (N locais, N built-in)
|
||||
**Agentes:** N com skills atribuidas / 62 total
|
||||
|
||||
### Skills instaladas
|
||||
| Nome | Key | Fonte | Trust | Compativel |
|
||||
...
|
||||
|
||||
### Agentes com skills
|
||||
| Agente | Role | Skills |
|
||||
...
|
||||
|
||||
### Agentes sem skills (top 10 por relevancia)
|
||||
[listar agentes com routines activas mas sem skills]
|
||||
```
|
||||
|
||||
## Modo instalar (com path ou key)
|
||||
|
||||
### Instalar skill de path local
|
||||
|
||||
Argumento: path absoluto para pasta com SKILL.md.
|
||||
|
||||
Verificar primeiro que existe SKILL.md:
|
||||
```bash
|
||||
ls {{PATH}}/SKILL.md 2>/dev/null && head -5 {{PATH}}/SKILL.md
|
||||
```
|
||||
|
||||
Instalar via API:
|
||||
```bash
|
||||
curl -s -X POST "http://localhost:3100/api/companies/ebe10308-efd7-453f-86ab-13e6fe84004f/skills/import" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-d '{"source": "{{PATH}}"}'
|
||||
```
|
||||
|
||||
Se API falha (auth), inserir directamente na BD:
|
||||
```sql
|
||||
-- Ler frontmatter do SKILL.md para obter name e description
|
||||
-- Depois inserir:
|
||||
INSERT INTO company_skills (id, company_id, key, slug, name, description, markdown, source_type, source_locator, trust_level, compatibility, file_inventory, created_at, updated_at)
|
||||
VALUES (
|
||||
gen_random_uuid(),
|
||||
'ebe10308-efd7-453f-86ab-13e6fe84004f',
|
||||
'descomplicar/{{PLUGIN}}/{{SKILL_NAME}}',
|
||||
'{{SKILL_NAME}}',
|
||||
'{{NAME_FROM_FRONTMATTER}}',
|
||||
'{{DESCRIPTION_FROM_FRONTMATTER}}',
|
||||
'{{FULL_SKILL_MD_CONTENT}}',
|
||||
'local_path',
|
||||
'{{PATH}}',
|
||||
'markdown_only',
|
||||
'compatible',
|
||||
'[]'::jsonb,
|
||||
NOW(), NOW()
|
||||
)
|
||||
RETURNING id, name, slug;
|
||||
```
|
||||
|
||||
Confirmar com o utilizador antes de instalar. Mostrar name e description do SKILL.md.
|
||||
|
||||
### Instalar skill do marketplace CC
|
||||
|
||||
Argumento: nome da skill no formato `plugin/skill` (ex: `crm-ops/crm`).
|
||||
|
||||
Resolver path:
|
||||
```bash
|
||||
SKILL_PATH="$HOME/.claude/plugins/marketplaces/descomplicar-plugins/{{PLUGIN}}/skills/{{SKILL}}"
|
||||
ls "$SKILL_PATH/SKILL.md" 2>/dev/null && head -10 "$SKILL_PATH/SKILL.md"
|
||||
```
|
||||
|
||||
Depois seguir o fluxo de instalacao por path local.
|
||||
|
||||
### Instalar em massa (com filtro de plugin)
|
||||
|
||||
Argumento: nome do plugin (ex: `crm-ops`).
|
||||
|
||||
```bash
|
||||
find "$HOME/.claude/plugins/marketplaces/descomplicar-plugins/{{PLUGIN}}/skills/" -name "SKILL.md" -exec dirname {} \;
|
||||
```
|
||||
|
||||
Listar todas as skills encontradas com name e description. Pedir confirmacao antes de instalar cada uma.
|
||||
|
||||
## Modo atribuir (skill a agente)
|
||||
|
||||
### Atribuir via API
|
||||
|
||||
```bash
|
||||
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": [{{SKILLS_ARRAY}}]}'
|
||||
```
|
||||
|
||||
### Atribuir via BD (fallback)
|
||||
|
||||
```sql
|
||||
UPDATE agents SET adapter_config = jsonb_set(
|
||||
COALESCE(adapter_config, '{}'::jsonb),
|
||||
'{paperclipSkillSync}',
|
||||
jsonb_build_object('desiredSkills', '{{SKILLS_JSON_ARRAY}}'::jsonb)
|
||||
)
|
||||
WHERE id = '{{AGENT_ID}}'
|
||||
AND company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
RETURNING name, adapter_config->'paperclipSkillSync'->'desiredSkills';
|
||||
```
|
||||
|
||||
Confirmar sempre:
|
||||
- Nome do agente
|
||||
- Skills a atribuir (mostrar name de cada)
|
||||
- Se vai substituir ou adicionar a skills existentes
|
||||
|
||||
### Atribuir por departamento (batch)
|
||||
|
||||
Argumento: nome do C-Level ou Director.
|
||||
|
||||
1. Obter agentes subordinados:
|
||||
```sql
|
||||
SELECT id, name, role FROM agents
|
||||
WHERE reports_to = '{{MANAGER_ID}}'
|
||||
AND company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f';
|
||||
```
|
||||
|
||||
2. Listar skills recomendadas para o departamento
|
||||
3. Confirmar com utilizador
|
||||
4. Aplicar a cada agente
|
||||
|
||||
## Modo remover
|
||||
|
||||
### Remover skill de agente
|
||||
|
||||
Ler skills actuais, filtrar a removida, escrever de volta:
|
||||
|
||||
```sql
|
||||
-- Ler
|
||||
SELECT adapter_config->'paperclipSkillSync'->'desiredSkills' as skills
|
||||
FROM agents WHERE id = '{{AGENT_ID}}';
|
||||
|
||||
-- Actualizar (remover skill especifica)
|
||||
-- Construir novo array sem a skill removida e usar o UPDATE do modo atribuir
|
||||
```
|
||||
|
||||
### Desinstalar skill da empresa
|
||||
|
||||
```sql
|
||||
DELETE FROM company_skills
|
||||
WHERE company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
AND slug = '{{SLUG}}'
|
||||
RETURNING name, slug;
|
||||
```
|
||||
|
||||
Avisar: isto remove a skill de todos os agentes que a referenciam. Confirmar sempre.
|
||||
|
||||
## Modo auditar
|
||||
|
||||
### Cobertura de skills
|
||||
|
||||
```sql
|
||||
-- Agentes com routines activas mas sem skills
|
||||
SELECT a.name, a.role, r.title as routine
|
||||
FROM agents a
|
||||
JOIN routines r ON r.assignee_agent_id = a.id
|
||||
WHERE a.company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
AND r.status = 'active'
|
||||
AND (a.adapter_config->'paperclipSkillSync' IS NULL
|
||||
OR a.adapter_config::text NOT LIKE '%paperclipSkillSync%')
|
||||
ORDER BY a.name;
|
||||
```
|
||||
|
||||
### Mapeamento recomendado (plugin CC → departamento Clip)
|
||||
|
||||
| Plugin CC | Agentes-alvo | Skills prioritarias |
|
||||
|-----------|-------------|-------------------|
|
||||
| core-tools | Todos | _core, quality-validator |
|
||||
| crm-ops | CRO, Dir. Comercial, Sales Manager, Lead Qualifier | crm, desk, lead-approach, orcamento |
|
||||
| gestao | COO, Dir. Operacoes, Project Manager | today, worklog, knowledge, tasks-overview |
|
||||
| infraestrutura | CTO, Dir. Infraestrutura, Infra Check, Backup Specialist | gateway-check, backup, easypanel, cwp-server |
|
||||
| marketing | CMO, Dir. SEO, Dir. Publicidade | seo-audit, seo-technical, ppc |
|
||||
| wordpress | Dir. Web, WP Update | wp-dev, wp-performance, wp-cli |
|
||||
| dev-tools | Dir. Desenvolvimento, Development Lead | dev-helper, pdf, docx |
|
||||
| automacao | Dir. Automacao | n8n, automation-lead |
|
||||
| negocio | CFO, CGO, Finance Manager | finance, research, saas |
|
||||
| perfex-dev | Dir. Desenvolvimento | perfex-module |
|
||||
|
||||
### Comparacao CC vs Clip
|
||||
|
||||
```sql
|
||||
-- Skills CC disponiveis (contar no filesystem)
|
||||
-- vs skills instaladas no Clip
|
||||
SELECT
|
||||
(SELECT COUNT(*) FROM company_skills WHERE company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f') as instaladas_clip;
|
||||
```
|
||||
|
||||
```bash
|
||||
find ~/.claude/plugins/marketplaces/descomplicar-plugins/ -name "SKILL.md" | wc -l
|
||||
```
|
||||
|
||||
Apresentar:
|
||||
```
|
||||
### Auditoria Skills — [data]
|
||||
|
||||
**CC:** N skills disponiveis em 15 plugins
|
||||
**Clip:** N instaladas (N%)
|
||||
**Agentes com skills:** N/62 (N%)
|
||||
**Agentes com routines sem skills:** N (ATENCAO)
|
||||
|
||||
### Gaps por departamento
|
||||
| Departamento | Agentes | Com skills | Sem skills | Skills recomendadas |
|
||||
...
|
||||
```
|
||||
|
||||
## Referencias
|
||||
|
||||
- Mecanismo runtime injection: conteúdo SKILL.md injectado como contexto adicional a cada heartbeat
|
||||
- API skills: `skills/paperclip/references/company-skills.md`
|
||||
- Schema BD: tabela `company_skills` (16 colunas)
|
||||
- Auditoria compatibilidade: `04-Stack/02.06-Clip/auditoria-skills-compatibilidade.md`
|
||||
- Manual: `06-Operacoes/Documentacao/Manuais/Paperclip/06-skills-e-plugins.md`
|
||||
|
||||
---
|
||||
|
||||
## Healing Log
|
||||
|
||||
Registo de erros conhecidos e como evitá-los. Lido automaticamente antes de executar.
|
||||
|
||||
```jsonl
|
||||
{"date":"2026-04-07","issue":"API /skills/import e /skills/sync sem Authorization header — rejeitadas com 401","fix":"Adicionar -H 'Authorization: Bearer $PAPERCLIP_API_KEY' a todos os curl da skill","source":"auto"}
|
||||
```
|
||||
|
||||
*Adicionar nova linha após cada erro corrigido.*
|
||||
@@ -0,0 +1,174 @@
|
||||
---
|
||||
name: clip-vision
|
||||
category: gestao
|
||||
description: "Gera documento de visao estrategica para o CEO Paperclip e bootstraps por C-Level. Resolve o vision gap — agentes param porque lhes falta a visao do fundador. Usar quando 'clip vision', 'visao paperclip', 'vision gap', 'bootstrap ceo', 'direcao agentes'."
|
||||
version: "1.0.0"
|
||||
created: 2026-04-07
|
||||
tools: [Read, Write, Bash, AskUserQuestion]
|
||||
---
|
||||
|
||||
# Skill: /clip-vision
|
||||
|
||||
Gera documento de visao estrategica para o CEO Paperclip e bootstrap documents para cada C-Level. Baseado no padrao Paperclip Vision Skill (#368 Aron Prins).
|
||||
|
||||
---
|
||||
|
||||
## Problema que resolve
|
||||
|
||||
Agentes Paperclip param de produzir apos setup porque so recebem tarefas operacionais, nao visao estrategica. O CEO nao sabe para onde ir. Os C-Level nao sabem o que priorizar. O "vision gap" faz com que a organizacao funcione mas sem direcao.
|
||||
|
||||
---
|
||||
|
||||
## Processo de execucao
|
||||
|
||||
### Fase 1 — Recolha de contexto (automatica)
|
||||
|
||||
Recolher dados do estado actual do Paperclip e do stack:
|
||||
|
||||
1. **Ler estado Paperclip:**
|
||||
- Ficheiro `/home/ealmeida/paperclip/` — listar agents activos
|
||||
- Desk CRM task #2041 (projecto Stack) — estado
|
||||
- `Hub/04-Stack/STK-Estado-Actual.md` — metricas actuais
|
||||
- `Hub/04-Stack/plano-consolidado-stack-q2-2026.md` — plano e metas
|
||||
|
||||
2. **Ler issues e routines:**
|
||||
- Via MCP Paperclip ou ficheiros locais em `/home/ealmeida/paperclip/`
|
||||
- Issues abertas, routines activas, ultimos heartbeats
|
||||
|
||||
3. **Compilar contexto:** resumo de 10-15 linhas com estado actual
|
||||
|
||||
### Fase 2 — Entrevista ao fundador (interactiva)
|
||||
|
||||
Fazer 5-7 perguntas ao Emanuel usando AskUserQuestion. Cada pergunta com 3-4 opcoes + "outro":
|
||||
|
||||
**Pergunta 1:** Qual e a prioridade principal para os proximos 30 dias?
|
||||
- a) Aumentar receita (novos clientes, propostas)
|
||||
- b) Melhorar operacoes internas (eficiencia, automacao)
|
||||
- c) Desenvolver produto/servico novo
|
||||
- d) Consolidar o que ja existe (qualidade, documentacao)
|
||||
- e) Outro: ___
|
||||
|
||||
**Pergunta 2:** Que departamento precisa de mais atencao neste momento?
|
||||
- a) D1 Comercial (vendas, leads, propostas)
|
||||
- b) D6 Marketing (SEO, conteudo, redes sociais)
|
||||
- c) D7 Tecnologia (infra, dev, automacao)
|
||||
- d) D3 Contabilidade (facturas, despesas, cash flow)
|
||||
- e) Outro: ___
|
||||
|
||||
**Pergunta 3:** Que nivel de autonomia queres dar aos agentes?
|
||||
- a) Passive — so actuam quando pedido, sempre com aprovacao
|
||||
- b) Balanced — actuam em rotinas, pedem aprovacao para novidades
|
||||
- c) Active — actuam proactivamente, reportam resultados
|
||||
- d) Manter o actual
|
||||
|
||||
**Pergunta 4:** Qual e o budget mensal aceitavel para agentes IA (tokens + APIs)?
|
||||
- a) <50 EUR/mes (minimo, so essenciais)
|
||||
- b) 50-100 EUR/mes (moderado)
|
||||
- c) 100-200 EUR/mes (investimento activo)
|
||||
- d) >200 EUR/mes (sem restricao significativa)
|
||||
|
||||
**Pergunta 5:** Qual e o objectivo principal do Q2 2026?
|
||||
- a) Facturacao: atingir X EUR/mes
|
||||
- b) Clientes: fechar N novos contratos
|
||||
- c) Produto: lancar servico/SaaS especifico
|
||||
- d) Stack: maximizar capacidade operacional
|
||||
- e) Outro: ___
|
||||
|
||||
**Pergunta 6:** O que deve ser explicitamente proibido para os agentes?
|
||||
- a) Contactar clientes directamente
|
||||
- b) Fazer deploy sem aprovacao
|
||||
- c) Gastar budget acima de X
|
||||
- d) Todas as anteriores
|
||||
- e) Outro: ___
|
||||
|
||||
**Pergunta 7:** Como medes sucesso dos agentes Paperclip?
|
||||
- a) Output tangivel (artigos, propostas, deploys)
|
||||
- b) Reducao de tempo em tarefas manuais
|
||||
- c) ROI mensuravel (custo vs valor gerado)
|
||||
- d) Consistencia (fazem sempre o basico bem)
|
||||
|
||||
### Fase 3 — Geracao de documentos
|
||||
|
||||
Com base nas respostas, gerar 5 documentos:
|
||||
|
||||
#### 1. vision.md (destino: /home/ealmeida/paperclip/vision.md)
|
||||
|
||||
```markdown
|
||||
# Visao Descomplicar — {data}
|
||||
|
||||
## Missao
|
||||
{extraido das respostas + contexto}
|
||||
|
||||
## Objectivos Q2 2026
|
||||
1. {objectivo principal}
|
||||
2. {objectivo secundario}
|
||||
3. {objectivo terciario}
|
||||
|
||||
## Prioridades (30 dias)
|
||||
1. {prioridade 1 com accao concreta}
|
||||
2. {prioridade 2}
|
||||
3. {prioridade 3}
|
||||
|
||||
## Restricoes
|
||||
- {proibicoes definidas na P6}
|
||||
- Budget maximo: {P4}
|
||||
- Governance: {P3}
|
||||
|
||||
## Metricas de sucesso
|
||||
- {P7 — como medir}
|
||||
|
||||
## Revisao
|
||||
- Gerado: {data}
|
||||
- Proxima revisao: {data + 90 dias}
|
||||
```
|
||||
|
||||
#### 2-5. Bootstrap documents (destino: /home/ealmeida/paperclip/)
|
||||
|
||||
Para cada C-Level (CEO, CTO, CMO, COO), gerar `{role}-bootstrap.md`:
|
||||
|
||||
```markdown
|
||||
# Bootstrap {Role} — {data}
|
||||
|
||||
## Contexto
|
||||
{estado actual do departamento relevante}
|
||||
|
||||
## Top 3 prioridades
|
||||
1. {prioridade com accao e deadline}
|
||||
2. {prioridade}
|
||||
3. {prioridade}
|
||||
|
||||
## Recursos disponiveis
|
||||
- Skills: {lista relevante}
|
||||
- MCPs: {lista relevante}
|
||||
- Routines: {activas no departamento}
|
||||
|
||||
## Restricoes
|
||||
- {do vision.md, filtradas para este papel}
|
||||
|
||||
## Proxima accao
|
||||
{1 accao concreta que pode fazer no proximo heartbeat}
|
||||
```
|
||||
|
||||
### Fase 4 — Resumo e confirmacao
|
||||
|
||||
Apresentar resumo ao utilizador:
|
||||
- 5 documentos gerados com paths
|
||||
- Sugerir proximos passos (injectar vision.md no CEO heartbeat, etc.)
|
||||
- Perguntar se quer ajustar algo
|
||||
|
||||
---
|
||||
|
||||
## Notas importantes
|
||||
|
||||
- Os documentos devem ser **accionaveis**, nao genericos
|
||||
- Cada bootstrap deve referenciar **dados reais** do estado actual
|
||||
- O vision.md deve ser **injectado no CEO** como contexto do heartbeat
|
||||
- Sugerir /schedule trimestral para refrescar a visao
|
||||
|
||||
---
|
||||
|
||||
## Healing Log
|
||||
|
||||
```jsonl
|
||||
{"date":"","issue":"","fix":"","source":"user|auto"}
|
||||
```
|
||||
@@ -0,0 +1,162 @@
|
||||
---
|
||||
name: clip
|
||||
description: Dashboard rápido Paperclip — estado agentes, issues abertas, heartbeats recentes, alertas. Usar quando "clip", "paperclip", "estado clip", "agentes clip".
|
||||
context: fork
|
||||
---
|
||||
|
||||
# /clip — Dashboard Paperclip
|
||||
|
||||
Dashboard rapido do estado do Clip (Paperclip Orquestrador Descomplicar).
|
||||
|
||||
## Constantes
|
||||
|
||||
```
|
||||
BD: PGPASSWORD="paperclip" psql -h localhost -p 54329 -U paperclip -d paperclip
|
||||
COMPANY_ID: ebe10308-efd7-453f-86ab-13e6fe84004f
|
||||
```
|
||||
|
||||
## Procedimento
|
||||
|
||||
Executar os 5 passos em paralelo (silenciosamente), depois apresentar dashboard compacto.
|
||||
|
||||
### Passo 1: Estado do servico
|
||||
|
||||
```bash
|
||||
systemctl --user status paperclip 2>&1 | head -5
|
||||
```
|
||||
|
||||
### Passo 2: Agentes por status
|
||||
|
||||
Invocar tool MCP: `mcp__paperclip__diag_agents_by_status`
|
||||
|
||||
### Passo 3: Issues abertas
|
||||
|
||||
```sql
|
||||
SELECT i.title, i.status, i.priority, a.name as assignee
|
||||
FROM issues i LEFT JOIN agents a ON i.assignee_agent_id = a.id
|
||||
WHERE i.company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
AND i.status NOT IN ('done','cancelled')
|
||||
ORDER BY
|
||||
CASE i.priority WHEN 'critical' THEN 1 WHEN 'high' THEN 2 WHEN 'medium' THEN 3 ELSE 4 END,
|
||||
i.status;
|
||||
```
|
||||
|
||||
### Passo 4: Ultimos 10 heartbeats
|
||||
|
||||
```sql
|
||||
SELECT a.name, hr.status, hr.started_at, hr.finished_at
|
||||
FROM heartbeat_runs hr JOIN agents a ON hr.agent_id = a.id
|
||||
WHERE a.company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
ORDER BY hr.started_at DESC LIMIT 10;
|
||||
```
|
||||
|
||||
### Passo 5: Alertas
|
||||
|
||||
Heartbeats falhados nas ultimas 24h:
|
||||
```sql
|
||||
SELECT a.name, LEFT(hr.error, 80) as erro, hr.started_at
|
||||
FROM heartbeat_runs hr JOIN agents a ON hr.agent_id = a.id
|
||||
WHERE a.company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
AND hr.status = 'failed'
|
||||
AND hr.started_at > NOW() - INTERVAL '24 hours'
|
||||
ORDER BY hr.started_at DESC;
|
||||
```
|
||||
|
||||
Falsos blockers (INC-07):
|
||||
|
||||
Invocar tool MCP: `mcp__paperclip__diag_false_blockers`
|
||||
|
||||
Se resultado > 0 → listar como alerta: "N issues blocked com sub-tasks activas — provavelmente deveriam ser in_progress".
|
||||
|
||||
Token burn — agentes com consumo excessivo de tokens nas últimas 24h (INC-12):
|
||||
|
||||
Invocar tool MCP: `mcp__paperclip__diag_heartbeat_token_usage(hours=24)`
|
||||
|
||||
Se `agentes_risco > 0` → alertar: "TOKEN BURN: N agentes com runs >500K cache tokens — usar /clip-health check 19 para detalhes".
|
||||
|
||||
Routines presas (INC-08) — a ligação issue→routine está em `routine_runs.linked_issue_id`:
|
||||
|
||||
Invocar tool MCP: `mcp__paperclip__diag_stuck_routines(hours=4)`
|
||||
|
||||
Se `routines_presas > 0` → alertar: "ROUTINE BLOQUEADA: N issues de routine paradas >4h — usar /clip-health check 20".
|
||||
|
||||
Budget (agentes com >0 configurado):
|
||||
```sql
|
||||
SELECT name, budget_monthly_cents, spent_monthly_cents,
|
||||
CASE WHEN budget_monthly_cents > 0
|
||||
THEN ROUND(spent_monthly_cents::numeric / budget_monthly_cents * 100, 1)
|
||||
ELSE 0 END as pct
|
||||
FROM agents
|
||||
WHERE company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'
|
||||
AND budget_monthly_cents > 0
|
||||
ORDER BY pct DESC;
|
||||
```
|
||||
|
||||
### Passo 6: Saude API (endpoint dashboard)
|
||||
|
||||
Verificar se os endpoints criticos respondem:
|
||||
```bash
|
||||
HEALTH=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3100/api/health)
|
||||
DASH=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3100/api/companies/ebe10308-efd7-453f-86ab-13e6fe84004f/dashboard)
|
||||
BADGES=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3100/api/companies/ebe10308-efd7-453f-86ab-13e6fe84004f/sidebar-badges)
|
||||
echo "health=$HEALTH dashboard=$DASH badges=$BADGES"
|
||||
```
|
||||
|
||||
- health=200 = OK (sem auth)
|
||||
- dashboard/badges=401 = OK (auth funciona, endpoint existe)
|
||||
- dashboard/badges=404 ou 500 = CRITICO (endpoint em falha, provavelmente refs orfas na BD)
|
||||
|
||||
Verificar instancias duplicadas:
|
||||
```bash
|
||||
ss -tlnp | grep -E "310[0-9]" | wc -l
|
||||
```
|
||||
- 1 = OK
|
||||
- 2+ = AVISO (processos orfaos, recomendar limpeza)
|
||||
|
||||
### Passo 7: Company skills
|
||||
|
||||
Invocar tool MCP: `mcp__paperclip__diag_company_skills_summary`
|
||||
|
||||
## Formato de output
|
||||
|
||||
Apresentar como dashboard compacto:
|
||||
|
||||
```
|
||||
## Clip Dashboard — [data/hora]
|
||||
|
||||
**Servico:** [running/stopped] ha [tempo]
|
||||
**Agentes:** [N] total — [n] running, [n] idle, [n] paused
|
||||
**API:** health=[code] dashboard=[code] badges=[code] | Instancias: [N]
|
||||
**Skills:** [N] instaladas ([n] locais, [n] skills.sh, [n] github)
|
||||
|
||||
### Issues abertas
|
||||
| Titulo | Status | Prioridade | Assignee |
|
||||
...
|
||||
|
||||
### Heartbeats recentes
|
||||
| Agente | Status | Inicio | Fim |
|
||||
...
|
||||
|
||||
### Alertas
|
||||
- [CRITICO] descricao
|
||||
- [TOKEN BURN] N agentes >500K tokens — /clip-health check 19
|
||||
- [ROUTINE BLOQUEADA] N issues paradas >4h — /clip-health check 20
|
||||
```
|
||||
|
||||
## Referencias
|
||||
|
||||
- Manual: Hub `06-Operacoes/Documentacao/Manuais/Paperclip/`
|
||||
- API: `04-Stack/02.06-Clip/skills.md`
|
||||
- Spec: `04-Stack/02.06-Clip/docs/superpowers/specs/2026-03-28-clip-grande-visao-design.md`
|
||||
|
||||
---
|
||||
|
||||
## Healing Log
|
||||
|
||||
Registo de erros conhecidos e como evitá-los. Lido automaticamente antes de executar.
|
||||
|
||||
```jsonl
|
||||
{"date":"","issue":"","fix":"","source":"user|auto"}
|
||||
```
|
||||
|
||||
*Adicionar nova linha após cada erro corrigido.*
|
||||
@@ -0,0 +1,110 @@
|
||||
---
|
||||
name: dual-review
|
||||
description: Revisão dual-model de conteúdo publicável — artigos WordPress, propostas comerciais, relatórios de cliente. Um agente gera com contexto completo, outro revisa sem contexto partilhado. Usar antes de publicar ou enviar a cliente.
|
||||
context: fork
|
||||
---
|
||||
|
||||
# /dual-review — Revisão Dual-Model
|
||||
|
||||
> "Um revisor sem contexto detecta o que o autor não vê."
|
||||
> Fonte: Eixo 4, Plano Stack Q2 2026.
|
||||
|
||||
---
|
||||
|
||||
## Quando usar
|
||||
|
||||
**OBRIGATÓRIO para:**
|
||||
- Artigos WordPress antes de publicar
|
||||
- Propostas comerciais antes de enviar
|
||||
- Relatórios de cliente antes de entregar
|
||||
- Qualquer conteúdo com dados factuais (métricas, preços, datas)
|
||||
|
||||
**Não usar para:**
|
||||
- Comentários internos no CRM
|
||||
- Notas de trabalho
|
||||
- Código (usar `superpowers:requesting-code-review`)
|
||||
|
||||
---
|
||||
|
||||
## Workflow (4 passos)
|
||||
|
||||
### Passo 1 — Gerar conteúdo (agente principal)
|
||||
|
||||
Gerar o conteúdo com contexto completo. Marcar dados não confirmados como `[VERIFICAR: X]`.
|
||||
|
||||
### Passo 2 — Preparar para revisão
|
||||
|
||||
Extrair apenas o output final + definir critérios. **Não incluir o contexto de geração.**
|
||||
|
||||
```
|
||||
CONTEÚDO PARA REVISÃO:
|
||||
[colar apenas o output final]
|
||||
|
||||
CRITÉRIOS:
|
||||
1. Factos verificados (sem "[VERIFICAR: X]" por resolver)
|
||||
2. URLs funcionais e correctas
|
||||
3. Datas e valores em EUR correctos
|
||||
4. PT-PT (não brasileiro): tu/você, descarregar/baixar, ficheiro/arquivo
|
||||
5. Acentos correctos: á, à, â, ã, é, ê, í, ó, ô, õ, ú, ç
|
||||
6. Coerência interna (sem contradições)
|
||||
7. Adequação ao público-alvo
|
||||
|
||||
JUDGMENT FRAMEWORK:
|
||||
[incluir exemplos relevantes de ~/.claude/design/judgment-frameworks/]
|
||||
```
|
||||
|
||||
### Passo 3 — Revisão por segundo agente
|
||||
|
||||
Usar `Agent tool` com `model: "sonnet"` passando APENAS o conteúdo + critérios.
|
||||
|
||||
Instrução ao segundo agente:
|
||||
```
|
||||
És um revisor de qualidade de conteúdo. Verifica o texto contra os critérios fornecidos.
|
||||
NÃO tens contexto sobre como foi gerado — avalia apenas o output final como leitor externo.
|
||||
Lista cada problema com localização exacta, ou responde "APROVADO" se não há problemas.
|
||||
```
|
||||
|
||||
### Passo 4 — Corrigir e repetir
|
||||
|
||||
Se o revisor retornar problemas:
|
||||
1. Corrigir cada problema no conteúdo
|
||||
2. Re-submeter (Passos 2-3)
|
||||
3. Repetir até "APROVADO" ou máximo 3 iterações
|
||||
|
||||
Se após 3 iterações ainda há problemas: escalar ao utilizador com lista de pendentes.
|
||||
|
||||
---
|
||||
|
||||
## Judgment Frameworks disponíveis
|
||||
|
||||
- `~/.claude/design/judgment-frameworks/global.md` — anti-alucinação geral
|
||||
- `~/.claude/design/judgment-frameworks/wordpress.md` — conteúdo e código WP
|
||||
- `~/.claude/design/judgment-frameworks/crm.md` — operações CRM
|
||||
|
||||
Incluir o framework relevante nos critérios do Passo 2.
|
||||
|
||||
---
|
||||
|
||||
## Exemplo de uso
|
||||
|
||||
```
|
||||
# Artigo WordPress sobre X gerado. Antes de publicar:
|
||||
/dual-review
|
||||
|
||||
Conteúdo: [artigo completo]
|
||||
Tipo: artigo-wordpress
|
||||
Público: PMEs portuguesas
|
||||
Judgment: ~/.claude/design/judgment-frameworks/wordpress.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Healing Log
|
||||
|
||||
Registo de erros conhecidos e como evitá-los. Lido automaticamente antes de executar.
|
||||
|
||||
```jsonl
|
||||
{"date":"2026-04-06","issue":"Placeholder vazio sem validacao previa","fix":"Validacao completa: frontmatter OK (name/description/context), paths judgment-frameworks OK (global/wordpress/crm), workflow 4 passos coerente. Nenhuma correccao necessaria.","source":"auto"}
|
||||
```
|
||||
|
||||
*Adicionar nova linha após cada erro corrigido.*
|
||||
@@ -0,0 +1,229 @@
|
||||
---
|
||||
name: hub-compile
|
||||
description: Compilador do 00-Inbox do Hub Obsidian. Classifica ficheiros .md, gera resumos, sugere destino e backlinks, move com aprovação explícita do utilizador. Usar quando "hub-compile", "compilar inbox", "triagem inbox", "processar inbox", "classificar notas".
|
||||
context: fork
|
||||
---
|
||||
|
||||
# /hub-compile v1.0 — Compilador do Hub Inbox
|
||||
|
||||
Processa automaticamente os ficheiros `.md` em `00-Inbox/` do Hub Obsidian: classifica, resume, sugere destino e backlinks, e move apenas com aprovação explícita.
|
||||
|
||||
---
|
||||
|
||||
## Filosofia
|
||||
|
||||
Padrão Karpathy (Raw → Compiler → Wiki): o LLM actua como compilador — lê ficheiros em bruto, extrai estrutura e significado, e propõe organização. O utilizador aprova antes de qualquer movimento.
|
||||
|
||||
**Princípio cardinal: NUNCA mover sem aprovação.**
|
||||
|
||||
---
|
||||
|
||||
## Protocolo de Execução
|
||||
|
||||
```
|
||||
1. LER ficheiros do Inbox
|
||||
2. CLASSIFICAR cada ficheiro
|
||||
3. APRESENTAR tabela ao utilizador
|
||||
4. AGUARDAR aprovação
|
||||
5. MOVER os aprovados
|
||||
6. ACTUALIZAR INDEX.md de origem e destino
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Passo 1 — Ler Inbox
|
||||
|
||||
```
|
||||
- Listar TODOS os .md em /media/ealmeida/Dados/Hub/00-Inbox/
|
||||
- EXCLUIR INDEX.md
|
||||
- Para cada ficheiro: ler conteúdo completo
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Passo 2 — Classificar
|
||||
|
||||
Para cada ficheiro, determinar:
|
||||
|
||||
### Tipo
|
||||
|
||||
| Tipo | Indicadores |
|
||||
|------|-------------|
|
||||
| `worklog` | "Worklog", datas de sessão, tool calls, MCPs usados |
|
||||
| `prompt` | "Prompt", instruções para Claude, specs de módulo |
|
||||
| `nota-tecnica` | Configurações, comandos, troubleshooting, fixes |
|
||||
| `planeamento` | "Plano", "migração", "roadmap", decisões activas |
|
||||
| `facturacao` | Facturas, recibos, datas limite, valores |
|
||||
| `migracao` | "Migração", "setup", hardware novo, configurações pendentes |
|
||||
| `stress-test` | Resultados de testes, benchmarks, métricas |
|
||||
| `outro` | Não se encaixa nas categorias acima |
|
||||
|
||||
### Destino Sugerido
|
||||
|
||||
| Tipo | Destino |
|
||||
|------|---------|
|
||||
| `worklog` | `99-Arquivo/Worklogs/YYYY/MM/` |
|
||||
| `prompt` | `99-Arquivo/Documentacao-Pontuais/` |
|
||||
| `nota-tecnica` com valor permanente | `06-Operacoes/Knowledge-Base/MDs/` |
|
||||
| `nota-tecnica` pontual/resolvida | `99-Arquivo/Documentacao-Pontuais/` |
|
||||
| `planeamento` activo | **MANTER** (fica no Inbox) |
|
||||
| `facturacao` | `99-Arquivo/Documentacao-Pontuais/` |
|
||||
| `migracao` activa | **MANTER** (fica no Inbox) |
|
||||
| `stress-test` | `99-Arquivo/Documentacao-Pontuais/` |
|
||||
| `outro` | Consultar utilizador |
|
||||
|
||||
### Acção
|
||||
|
||||
| Acção | Significado |
|
||||
|-------|-------------|
|
||||
| `mover` | Pronto para mover para o destino sugerido |
|
||||
| `arquivar` | Mover para `99-Arquivo/` |
|
||||
| `manter` | Deixar no Inbox — activo ou precisa mais trabalho |
|
||||
| `eliminar` | Duplicado, vazio, ou sem valor — confirmar antes |
|
||||
|
||||
### Gerar por ficheiro
|
||||
|
||||
- **Resumo:** 2-3 linhas descrevendo o conteúdo essencial
|
||||
- **Conceitos-chave:** 3-5 termos ou tópicos principais
|
||||
- **Backlinks sugeridos:** docs existentes no Hub que devem referenciar este ficheiro ou ser referenciados por ele (usar conhecimento do vault — QR-*, PROC-*, etc.)
|
||||
|
||||
---
|
||||
|
||||
## Passo 3 — Apresentar Tabela
|
||||
|
||||
Mostrar ao utilizador antes de qualquer acção:
|
||||
|
||||
```markdown
|
||||
## Hub Compile — Inbox Analysis
|
||||
|
||||
**N ficheiros encontrados** | Data: YYYY-MM-DD HH:MM
|
||||
|
||||
| # | Ficheiro | Tipo | Acção | Destino | Resumo |
|
||||
|---|----------|------|-------|---------|--------|
|
||||
| 1 | `nome.md` | worklog | mover | 99-Arquivo/Worklogs/2026/03/ | Sessão de 31 Mar: implementação X, Y, Z. Duração ~2h. |
|
||||
| 2 | `nome.md` | planeamento | manter | — (Inbox) | Migração Alurin em curso. Pendentes: configurar Syncthing. |
|
||||
| 3 | `nome.md` | prompt | mover | 99-Arquivo/Documentacao-Pontuais/ | Prompt para módulo Moloni-Perfex sync. Concluído. |
|
||||
|
||||
### Backlinks Sugeridos
|
||||
|
||||
| Ficheiro | Backlink para |
|
||||
|----------|--------------|
|
||||
| `stress-tests-alurin.md` | `QR-Servidores.md`, `infra.md` (memory) |
|
||||
| `worklog-2026-03-31.md` | Discussão Logs #31 Desk CRM |
|
||||
|
||||
---
|
||||
|
||||
**Aprovação necessária.**
|
||||
|
||||
Para aprovar todos os movimentos sugeridos: escrever `aprovar tudo`
|
||||
Para aprovar individualmente: escrever `aprovar 1,3,5` (números da tabela)
|
||||
Para ajustar uma acção: escrever `1=arquivar` ou `2=eliminar`
|
||||
Para cancelar: escrever `cancelar`
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Passo 4 — Aguardar Aprovação
|
||||
|
||||
**PARAR aqui. Não executar nada antes da resposta do utilizador.**
|
||||
|
||||
Interpretar respostas:
|
||||
- `aprovar tudo` → processar todos os `mover` e `arquivar` (nunca os `manter`)
|
||||
- `aprovar 1,3` → processar apenas linhas 1 e 3
|
||||
- `1=arquivar` → alterar acção da linha 1 para arquivar
|
||||
- `cancelar` → terminar sem mover nada
|
||||
|
||||
---
|
||||
|
||||
## Passo 5 — Mover Ficheiros Aprovados
|
||||
|
||||
Para cada ficheiro aprovado com acção `mover` ou `arquivar`:
|
||||
|
||||
```
|
||||
1. Verificar se directório de destino existe
|
||||
- SE não existe: criar com mcp__filesystem__create_directory
|
||||
2. Mover ficheiro: mcp__filesystem__move_file
|
||||
3. Confirmar que ficheiro chegou ao destino
|
||||
4. Registar movimento para actualização de INDEX.md
|
||||
```
|
||||
|
||||
**Formato do path de destino:**
|
||||
- Worklogs: `/media/ealmeida/Dados/Hub/99-Arquivo/Worklogs/YYYY/MM/nome.md`
|
||||
- Documentação pontual: `/media/ealmeida/Dados/Hub/99-Arquivo/Documentacao-Pontuais/nome.md`
|
||||
- Knowledge-Base: `/media/ealmeida/Dados/Hub/06-Operacoes/Knowledge-Base/MDs/nome.md`
|
||||
|
||||
---
|
||||
|
||||
## Passo 6 — Actualizar INDEX.md
|
||||
|
||||
### INDEX.md do Inbox (00-Inbox/INDEX.md)
|
||||
|
||||
Remover entradas dos ficheiros movidos. Se INDEX.md não tiver lista estruturada, adicionar nota de actualização no topo.
|
||||
|
||||
### INDEX.md do Destino
|
||||
|
||||
Para cada destino com movimentos:
|
||||
1. Ler INDEX.md existente (se existir)
|
||||
2. Adicionar entradas dos ficheiros movidos
|
||||
3. Se INDEX.md não existir e destino tem >3 ficheiros → criar
|
||||
|
||||
**Formato de entrada no INDEX.md:**
|
||||
|
||||
```markdown
|
||||
| `nome-ficheiro.md` | Tipo | YYYY-MM-DD | Descrição breve |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Output Final
|
||||
|
||||
```markdown
|
||||
## Hub Compile — Concluído
|
||||
|
||||
**Movidos:** N ficheiros
|
||||
**Mantidos no Inbox:** M ficheiros
|
||||
**Eliminados:** 0 (requer confirmação separada)
|
||||
|
||||
### Movimentos Realizados
|
||||
|
||||
- `worklog-2026-03-31.md` → 99-Arquivo/Worklogs/2026/03/
|
||||
- `prompt-modulo-moloni.md` → 99-Arquivo/Documentacao-Pontuais/
|
||||
|
||||
### Mantidos
|
||||
|
||||
- `migracao-alurin-mini-pc.md` — migração activa
|
||||
- `NovoPC.md` — planeamento em curso
|
||||
|
||||
### INDEX.md Actualizados
|
||||
|
||||
- 00-Inbox/INDEX.md ✓
|
||||
- 99-Arquivo/Worklogs/2026/03/INDEX.md ✓
|
||||
- 99-Arquivo/Documentacao-Pontuais/INDEX.md ✓
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Regras Obrigatórias
|
||||
|
||||
1. **NUNCA mover sem aprovação explícita** do utilizador
|
||||
2. **Worklogs** vão sempre para `99-Arquivo/Worklogs/YYYY/MM/`
|
||||
3. **Planeamento activo** e **migração em curso** ficam sempre no Inbox (`manter`)
|
||||
4. **Prompts concluídos** vão para `99-Arquivo/Documentacao-Pontuais/`
|
||||
5. **Notas técnicas com valor permanente** vão para `06-Operacoes/Knowledge-Base/MDs/`
|
||||
6. **Eliminar** requer confirmação separada — nunca eliminar na aprovação em massa
|
||||
7. **INDEX.md** deve ser actualizado em origem e destino após cada movimento
|
||||
8. **Usar mcp__filesystem__*** para todas as operações de ficheiros
|
||||
|
||||
---
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
- **NUNCA** mover com `aprovar tudo` sem excluir os `manter`
|
||||
- **NUNCA** criar destinos sem verificar se já existem
|
||||
- **NUNCA** omitir actualização dos INDEX.md
|
||||
- **NUNCA** classificar como `eliminar` sem justificação clara
|
||||
- **NUNCA** confundir planeamento activo com documentação pontual
|
||||
|
||||
---
|
||||
|
||||
*Skill v1.0 | 2026-04-07 | Descomplicar®*
|
||||
@@ -0,0 +1,255 @@
|
||||
---
|
||||
name: monday-briefing
|
||||
description: Briefing semanal automatizado com 4 componentes — tendências de mercado, métricas chave, acção da semana e contra-argumentos. Usar quando "monday", "briefing segunda", "resumo semanal", "briefing semana", "monday briefing".
|
||||
context: fork
|
||||
---
|
||||
|
||||
# /monday-briefing v1.0
|
||||
|
||||
Briefing semanal com 4 perspectivas: tendências, métricas, acção e realidade.
|
||||
|
||||
---
|
||||
|
||||
## Protocolo
|
||||
|
||||
### Passo 1: Recolher data e período
|
||||
|
||||
```
|
||||
mcp__mcp-time__current_time → data actual
|
||||
Calcular:
|
||||
- semana_actual: Segunda a Domingo desta semana
|
||||
- semana_anterior: Segunda a Domingo da semana passada
|
||||
- periodo_atual: "Semana W{N} ({DD Mmm} – {DD Mmm YYYY})"
|
||||
```
|
||||
|
||||
### Passo 2: Recolher dados em paralelo
|
||||
|
||||
> Executar TUDO em paralelo. Registar erros sem bloquear — continuar com dados disponíveis.
|
||||
|
||||
```
|
||||
Em paralelo:
|
||||
|
||||
[A] DESK CRM — Métricas da semana
|
||||
mcp__desk-crm-v3__get_tasks({ date_from: inicio_semana_anterior, date_to: fim_semana_anterior, status: 5 })
|
||||
→ tarefas_concluidas_semana_ant
|
||||
mcp__desk-crm-v3__get_tasks({ date_from: inicio_semana, date_to: fim_semana, status: [1,2,3] })
|
||||
→ tarefas_abertas_semana_atual
|
||||
mcp__desk-crm-v3__get_timesheets({ date_from: inicio_semana_anterior, date_to: fim_semana_anterior })
|
||||
→ horas_semana_ant
|
||||
mcp__desk-crm-v3__get_tickets({ status: [1,2,3] })
|
||||
→ tickets_abertos
|
||||
|
||||
[B] MOLONI — Facturação da semana
|
||||
mcp__moloni-mcp__list_documents({
|
||||
document_type: "invoice",
|
||||
date_from: inicio_semana_anterior,
|
||||
date_to: fim_semana_anterior
|
||||
})
|
||||
→ facturas_semana_ant (valor total + contagem)
|
||||
mcp__moloni-mcp__list_documents({
|
||||
document_type: "invoice",
|
||||
date_from: inicio_semana,
|
||||
date_to: fim_semana
|
||||
})
|
||||
→ facturas_semana_atual (se já existirem)
|
||||
|
||||
[C] DESK CRM — Leads e pipeline
|
||||
mcp__desk-crm-v3__get_leads({ status: [7,14], limit: 20 })
|
||||
→ leads_novos
|
||||
mcp__desk-crm-v3__get_estimates({ status: [3,4], limit: 20 })
|
||||
→ propostas_pipeline
|
||||
|
||||
[D] TENDÊNCIAS — WebSearch
|
||||
WebSearch: "marketing digital tendencias semana {data_atual}"
|
||||
WebSearch: "SEO updates {mes} {ano}"
|
||||
WebSearch: "WordPress news {mes} {ano}"
|
||||
→ 3 tendências relevantes ao negócio Descomplicar
|
||||
|
||||
[E] GOOGLE WORKSPACE — Agenda da semana
|
||||
mcp__google-workspace__calendar_get_events({
|
||||
user_google_email: "emanuelalmeidaa@gmail.com",
|
||||
time_min: "{inicio_semana}T00:00:00Z",
|
||||
time_max: "{fim_semana}T23:59:59Z"
|
||||
})
|
||||
→ eventos_semana
|
||||
```
|
||||
|
||||
### Passo 3: Calcular métricas comparativas
|
||||
|
||||
```
|
||||
Calcular variações:
|
||||
delta_horas = horas_semana_ant vs semana anterior a essa (% variação)
|
||||
delta_facturas = valor_semana_ant vs média das últimas 4 semanas
|
||||
taxa_conclusao = tarefas_concluidas / (tarefas_concluidas + abertas) * 100
|
||||
|
||||
SE dados incompletos: usar "N/D" e indicar fonte em falta
|
||||
```
|
||||
|
||||
### Passo 4: Gerar briefing em 4 secções
|
||||
|
||||
---
|
||||
|
||||
## Output
|
||||
|
||||
```markdown
|
||||
# Monday Briefing — {periodo_atual}
|
||||
*Gerado em {data_hora_atual} | Descomplicar®*
|
||||
|
||||
---
|
||||
|
||||
## 1. Trend Researcher — O Que Mudou Esta Semana
|
||||
|
||||
> Tendências relevantes para o negócio (marketing digital, SEO, WordPress, automação)
|
||||
|
||||
**[Tendência 1]**
|
||||
_Fonte: [URL]_
|
||||
Impacto potencial: [curto resumo do que muda para a Descomplicar]
|
||||
|
||||
**[Tendência 2]**
|
||||
_Fonte: [URL]_
|
||||
Impacto potencial: [curto resumo]
|
||||
|
||||
**[Tendência 3]**
|
||||
_Fonte: [URL]_
|
||||
Impacto potencial: [curto resumo]
|
||||
|
||||
---
|
||||
|
||||
## 2. Analytics Reporter — Métricas vs Semana Anterior
|
||||
|
||||
### Produtividade
|
||||
| Métrica | Semana Anterior | Esta Semana | Δ |
|
||||
|---------|----------------|-------------|---|
|
||||
| Horas registadas | X h | Y h | ±Z% |
|
||||
| Tarefas concluídas | X | Y | ±Z |
|
||||
| Tickets abertos | X | Y | ±Z |
|
||||
|
||||
### Pipeline Comercial
|
||||
| Métrica | Valor | Estado |
|
||||
|---------|-------|--------|
|
||||
| Facturação semana ant | {valor} EUR | {vs média} |
|
||||
| Leads novos | X | — |
|
||||
| Propostas pendentes | X | Total: Y EUR |
|
||||
| Propostas aceites | X | Total: Y EUR |
|
||||
|
||||
### Agenda da Semana
|
||||
| Data | Evento |
|
||||
|------|--------|
|
||||
| {DD Mmm} | {evento} |
|
||||
| … | … |
|
||||
|
||||
> Fontes: Desk CRM + Moloni + Google Calendar
|
||||
> Dados Google Analytics / GSC: configurar via /n8n-webhook ou GSC MCP
|
||||
|
||||
---
|
||||
|
||||
## 3. Growth Hacker — 1 Acção Para Esta Semana
|
||||
|
||||
**Acção:** [Nome curto e concreto]
|
||||
|
||||
**Porquê agora:** [Justificação baseada nos dados das secções 1 e 2]
|
||||
|
||||
**3 Passos para executar:**
|
||||
|
||||
1. **[Passo 1]** — [Descrição detalhada, quem faz, quando]
|
||||
2. **[Passo 2]** — [Descrição detalhada, ferramentas necessárias]
|
||||
3. **[Passo 3]** — [Métrica de validação: como saber que funcionou]
|
||||
|
||||
**Tempo estimado:** X horas
|
||||
**Impacto esperado:** [Métrica específica]
|
||||
|
||||
---
|
||||
|
||||
## 4. Reality Checker — Contra-Argumentos
|
||||
|
||||
> Questionar os pressupostos das secções anteriores antes de agir.
|
||||
|
||||
**Sobre as tendências (secção 1):**
|
||||
- [Contra-argumento]: Pode ser que [tendência X] não se aplique porque [razão específica ao contexto Descomplicar]
|
||||
- Pergunta a fazer: Temos capacidade para responder a esta tendência esta semana?
|
||||
|
||||
**Sobre as métricas (secção 2):**
|
||||
- [Contra-argumento]: A variação de [±Z%] pode ser [sazonalidade / evento pontual / dado incompleto]
|
||||
- Atenção: [Métrica Y] está a [subir/descer] — verificar se é tendência real ou ruído
|
||||
|
||||
**Sobre a acção (secção 3):**
|
||||
- [Contra-argumento]: A acção proposta assume que [pressuposto] — validar antes de executar
|
||||
- Risco principal: [O que pode correr mal e como mitigar]
|
||||
- Alternativa mais simples: [Versão reduzida da acção com menor risco]
|
||||
|
||||
---
|
||||
|
||||
*Próximo briefing: Segunda, {data_prox_segunda} 09:00*
|
||||
*Para agendar: `/schedule "monday-briefing" cron:"0 9 * * 1"`*
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Envio Automático (opcional)
|
||||
|
||||
```
|
||||
SE argumento "email":
|
||||
mcp__google-workspace__gmail_send_email({
|
||||
to: "emanuelalmeidaa@gmail.com",
|
||||
subject: "Monday Briefing — {periodo_atual}",
|
||||
body: {output_markdown_acima},
|
||||
format: "html"
|
||||
})
|
||||
|
||||
SE argumento "inbox" OU sem argumento:
|
||||
Guardar em: /media/ealmeida/Dados/Hub/00-Inbox/monday-briefing-{YYYY-MM-DD}.md
|
||||
Formato: frontmatter YAML + conteúdo markdown
|
||||
|
||||
SE argumento "terminal":
|
||||
Mostrar directamente no terminal (default)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Como Agendar
|
||||
|
||||
Para receber automaticamente todas as segundas às 9h:
|
||||
|
||||
```bash
|
||||
# Via skill /schedule:
|
||||
/schedule "monday-briefing" cron:"0 9 * * 1"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Fontes de Dados
|
||||
|
||||
| Fonte | MCP / Ferramenta | Dados |
|
||||
|-------|-----------------|-------|
|
||||
| Desk CRM | `mcp__desk-crm-v3` | Tarefas, tickets, timesheets, leads, propostas |
|
||||
| Moloni | `mcp__moloni-mcp` | Facturação semanal |
|
||||
| Google Calendar | `mcp__google-workspace` | Agenda da semana |
|
||||
| Tendências | `WebSearch` | Novidades mercado digital |
|
||||
| Google Analytics | n8n BI workflow | Sessions, conversões (a configurar) |
|
||||
| Google Search Console | GSC MCP (quando disponível) | Keywords, cliques orgânicos |
|
||||
|
||||
> GA4 e GSC: quando MCPs dedicados estiverem configurados, adicionar ao Passo 2[A]. Ver `/n8n-webhook` para workflow BI existente.
|
||||
|
||||
---
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
- NUNCA bloquear o briefing por falta de dados GA4/GSC — usar "N/D" e continuar
|
||||
- NUNCA inventar tendências — apenas WebSearch com fontes verificáveis
|
||||
- NUNCA recomendar mais de 1 acção na secção 3 (Growth Hacker)
|
||||
- SEMPRE usar mcp-time para calcular períodos (nunca assumir datas)
|
||||
- SEMPRE incluir secção Reality Checker — é o que diferencia este briefing
|
||||
|
||||
---
|
||||
|
||||
## Healing Log
|
||||
|
||||
```jsonl
|
||||
{"date":"","issue":"","fix":"","source":"user|auto"}
|
||||
```
|
||||
|
||||
*Adicionar nova linha após cada erro corrigido.*
|
||||
|
||||
---
|
||||
|
||||
*Skill v1.0.0 | 07-04-2026 | Descomplicar®*
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: reflect
|
||||
description: Alias de /worklog. Auto-reflexão e registo de trabalho unificado. /reflect = /worklog (mesmo resultado). /reflect deep = análise profunda com histórico. /reflect week = revisão semanal. Usar quando "reflect", "reflexão", "análise", "melhoria", "insight".
|
||||
description: Alias de /worklog. Auto-reflexão e registo de trabalho unificado. /reflect = /worklog (mesmo resultado). /reflect view = ver últimos registos. /reflect deep = análise profunda com histórico → Discussão #32. /reflect week = revisão semanal → Discussão #32. Usar quando "reflect", "reflexão", "análise", "melhoria", "insight".
|
||||
context: fork
|
||||
---
|
||||
|
||||
@@ -8,15 +8,18 @@ context: fork
|
||||
|
||||
**`/reflect` = `/worklog`** - Produzem o mesmo resultado.
|
||||
|
||||
Desde v4.0, worklog e reflect estao unificados. Toda a logica, formatos e workflows estao em `/worklog`.
|
||||
Desde v4.0, worklog e reflect estao unificados. Toda a logica, formatos e workflows estao em `/worklog` (v4.2.0).
|
||||
|
||||
## Comandos
|
||||
|
||||
| Comando | Equivalente | Descrição |
|
||||
|---------|-------------|-----------|
|
||||
| Comando | Equivalente worklog | Descrição |
|
||||
|---------|---------------------|-----------|
|
||||
| `/reflect` | `/worklog` | Registo + reflexão da sessão → Discussão #31 |
|
||||
| `/reflect deep` | (exclusivo) | Análise profunda com histórico → Discussão #32 |
|
||||
| `/reflect week` | (exclusivo) | Revisão semanal → Discussão #32 |
|
||||
| `/reflect view` | `/worklog view` | Ver últimos registos (discussão #31) |
|
||||
| `/reflect deep` | `/reflect deep` | Análise profunda com histórico → Discussão #32 |
|
||||
| `/reflect week` | `/reflect week` | Revisão semanal → Discussão #32 |
|
||||
|
||||
> **Nota:** `/reflect deep` e `/reflect week` são comandos partilhados — existem tanto no worklog como aqui. Não são exclusivos do reflect.
|
||||
|
||||
## Instrução
|
||||
|
||||
@@ -24,9 +27,26 @@ Ao receber `/reflect` (sem argumentos):
|
||||
1. Executar exactamente o protocolo de `/worklog`
|
||||
2. Ver skill `/worklog` (SKILL.md) para formato, workflow e storage
|
||||
|
||||
Ao receber `/reflect view`:
|
||||
1. Executar o protocolo de `/worklog view` (ver secção em `/worklog` SKILL.md)
|
||||
|
||||
Ao receber `/reflect deep` ou `/reflect week`:
|
||||
1. Ver secções específicas na skill `/worklog`
|
||||
|
||||
---
|
||||
|
||||
*Skill v4.0.0 | 2026-02-06 | Descomplicar(R)*
|
||||
*Skill v4.0.1 | 2026-04-06 | Descomplicar(R)*
|
||||
|
||||
---
|
||||
|
||||
## Healing Log
|
||||
|
||||
Registo de erros conhecidos e como evitá-los. Lido automaticamente antes de executar.
|
||||
|
||||
```jsonl
|
||||
{"date":"2026-04-06","issue":"Comandos /reflect deep e /reflect week marcados como '(exclusivo)' na tabela mas existem no worklog SKILL.md — causava confusão sobre origem dos comandos","fix":"Remover '(exclusivo)', mapear correctamente para equivalente worklog e adicionar nota explicativa","source":"auto"}
|
||||
{"date":"2026-04-06","issue":"Versão reflect desactualizada (v4.0.0) face a worklog (v4.2.0) — utilizador podia assumir reflect obsoleto","fix":"Actualizar referência de versão do worklog para v4.2.0 na descrição; reflect mantém v4.0.1 (alias sem lógica própria)","source":"auto"}
|
||||
{"date":"2026-04-06","issue":"/worklog view não referenciado em reflect — utilizador com /reflect não sabia como ver registos anteriores","fix":"Adicionar /reflect view como alias de /worklog view na tabela de comandos e na secção de instrução","source":"auto"}
|
||||
```
|
||||
|
||||
*Adicionar nova linha após cada erro corrigido.*
|
||||
|
||||
Reference in New Issue
Block a user