encore-code-review — скилл для Claude Code: ревью Encore.ts
Скилл проверяет код на Encore.ts перед мержем: инфраструктура внутри функций, require() вместо импортов, сырые Error вместо APIError.
npx skills add encoredev/skills --skill encore-code-review -a claude-code
npx skills add encoredev/skills --skill code-review -a claude-codeЧто делает encore-code-review
Скилл включается, когда вы ревьюите пул-реквест или аудит уже написанного кода на Encore.ts. Он проходит по чек-листу и помечает проблемы уровнями [CRITICAL], [WARNING] и [GOOD] с указанием файла и строки. Для свежего кода, который ещё пишется, он не предназначен — там ловить нечего.
Что именно он ищет: объявление SQLDatabase и Topic внутри функций вместо уровня пакета, импорт из чужого сервиса напрямую вместо ~encore/clients, require() вместо ES6-импортов, возврат null вместо throw APIError.notFound(...), конкатенацию строк в SQL вместо шаблонных литералов. Отдельно — неидемпотентные обработчики подписок: при доставке at-least-once повторный вызов может списать деньги дважды, поэтому скилл советует проверять статус перед обработкой. Ещё он смотрит, не вызывается ли secret() на уровне модуля при импорте, и не торчит ли наружу внутренний эндпоинт с expose: true.
Скилл опирается на конкретный стек: Encore.ts, его encore.dev/api, encore.dev/pubsub и клиенты ~encore/clients. Если у вас другой фреймворк, проверки просто не совпадут с вашим кодом. Установка — через npx skills add или gh skill install, файл скилла кладётся в каталог платформы.
Какие задачи решает
- поймать объявление базы или топика внутри функции до мержа
- заменить прямой импорт из чужого сервиса на
~encore/clients - найти возврат
nullтам, где нуженAPIError - проверить обработчики подписок на идемпотентность
- убрать конкатенацию строк в SQL-запросах
Примеры запросов агенту
- проверь этот пул-реквест на анти-паттерны Encore.ts
- сделай аудит сервиса user перед мержем
- прогони чек-лист ревью по файлам в
services/order
Как установить encore-code-review
1Через Skills CLI (Vercel)
npx skills add encoredev/skills --skill encore-code-review -a claude-codenpx skills add encoredev/skills --skill code-review -a claude-code2Вручную: положить файлы
~/.claude/skills/code-review/SKILL.mdПапка скилла целиком; для одного проекта — .claude/skills/code-review/
1Через Skills CLI (Vercel)
npx skills add encoredev/skills --skill code-review -a codex2Вручную: положить файлы
.agents/skills/code-review/SKILL.mdДля всех проектов — ~/.agents/skills/
1Через Skills CLI (Vercel)
npx skills add encoredev/skills --skill code-review -a cursor2Вручную: положить файлы
.cursor/skills/code-review/SKILL.mdCursor читает и .agents/skills/, и .claude/skills/
1GitHub CLI
gh skill install encoredev/skills code-review2Вручную: положить файлы
.github/skills/code-review/SKILL.md1Через Skills CLI (Vercel)
npx skills add encoredev/skills --skill code-review -a gemini-cli2Командой платформы
gemini skills install https://github.com/encoredev/skills --path encore/code-reviewВручную: положить файлы
.devin/skills/code-review/SKILL.mdСтарый путь .windsurf/skills/ тоже читается
Через Skills CLI (Vercel)
npx skills add encoredev/skills --skill code-review -a opencodeClaude.ai
Упакуйте папку в zip и загрузите в Settings → Capabilities → Skills
По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов
Вопросы о encore-code-review
Как установить encore-code-review в Claude?
Выполните npx skills add encoredev/skills --skill encore-code-review -a claude-code. Скилл появится в ~/.claude/skills/code-review/SKILL.md.
Что делает encore-code-review?
Проверяет код на Encore.ts по чек-листу: инфраструктура на уровне пакета, импорты, обработка ошибок, SQL, идемпотентность подписок. Результат — список замечаний с уровнями и ссылками на файл и строку.
На чём скилл сломается?
Он заточен под Encore.ts и его модули encore.dev/api, encore.dev/pubsub, ~encore/clients. На другом фреймворке проверки не совпадут с вашим кодом.
Исходный текст
SKILL.md · Apache-2.0
---
name: encore-code-review
description: Review existing Encore.ts code for best practices and common anti-patterns.
when_to_use: >-
User is reviewing a pull request, auditing existing code, or checking for Encore-specific anti-patterns before merging — infrastructure declared inside functions, missing service files, wrong import paths, raw `Error` thrown instead of `APIError`, untyped APIs. SKIP for greenfield code being actively written. Trigger phrases: "audit", "review", "before merge", "PR review", "anti-patterns", "code smell", "lint this".
---
# Encore Code Review
## Instructions
When reviewing Encore.ts code, check for these common issues:
## Critical Issues
### 1. Infrastructure Inside Functions
```typescript
// WRONG: Infrastructure declared inside function
async function setup() {
const db = new SQLDatabase("mydb", { migrations: "./migrations" });
const topic = new Topic<Event>("events", { deliveryGuarantee: "at-least-once" });
}
// CORRECT: Package level declaration
const db = new SQLDatabase("mydb", { migrations: "./migrations" });
const topic = new Topic<Event>("events", { deliveryGuarantee: "at-least-once" });
```
### 2. Using require() Instead of import
```typescript
// WRONG
const { api } = require("encore.dev/api");
// CORRECT
import { api } from "encore.dev/api";
```
### 3. Wrong Service Import Pattern
```typescript
// WRONG: Direct import from another service
import { getUser } from "../user/api";
// CORRECT: Use ~encore/clients
import { user } from "~encore/clients";
const result = await user.getUser({ id });
```
### 4. Missing Error Handling
```typescript
// WRONG: Returning null for not found
const user = await db.queryRow`SELECT * FROM users WHERE id = ${id}`;
if (!user) return null;
// CORRECT: Throw APIError
import { APIError } from "encore.dev/api";
const user = await db.queryRow`SELECT * FROM users WHERE id = ${id}`;
if (!user) {
throw APIError.notFound("user not found");
}
```
### 5. SQL Injection Risk
```typescript
// WRONG: String concatenation
await db.query(`SELECT * FROM users WHERE email = '${email}'`);
// CORRECT: Template literal with automatic escaping
await db.queryRow`SELECT * FROM users WHERE email = ${email}`;
```
## Warning Issues
### 6. Missing Type Annotations
```typescript
// WEAK: No explicit types
export const getUser = api(
{ method: "GET", path: "/users/:id", expose: true },
async ({ id }) => {
return await findUser(id);
}
);
// BETTER: Explicit request/response types
interface GetUserRequest { id: string; }
interface User { id: string; email: string; name: string; }
export const getUser = api(
{ method: "GET", path: "/users/:id", expose: true },
async ({ id }: GetUserRequest): Promise<User> => {
return await findUser(id);
}
);
```
### 7. Exposed Internal Endpoints
```typescript
// CHECK: Should this cron endpoint be exposed?
export const cleanupJob = api(
{ expose: true }, // Probably should be false
async () => { /* ... */ }
);
```
### 8. Non-Idempotent Subscription Handlers
```typescript
// RISKY: Not idempotent (pubsub has at-least-once delivery)
const _ = new Subscription(orderCreated, "process-order", {
handler: async (event) => {
await chargeCustomer(event.orderId); // Could charge twice!
},
});
// SAFER: Check before processing
const _ = new Subscription(orderCreated, "process-order", {
handler: async (event) => {
const order = await getOrder(event.orderId);
if (order.status !== "pending") return; // Already processed
await chargeCustomer(event.orderId);
},
});
```
### 9. Secrets Called at Module Level
```typescript
// WRONG: Secret accessed at startup
const stripeKey = secret("StripeKey");
const client = new Stripe(stripeKey()); // Called during import
// CORRECT: Access inside functions
const stripeKey = secret("StripeKey");
async function charge() {
const client = new Stripe(stripeKey()); // Called at runtime
}
```
## Review Checklist
- [ ] All infrastructure at package level
- [ ] Using ES6 imports, not require()
- [ ] Cross-service calls use `~encore/clients`
- [ ] Proper error handling with APIError
- [ ] SQL uses template literals
- [ ] Request/response types defined
- [ ] Internal endpoints have `expose: false`
- [ ] Subscription handlers are idempotent
- [ ] Secrets accessed inside functions, not at import time
- [ ] Migrations follow naming convention (001_name.up.sql)
## Output Format
When reviewing, report issues as:
```
[CRITICAL] [file:line] Description of issue
[WARNING] [file:line] Description of concern
[GOOD] Notable good practice observed
```
© encoredev · Apache-2.0 · encoredev/skills