feat(acidaos): adiciona plugin acidaos (agents, skills, knowledge e metadados do projeto)
This commit is contained in:
@@ -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,17 @@
|
|||||||
|
# AGENTS.md — skills
|
||||||
|
|
||||||
|
Directrizes específicas deste projecto para agentes IA.
|
||||||
|
|
||||||
|
## Referências obrigatórias
|
||||||
|
|
||||||
|
- `ECOSYSTEM.md` — Hub/06-Operacoes/Documentacao/ECOSYSTEM.md
|
||||||
|
- `PROC-DEV-STANDARD` — D7-SIS-006
|
||||||
|
- `PROC-AUDIT-STANDARD` — D7-SIS-007
|
||||||
|
|
||||||
|
## Protocolo de sessão
|
||||||
|
|
||||||
|
Início: ler MEMORY.md + STATUS.md. Fim: actualizar ambos.
|
||||||
|
|
||||||
|
## Convenções específicas
|
||||||
|
|
||||||
|
- (a definir)
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# CHANGELOG — skills
|
||||||
|
|
||||||
|
## 0.1.0 — 2026-06-30
|
||||||
|
### Adicionado
|
||||||
|
- Estrutura padrão (PROC-DEV-STANDARD)
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# MEMORY.md — skills
|
||||||
|
|
||||||
|
## 2026-06-30
|
||||||
|
- Estrutura padrão aplicada via propagate-structure.sh
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# skills
|
||||||
|
|
||||||
|
Subprojecto skills do ecossistema Descomplicar.
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# STATUS.md — skills
|
||||||
|
|
||||||
|
**Versão:** 0.1 | **Data:** 2026-06-30 | **Agente:** —
|
||||||
|
|
||||||
|
## Estado actual
|
||||||
|
- Em curso: —
|
||||||
|
- Bloqueios: —
|
||||||
|
- Próximos passos: —
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"task_id": null,
|
||||||
|
"project_id": null,
|
||||||
|
"customer_id": null,
|
||||||
|
"gitea_repo": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
---
|
||||||
|
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__mem_search_memories
|
||||||
|
layer: wiki
|
||||||
|
---
|
||||||
|
|
||||||
|
# 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 `gateway.descomplicar.pt`.
|
||||||
|
|
||||||
|
## Contexto
|
||||||
|
|
||||||
|
```
|
||||||
|
Repositórios Gitea:
|
||||||
|
acidaos-core → Rust Core
|
||||||
|
acidaos-dashboard → Next.js Dashboard
|
||||||
|
|
||||||
|
Runners:
|
||||||
|
self-hosted @ gateway.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__mem_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
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
---
|
||||||
|
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__mem_search_memories
|
||||||
|
layer: wiki
|
||||||
|
---
|
||||||
|
|
||||||
|
# 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__mem_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
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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,8 @@
|
|||||||
|
# skills
|
||||||
|
|
||||||
|
## Documentação
|
||||||
|
|
||||||
|
- [README](README.md)
|
||||||
|
- [AGENTS](AGENTS.md)
|
||||||
|
- [STATUS](STATUS.md)
|
||||||
|
- [MEMORY](MEMORY.md)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
## 2026-06-30 init | Estrutura padrão
|
||||||
@@ -0,0 +1,208 @@
|
|||||||
|
---
|
||||||
|
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__mem_search_memories, mcp__gitea__get_file_content, mcp__gitea__create_file, mcp__gitea__update_file
|
||||||
|
layer: wiki
|
||||||
|
---
|
||||||
|
|
||||||
|
# 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__mem_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
|
||||||
|
ca
|
||||||
@@ -0,0 +1,208 @@
|
|||||||
|
---
|
||||||
|
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__mem_search_memories, mcp__gitea__get_file_content, mcp__gitea__create_file, mcp__gitea__update_file
|
||||||
|
layer: wiki
|
||||||
|
---
|
||||||
|
|
||||||
|
# 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__mem_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
|
||||||
Reference in New Issue
Block a user