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>
5.9 KiB
name, description, allowed-tools
| name | description | allowed-tools |
|---|---|---|
| spoke-dev | 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". | 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:
# 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:
/**
* 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:
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:
/**
* <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:
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:
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:
export { <Nome> } from './<Nome>';
export type { <Nome>Props } from './<Nome>';
Padrões Obrigatórios
TypeScript
strict: trueemtsconfig.json— semanyimplí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/imagepara todas as imagensloading="lazy"em componentes pesadosSuspenseboundaries em torno de data-fetching- Cache de API calls com
unstable_cacheou React cache
Segurança
- Nunca expor
ACIDAOS_CORE_URLno cliente (apenas server-side) - Sanitizar input do utilizador antes de enviar ao Core
- Usar Next.js Server Actions para mutações
Quality Gate
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
displayNamedefinido no componentepnpm buildsem erros- Client/Server components correctamente marcados
- CHANGELOG.md actualizado
Versão: 1.0.0 | Autor: Descomplicar® | Plugin: acidaos