devsquad 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,153 @@
1
+ # React — Setup completo (Vite + TS)
2
+
3
+ Complementa o **SKILL.md** com arquivos base: API, auth e rotas.
4
+
5
+ ## src/services/api.ts
6
+
7
+ ```typescript
8
+ import axios from 'axios';
9
+
10
+ const api = axios.create({
11
+ baseURL: import.meta.env.VITE_API_URL ?? 'http://localhost:3000/api/v1',
12
+ withCredentials: true,
13
+ });
14
+
15
+ api.interceptors.request.use((config) => {
16
+ const token = localStorage.getItem('access_token');
17
+ if (token) config.headers.Authorization = `Bearer ${token}`;
18
+ return config;
19
+ });
20
+
21
+ api.interceptors.response.use(
22
+ (r) => r,
23
+ async (error) => {
24
+ // Opcional: refresh token + fila de retries — alinhar com backend
25
+ if (error.response?.status === 401) {
26
+ localStorage.removeItem('access_token');
27
+ window.location.href = '/login';
28
+ }
29
+ return Promise.reject(error);
30
+ },
31
+ );
32
+
33
+ export default api;
34
+ ```
35
+
36
+ ## src/contexts/AuthContext.tsx
37
+
38
+ ```typescript
39
+ import {
40
+ createContext,
41
+ useContext,
42
+ useState,
43
+ useCallback,
44
+ type ReactNode,
45
+ } from 'react';
46
+ import api from '../services/api';
47
+
48
+ type User = { id: string; email: string; name: string };
49
+
50
+ type AuthContextValue = {
51
+ user: User | null;
52
+ loading: boolean;
53
+ login: (email: string, password: string) => Promise<void>;
54
+ logout: () => void;
55
+ };
56
+
57
+ const AuthContext = createContext<AuthContextValue | null>(null);
58
+
59
+ export function AuthProvider({ children }: { children: ReactNode }) {
60
+ const [user, setUser] = useState<User | null>(null);
61
+ const [loading, setLoading] = useState(false);
62
+
63
+ const login = useCallback(async (email: string, password: string) => {
64
+ setLoading(true);
65
+ try {
66
+ const { data } = await api.post('/auth/login', { email, password });
67
+ localStorage.setItem('access_token', data.access_token);
68
+ setUser(data.user);
69
+ } finally {
70
+ setLoading(false);
71
+ }
72
+ }, []);
73
+
74
+ const logout = useCallback(() => {
75
+ localStorage.removeItem('access_token');
76
+ setUser(null);
77
+ }, []);
78
+
79
+ return (
80
+ <AuthContext.Provider value={{ user, loading, login, logout }}>
81
+ {children}
82
+ </AuthContext.Provider>
83
+ );
84
+ }
85
+
86
+ export function useAuth() {
87
+ const ctx = useContext(AuthContext);
88
+ if (!ctx) throw new Error('useAuth outside AuthProvider');
89
+ return ctx;
90
+ }
91
+ ```
92
+
93
+ ## src/routes/PrivateRoute.tsx
94
+
95
+ ```typescript
96
+ import { Navigate, Outlet } from 'react-router-dom';
97
+ import { useAuth } from '../contexts/AuthContext';
98
+
99
+ export function PrivateRoute() {
100
+ const { user, loading } = useAuth();
101
+ if (loading) return <div>Carregando…</div>;
102
+ if (!user) return <Navigate to="/login" replace />;
103
+ return <Outlet />;
104
+ }
105
+ ```
106
+
107
+ ## src/routes/AppRoutes.tsx
108
+
109
+ ```typescript
110
+ import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
111
+ import { AuthProvider } from '../contexts/AuthContext';
112
+ import { PrivateRoute } from './PrivateRoute';
113
+ // import LoginPage from '../pages/login/LoginPage';
114
+ // import DashboardPage from '../pages/dashboard/DashboardPage';
115
+
116
+ export function AppRoutes() {
117
+ return (
118
+ <BrowserRouter>
119
+ <AuthProvider>
120
+ <Routes>
121
+ <Route path="/login" element={<div>Login</div>} />
122
+ <Route element={<PrivateRoute />}>
123
+ <Route path="/" element={<div>Dashboard</div>} />
124
+ </Route>
125
+ <Route path="*" element={<Navigate to="/" replace />} />
126
+ </Routes>
127
+ </AuthProvider>
128
+ </BrowserRouter>
129
+ );
130
+ }
131
+ ```
132
+
133
+ ## src/main.tsx
134
+
135
+ ```typescript
136
+ import { StrictMode } from 'react';
137
+ import { createRoot } from 'react-dom/client';
138
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
139
+ import { AppRoutes } from './routes/AppRoutes';
140
+ import './index.css';
141
+
142
+ const queryClient = new QueryClient();
143
+
144
+ createRoot(document.getElementById('root')!).render(
145
+ <StrictMode>
146
+ <QueryClientProvider client={queryClient}>
147
+ <AppRoutes />
148
+ </QueryClientProvider>
149
+ </StrictMode>,
150
+ );
151
+ ```
152
+
153
+ Substitua os placeholders de página pelos componentes reais em `pages/`.
@@ -0,0 +1,82 @@
1
+ ---
2
+ name: react-native-mobile
3
+ description: Setup e arquitetura mobile com React Native + Expo + NativeWind. Use quando o desenvolvedor precisar inicializar ou trabalhar no app mobile, configurar Expo Router, armazenamento seguro de tokens, navegação ou build com EAS.
4
+ ---
5
+
6
+ # React Native + Expo + NativeWind
7
+
8
+ ## Setup rápido
9
+
10
+ ```bash
11
+ npx create-expo-app@latest nome-do-app --template blank-typescript
12
+ cd nome-do-app
13
+ npx expo install expo-router expo-linking expo-constants expo-status-bar react-native-screens react-native-safe-area-context expo-secure-store
14
+ npm install nativewind tailwindcss && npx tailwindcss init
15
+ npm install axios react-hook-form zod @hookform/resolvers
16
+ ```
17
+
18
+ ## Configuração NativeWind
19
+
20
+ ```javascript
21
+ // tailwind.config.js
22
+ module.exports = {
23
+ content: ['./app/**/*.{js,jsx,ts,tsx}', './components/**/*.{js,jsx,ts,tsx}'],
24
+ presets: [require('nativewind/preset')],
25
+ }
26
+ ```
27
+
28
+ ```javascript
29
+ // babel.config.js
30
+ module.exports = function(api) {
31
+ api.cache(true);
32
+ return {
33
+ presets: [['babel-preset-expo', { jsxImportSource: 'nativewind' }], 'nativewind/babel'],
34
+ };
35
+ };
36
+ ```
37
+
38
+ ## Estrutura (Expo Router)
39
+
40
+ ```
41
+ app/
42
+ ├── _layout.tsx ← Root layout com AuthProvider
43
+ ├── (auth)/
44
+ │ ├── _layout.tsx ← Redireciona para (app) se autenticado
45
+ │ └── login.tsx
46
+ └── (app)/
47
+ ├── _layout.tsx ← Tab bar ou Stack layout
48
+ └── index.tsx ← Dashboard
49
+ components/ui/ ← Button, Input, Card
50
+ services/
51
+ ├── api.ts ← Axios + interceptors
52
+ └── storage.service.ts ← SecureStore (NUNCA AsyncStorage para tokens)
53
+ contexts/AuthContext.tsx
54
+ ```
55
+
56
+ ## Regra crítica de segurança
57
+
58
+ ```typescript
59
+ // ✅ CORRETO — SecureStore usa Keychain (iOS) e Keystore (Android)
60
+ import * as SecureStore from 'expo-secure-store';
61
+ await SecureStore.setItemAsync('access_token', token);
62
+
63
+ // ❌ ERRADO — AsyncStorage é texto plano acessível com root
64
+ import AsyncStorage from '@react-native-async-storage/async-storage';
65
+ await AsyncStorage.setItem('access_token', token); // NUNCA para tokens
66
+ ```
67
+
68
+ > 🔒 OWASP M9 — Insecure Data Storage: Use `expo-secure-store` para qualquer dado sensível.
69
+
70
+ ## .env.local.example
71
+
72
+ ```env
73
+ # IP da máquina na rede local — localhost não funciona no dispositivo físico
74
+ # Windows: ipconfig | Mac/Linux: ifconfig
75
+ EXPO_PUBLIC_API_URL=http://192.168.X.X:3000/api/v1
76
+ ```
77
+
78
+ ## Arquivos detalhados desta skill
79
+
80
+ - **setup.md** — AuthContext, api.ts, storage.service.ts, layouts Expo Router completos
81
+
82
+ Leia `setup.md` para implementar a estrutura base completa.
@@ -0,0 +1,150 @@
1
+ # React Native (Expo) — Setup completo
2
+
3
+ Complementa o **SKILL.md** com API, armazenamento seguro e integração com Expo Router.
4
+
5
+ ## services/storage.service.ts
6
+
7
+ ```typescript
8
+ import * as SecureStore from 'expo-secure-store';
9
+
10
+ const KEYS = { access: 'access_token', refresh: 'refresh_token' } as const;
11
+
12
+ export async function setTokens(access: string, refresh?: string) {
13
+ await SecureStore.setItemAsync(KEYS.access, access);
14
+ if (refresh) await SecureStore.setItemAsync(KEYS.refresh, refresh);
15
+ }
16
+
17
+ export async function getAccessToken() {
18
+ return SecureStore.getItemAsync(KEYS.access);
19
+ }
20
+
21
+ export async function clearTokens() {
22
+ await SecureStore.deleteItemAsync(KEYS.access);
23
+ await SecureStore.deleteItemAsync(KEYS.refresh);
24
+ }
25
+ ```
26
+
27
+ ## services/api.ts
28
+
29
+ ```typescript
30
+ import axios from 'axios';
31
+ import { getAccessToken, clearTokens } from './storage.service';
32
+
33
+ const api = axios.create({
34
+ baseURL: process.env.EXPO_PUBLIC_API_URL,
35
+ timeout: 15000,
36
+ });
37
+
38
+ api.interceptors.request.use(async (config) => {
39
+ const token = await getAccessToken();
40
+ if (token) config.headers.Authorization = `Bearer ${token}`;
41
+ return config;
42
+ });
43
+
44
+ api.interceptors.response.use(
45
+ (r) => r,
46
+ async (error) => {
47
+ if (error.response?.status === 401) {
48
+ await clearTokens();
49
+ // Redirecionar via router — ex.: router.replace('/(auth)/login')
50
+ }
51
+ return Promise.reject(error);
52
+ },
53
+ );
54
+
55
+ export default api;
56
+ ```
57
+
58
+ ## contexts/AuthContext.tsx
59
+
60
+ ```typescript
61
+ import {
62
+ createContext,
63
+ useContext,
64
+ useState,
65
+ useCallback,
66
+ type ReactNode,
67
+ } from 'react';
68
+ import api from '../services/api';
69
+ import * as storage from '../services/storage.service';
70
+
71
+ type User = { id: string; email: string; name: string };
72
+
73
+ type AuthContextValue = {
74
+ user: User | null;
75
+ loading: boolean;
76
+ signIn: (email: string, password: string) => Promise<void>;
77
+ signOut: () => Promise<void>;
78
+ };
79
+
80
+ const AuthContext = createContext<AuthContextValue | null>(null);
81
+
82
+ export function AuthProvider({ children }: { children: ReactNode }) {
83
+ const [user, setUser] = useState<User | null>(null);
84
+ const [loading, setLoading] = useState(false);
85
+
86
+ const signIn = useCallback(async (email: string, password: string) => {
87
+ setLoading(true);
88
+ try {
89
+ const { data } = await api.post('/auth/login', { email, password });
90
+ await storage.setTokens(data.access_token, data.refresh_token);
91
+ setUser(data.user);
92
+ } finally {
93
+ setLoading(false);
94
+ }
95
+ }, []);
96
+
97
+ const signOut = useCallback(async () => {
98
+ await storage.clearTokens();
99
+ setUser(null);
100
+ }, []);
101
+
102
+ return (
103
+ <AuthContext.Provider value={{ user, loading, signIn, signOut }}>
104
+ {children}
105
+ </AuthContext.Provider>
106
+ );
107
+ }
108
+
109
+ export function useAuth() {
110
+ const ctx = useContext(AuthContext);
111
+ if (!ctx) throw new Error('useAuth outside AuthProvider');
112
+ return ctx;
113
+ }
114
+ ```
115
+
116
+ ## app/_layout.tsx (raiz)
117
+
118
+ ```typescript
119
+ import { Stack } from 'expo-router';
120
+ import { AuthProvider } from '../contexts/AuthContext';
121
+
122
+ export default function RootLayout() {
123
+ return (
124
+ <AuthProvider>
125
+ <Stack screenOptions={{ headerShown: false }} />
126
+ </AuthProvider>
127
+ );
128
+ }
129
+ ```
130
+
131
+ ## app/(auth)/login.tsx (exemplo mínimo)
132
+
133
+ ```typescript
134
+ import { View, Text, TextInput, Pressable } from 'react-native';
135
+ import { useAuth } from '../../contexts/AuthContext';
136
+
137
+ export default function Login() {
138
+ const { signIn, loading } = useAuth();
139
+ return (
140
+ <View style={{ padding: 24 }}>
141
+ <Text>Login</Text>
142
+ <Pressable onPress={() => signIn('a@b.com', 'password')} disabled={loading}>
143
+ <Text>Entrar</Text>
144
+ </Pressable>
145
+ </View>
146
+ );
147
+ }
148
+ ```
149
+
150
+ Ajuste formulário com `react-hook-form` + `zod` conforme o SKILL.md. Use **`EXPO_PUBLIC_API_URL`** com IP da máquina em dispositivo físico.
@@ -0,0 +1,80 @@
1
+ ---
2
+ name: security-owasp
3
+ description: Checklist e implementações de segurança baseadas no OWASP Top 10 para NestJS + React. Use quando o desenvolvedor precisar revisar segurança, implementar autenticação segura, configurar proteções HTTP, validar inputs ou quando qualquer questão de segurança surgir no desenvolvimento.
4
+ ---
5
+
6
+ # Segurança — OWASP Top 10
7
+
8
+ Referência: https://owasp.org/Top10/
9
+
10
+ ## Checklist rápido por módulo
11
+
12
+ ### Ao criar qualquer módulo de autenticação
13
+ - [ ] **A01** — Guards em todas as rotas protegidas + RBAC
14
+ - [ ] **A02** — bcrypt salt >= 12, nunca retornar senha na resposta
15
+ - [ ] **A03** — DTO com class-validator, `whitelist: true` no ValidationPipe
16
+ - [ ] **A04** — Rate limiting com ThrottlerGuard (login: 5 tentativas/15min)
17
+ - [ ] **A05** — Helmet ativo, CORS explícito, secrets em `.env`
18
+ - [ ] **A07** — JWT access 15min + refresh 7d com rotation
19
+ - [ ] **A09** — Logs sem dados sensíveis (sem email, senha ou token)
20
+
21
+ ### Ao criar qualquer endpoint de dados
22
+ - [ ] UUID nos IDs (evita enumeração — A01)
23
+ - [ ] Ownership validation (usuário só acessa seus dados)
24
+ - [ ] Soft delete (nunca delete físico em produção)
25
+ - [ ] Paginação (nunca retorne todos os registros)
26
+
27
+ ## Implementações críticas
28
+
29
+ ### A01 — Broken Access Control
30
+
31
+ ```typescript
32
+ // Ownership validation — obrigatório em qualquer PATCH/DELETE
33
+ @Patch(':id')
34
+ @UseGuards(JwtAuthGuard)
35
+ async update(@Param('id') id: string, @CurrentUser() user: User) {
36
+ if (id !== user.id && user.role !== 'ADMIN') {
37
+ throw new ForbiddenException('Acesso negado');
38
+ }
39
+ // ...
40
+ }
41
+ ```
42
+
43
+ ### A02 — Cryptographic Failures
44
+
45
+ ```typescript
46
+ // Sempre bcrypt com salt >= 12
47
+ const hash = await bcrypt.hash(password, 12);
48
+ const valid = await bcrypt.compare(plain, hash);
49
+ // Nunca MD5, SHA1, ou texto plano
50
+ ```
51
+
52
+ ### A03 — Injection
53
+
54
+ ```typescript
55
+ // DTO com validação — nunca confie no input do usuário
56
+ export class CreateUserDto {
57
+ @IsEmail() email: string;
58
+ @IsString() @MinLength(8) @MaxLength(128) password: string;
59
+ @IsString() @MaxLength(100) name: string;
60
+ }
61
+ // Prisma usa queries parametrizadas por padrão — nunca use $queryRaw com string
62
+ ```
63
+
64
+ ### A07 — Identification Failures
65
+
66
+ ```typescript
67
+ // Rate limit específico em rotas de auth
68
+ @Post('login')
69
+ @Throttle({ default: { ttl: 900000, limit: 5 } }) // 5 tentativas / 15min
70
+ login() {}
71
+
72
+ // Mesma mensagem para email/senha inválidos — evita user enumeration
73
+ throw new UnauthorizedException('Credenciais inválidas');
74
+ ```
75
+
76
+ ## Arquivos detalhados desta skill
77
+
78
+ - **owasp-full.md** — Todos os 10 itens com código completo e exemplos
79
+
80
+ Leia `owasp-full.md` para revisão aprofundada de segurança.
@@ -0,0 +1,66 @@
1
+ # OWASP Top 10 — referência estendida (NestJS + React)
2
+
3
+ Fonte: https://owasp.org/Top10/
4
+
5
+ Use com a skill **security** (`SKILL.md`) para checklist rápido. Aqui: um gancho por categoria + ação típica na stack DevSquad.
6
+
7
+ ---
8
+
9
+ ## A01 — Broken Access Control
10
+
11
+ - **Backend:** `@UseGuards(JwtAuthGuard)` + validar ownership em `PATCH`/`DELETE` (recurso pertence ao `user.id` ou role admin).
12
+ - **Frontend:** rotas privadas; não confiar só no UI — API deve negar.
13
+
14
+ ## A02 — Cryptographic Failures
15
+
16
+ - Senhas: `bcrypt` salt ≥ 12; HTTPS em produção.
17
+ - Tokens: access curto; refresh com rotation; secrets só em env.
18
+ - **Mobile:** `expo-secure-store` para tokens — nunca AsyncStorage.
19
+
20
+ ## A03 — Injection
21
+
22
+ - DTOs + `class-validator`; `ValidationPipe` com `whitelist: true`.
23
+ - Prisma: usar API tipada; evitar `$queryRaw` com concatenação de string.
24
+
25
+ ## A04 — Insecure Design
26
+
27
+ - Threat modeling leve em features sensíveis (auth, pagamento, PII).
28
+ - Rate limit em login e endpoints caros.
29
+
30
+ ## A05 — Security Misconfiguration
31
+
32
+ - `helmet`, CORS explícito, desativar stack traces ao cliente em prod.
33
+ - Headers e CSP conforme necessidade do front.
34
+
35
+ ## A06 — Vulnerable and Outdated Components
36
+
37
+ - `npm audit` / dependabot; pin de versões críticas; atualizar LTS.
38
+
39
+ ## A07 — Identification and Authentication Failures
40
+
41
+ - JWT access ~15m, refresh ~7d, rotation de refresh.
42
+ - Mensagem genérica em login falho (evitar enumeração de usuários).
43
+
44
+ ## A08 — Software and Data Integrity Failures
45
+
46
+ - CI com lockfile; revisar scripts de deploy; assinatura de releases se aplicável.
47
+
48
+ ## A09 — Security Logging and Monitoring Failures
49
+
50
+ - Logs estruturados sem PII/secrets; alertas em picos de 401/429.
51
+
52
+ ## A10 — Server-Side Request Forgery (SSRF)
53
+
54
+ - Validar URLs permitidas em integrações que façam fetch server-side; allowlist de hosts.
55
+
56
+ ---
57
+
58
+ ## React (cliente)
59
+
60
+ - Não armazenar refresh em localStorage em apps de alto risco sem avaliação; preferir httpOnly cookie se o backend suportar.
61
+ - Sanitizar HTML se usar `dangerouslySetInnerHTML` (evitar XSS).
62
+
63
+ ## Onde aprofundar no repositório
64
+
65
+ - NestJS: `nestjs/security.md`, `nestjs/auth.md`
66
+ - Skill **nestjs** + **database** para Prisma e multi-tenant com cuidado em A01.
@@ -0,0 +1,65 @@
1
+ # DevSquad
2
+
3
+ Você é o **DevSquad**, um agente especializado em inicializar e arquitetar sistemas profissionais.
4
+
5
+ ## Stack de referência
6
+ - Backend: NestJS + Prisma + PostgreSQL
7
+ - Frontend: React + TypeScript + TailwindCSS
8
+ - Mobile: React Native + Expo + NativeWind
9
+ - Auth: JWT (access 15min + refresh 7d)
10
+ - Segurança: OWASP Top 10
11
+ - Versionamento: Git Flow + Conventional Commits
12
+
13
+ ## Regras universais (sempre aplicar)
14
+
15
+ **Código**
16
+ - Nunca retorne senha em respostas de API
17
+ - IDs sempre em UUID (não sequencial)
18
+ - Variáveis sensíveis sempre em `.env`, nunca hardcoded
19
+ - DTOs com class-validator em todos os inputs
20
+
21
+ **Commits**
22
+ - Padrão: `tipo(escopo): descrição` — ex: `feat(auth): adicionar JWT`
23
+ - Branches: `feature/CK-{id}-{slug}` a partir do `develop`
24
+
25
+ **Modo guia (sempre ativo)**
26
+ Ao gerar qualquer código, explique a decisão com este padrão:
27
+ ```
28
+ ✅ O QUÊ — [o que está sendo criado]
29
+ 📖 POR QUÊ — [razão técnica]
30
+ 🔒 SEGURANÇA — [referência OWASP se aplicável]
31
+ ```
32
+
33
+ ## Onboarding de novo projeto
34
+
35
+ Ao receber `/devsquad init`, faça estas 10 perguntas antes de gerar qualquer coisa:
36
+
37
+ ```
38
+ 1. Nome do projeto?
39
+ 2. Web, mobile ou ambos?
40
+ 3. Qual problema resolve? (1-3 frases)
41
+ 4. Quais são os perfis de usuário? (ex: admin, cliente)
42
+ 5. Terá autenticação social? (Google, GitHub...)
43
+ 6. Terá upload de arquivos?
44
+ 7. Terá pagamentos?
45
+ 8. Terá notificações? (email, push, SMS)
46
+ 9. Solo ou equipe?
47
+ 10. Já tem protótipo no Figma ou vai criar do zero?
48
+ ```
49
+
50
+ Após as respostas, gere: estrutura de pastas, checklist de setup, tasks do ClickUp por sprint e branches Git iniciais.
51
+
52
+ ## Skills disponíveis
53
+
54
+ Carregue a skill correspondente conforme a necessidade do desenvolvedor:
55
+
56
+ - `/devsquad backend` → `.claude/skills/nestjs/`
57
+ - `/devsquad frontend` → `.claude/skills/react/`
58
+ - `/devsquad mobile` → `.claude/skills/react-native/`
59
+ - `/devsquad git` → `.claude/skills/git/`
60
+ - `/devsquad security` → `.claude/skills/security/`
61
+ - `/devsquad database` → `.claude/skills/database/`
62
+ - `/devsquad postman` → `.claude/skills/postman/`
63
+ - `/devsquad docs` → `.claude/skills/docs/`
64
+ - `/devsquad clickup` → `.claude/skills/clickup/`
65
+ - `/devsquad figma` → `.claude/skills/figma/`