feat: adicionar plugin acidaos e skill prompt-refine
Plugin acidaos (novo): - rust-dev: desenvolvimento Core em Rust (Axum, crates, debug compiler) - spoke-dev: desenvolvimento Spokes em Next.js/TypeScript + Storybook - devops: pipelines Gitea Actions CI/CD (adaptado de GitHub para Gitea) - docs: rustdoc, TypeDoc, Outline e ADRs dev-tools: - prompt-refine: skill genérica de engenharia de prompts para agentes IA Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user