encore-go-code-review — скилл для Claude Code: ревью Encore Go
Скилл проверяет Go-код на Encore: инфраструктура внутри функций, забытый context, SQL-инъекции и другие анти-паттерны перед мержем.
npx skills add encoredev/skills --skill encore-go-code-review -a claude-code
npx skills add encoredev/skills --skill go-code-review -a claude-codeЧто делает encore-go-code-review
Скилл включается, когда вы ревьюите пул-реквест, аудите существующий код или ищете специфичные для Encore анти-паттерны перед слиянием. Для нового кода, который ещё пишется, он не нужен.
Внутри — чеклист из девяти пунктов с примерами «как неправильно» и «как правильно». Критичные: инфраструктура объявлена внутри функции вместо уровня пакета, у API-эндпоинта нет context.Context первым параметром, SQL собирается через fmt.Sprintf вместо параметров $1, ответ возвращается значением вместо указателя, ошибки игнорируются. В предупреждениях — непроверенный sqldb.ErrNoRows, публичные внутренние эндпоинты, неидемпотентные обработчики подписок, незакрытые rows без defer rows.Close().
Результат выдаётся строками [CRITICAL], [WARNING] и [GOOD] с указанием файла и строки. Скилл рассчитан именно на Encore Go: вне этого стека проверки вроде errs.B и sqldb.Query смысла не имеют.
Какие задачи решает
- найти инфраструктуру, объявленную внутри функций, и вынести её на уровень пакета
- поймать SQL-инъекцию из-за склейки строки вместо параметров
- проверить, что у каждого API-эндпоинта есть
context.Contextпервым параметром - отловить неидемпотентный обработчик подписки до того, как он спишет деньги дважды
- убедиться, что
rowsзакрываются черезdefer rows.Close()
Примеры запросов агенту
- отревьюй этот PR на анти-паттерны Encore
- проверь код перед мержем: инфраструктура, context, SQL
- сделай аудит пакета на code smell в Encore Go
Как установить encore-go-code-review
1Через Skills CLI (Vercel)
npx skills add encoredev/skills --skill encore-go-code-review -a claude-codenpx skills add encoredev/skills --skill go-code-review -a claude-code2Вручную: положить файлы
~/.claude/skills/go-code-review/SKILL.mdПапка скилла целиком; для одного проекта — .claude/skills/go-code-review/
1Через Skills CLI (Vercel)
npx skills add encoredev/skills --skill go-code-review -a codex2Вручную: положить файлы
.agents/skills/go-code-review/SKILL.mdДля всех проектов — ~/.agents/skills/
1Через Skills CLI (Vercel)
npx skills add encoredev/skills --skill go-code-review -a cursor2Вручную: положить файлы
.cursor/skills/go-code-review/SKILL.mdCursor читает и .agents/skills/, и .claude/skills/
1GitHub CLI
gh skill install encoredev/skills go-code-review2Вручную: положить файлы
.github/skills/go-code-review/SKILL.md1Через Skills CLI (Vercel)
npx skills add encoredev/skills --skill go-code-review -a gemini-cli2Командой платформы
gemini skills install https://github.com/encoredev/skills --path encore/go-code-reviewВручную: положить файлы
.devin/skills/go-code-review/SKILL.mdСтарый путь .windsurf/skills/ тоже читается
Через Skills CLI (Vercel)
npx skills add encoredev/skills --skill go-code-review -a opencodeClaude.ai
Упакуйте папку в zip и загрузите в Settings → Capabilities → Skills
По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов
Вопросы о encore-go-code-review
Как установить encore-go-code-review в Claude?
Через skills CLI: npx skills add encoredev/skills --skill encore-go-code-review -a claude-code. После этого файл скилла лежит в ~/.claude/skills/go-code-review/SKILL.md.
Что делает encore-go-code-review?
Прогоняет существующий Encore Go-код по чеклисту из девяти анти-паттернов и выдаёт замечания с уровнями [CRITICAL], [WARNING] и [GOOD].
Для какого кода скилл не подходит?
Для нового кода, который ещё активно пишется: скилл рассчитан на ревью и аудит готового кода, а не на написание с нуля.
Исходный текст
SKILL.md · Apache-2.0
---
name: encore-go-code-review
description: Review existing Encore Go 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 `errors.New(...)` thrown instead of `errs.B`, untyped APIs, panicking in handlers. SKIP for greenfield code being actively written. Trigger phrases: "audit", "review", "before merge", "PR review", "anti-patterns", "code smell", "lint this".
---
# Encore Go Code Review
## Instructions
When reviewing Encore Go code, check for these common issues:
## Critical Issues
### 1. Infrastructure Inside Functions
```go
// WRONG: Infrastructure declared inside function
func setup() {
db := sqldb.NewDatabase("mydb", sqldb.DatabaseConfig{...})
topic := pubsub.NewTopic[*Event]("events", pubsub.TopicConfig{...})
}
// CORRECT: Package level declaration
var db = sqldb.NewDatabase("mydb", sqldb.DatabaseConfig{
Migrations: "./migrations",
})
var topic = pubsub.NewTopic[*Event]("events", pubsub.TopicConfig{
DeliveryGuarantee: pubsub.AtLeastOnce,
})
```
### 2. Missing Context Parameter
```go
// WRONG: Missing context
//encore:api public method=GET path=/users/:id
func GetUser(params *GetUserParams) (*User, error) {
// ...
}
// CORRECT: Context as first parameter
//encore:api public method=GET path=/users/:id
func GetUser(ctx context.Context, params *GetUserParams) (*User, error) {
// ...
}
```
### 3. SQL Injection Risk
```go
// WRONG: String interpolation
query := fmt.Sprintf("SELECT * FROM users WHERE email = '%s'", email)
rows, err := db.Query(ctx, query)
// CORRECT: Parameterized query
rows, err := sqldb.Query[User](ctx, db, `
SELECT * FROM users WHERE email = $1
`, email)
```
### 4. Wrong Return Types
```go
// WRONG: Returning non-pointer struct
//encore:api public method=GET path=/users/:id
func GetUser(ctx context.Context, params *GetUserParams) (User, error) {
// ...
}
// CORRECT: Return pointer to struct
//encore:api public method=GET path=/users/:id
func GetUser(ctx context.Context, params *GetUserParams) (*User, error) {
// ...
}
```
### 5. Ignoring Errors
```go
// WRONG: Ignoring error
user, _ := sqldb.QueryRow[User](ctx, db, query, id)
// CORRECT: Handle error
user, err := sqldb.QueryRow[User](ctx, db, query, id)
if err != nil {
return nil, err
}
```
## Warning Issues
### 6. Not Checking for ErrNoRows
```go
// RISKY: Returns nil without proper error
func getUser(ctx context.Context, id string) (*User, error) {
user, err := sqldb.QueryRow[User](ctx, db, `
SELECT * FROM users WHERE id = $1
`, id)
if err != nil {
return nil, err // ErrNoRows returns generic error
}
return user, nil
}
// BETTER: Check for not found specifically
import "errors"
func getUser(ctx context.Context, id string) (*User, error) {
user, err := sqldb.QueryRow[User](ctx, db, `
SELECT * FROM users WHERE id = $1
`, id)
if errors.Is(err, sqldb.ErrNoRows) {
return nil, &errs.Error{
Code: errs.NotFound,
Message: "user not found",
}
}
if err != nil {
return nil, err
}
return user, nil
}
```
### 7. Public Internal Endpoints
```go
// CHECK: Should this cron endpoint be public?
//encore:api public method=POST path=/internal/cleanup
func CleanupJob(ctx context.Context) error {
// ...
}
// BETTER: Use private for internal endpoints
//encore:api private
func CleanupJob(ctx context.Context) error {
// ...
}
```
### 8. Non-Idempotent Subscription Handlers
```go
// RISKY: Not idempotent (pubsub has at-least-once delivery)
var _ = pubsub.NewSubscription(OrderCreated, "process-order",
pubsub.SubscriptionConfig[*OrderCreatedEvent]{
Handler: func(ctx context.Context, event *OrderCreatedEvent) error {
return chargeCustomer(ctx, event.OrderID) // Could charge twice!
},
},
)
// SAFER: Check before processing
var _ = pubsub.NewSubscription(OrderCreated, "process-order",
pubsub.SubscriptionConfig[*OrderCreatedEvent]{
Handler: func(ctx context.Context, event *OrderCreatedEvent) error {
order, err := getOrder(ctx, event.OrderID)
if err != nil {
return err
}
if order.Status != "pending" {
return nil // Already processed
}
return chargeCustomer(ctx, event.OrderID)
},
},
)
```
### 9. Not Closing Query Rows
```go
// WRONG: Rows not closed
func listUsers(ctx context.Context) ([]*User, error) {
rows, err := sqldb.Query[User](ctx, db, `SELECT * FROM users`)
if err != nil {
return nil, err
}
// Missing: defer rows.Close()
var users []*User
for rows.Next() {
users = append(users, rows.Value())
}
return users, nil
}
// CORRECT: Always close rows
func listUsers(ctx context.Context) ([]*User, error) {
rows, err := sqldb.Query[User](ctx, db, `SELECT * FROM users`)
if err != nil {
return nil, err
}
defer rows.Close()
var users []*User
for rows.Next() {
users = append(users, rows.Value())
}
return users, rows.Err()
}
```
## Review Checklist
- [ ] All infrastructure at package level
- [ ] All API endpoints have `context.Context` as first parameter
- [ ] SQL uses parameterized queries (`$1`, `$2`, etc.)
- [ ] Response types are pointers
- [ ] Errors are handled, not ignored
- [ ] `sqldb.ErrNoRows` checked where appropriate
- [ ] Internal endpoints use `private` not `public`
- [ ] Subscription handlers are idempotent
- [ ] Query rows are closed with `defer rows.Close()`
- [ ] Migrations follow naming convention (`1_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