원클릭으로
go-expert
Deep expertise in Go - Gin/Chi patterns, concurrency, error handling, testing
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Deep expertise in Go - Gin/Chi patterns, concurrency, error handling, testing
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
When the user wants help with paid advertising campaigns on Google Ads, Meta (Facebook/Instagram), LinkedIn, Twitter/X, or other ad platforms. Also use when the user mentions 'PPC,' 'paid media,' 'ROAS,' 'CPA,' 'ad campaign,' 'retargeting,' 'audience targeting,' 'Google Ads,' 'Facebook ads,' 'LinkedIn ads,' 'ad budget,' 'cost per click,' 'ad spend,' or 'should I run ads.' Use this for campaign strategy, audience targeting, bidding, and optimization. For bulk ad creative generation and iteration, see ad-creative. For landing page optimization, see cro.
Persistent engineering knowledge vault with semantic search. Save decisions, patterns, debug solutions, and insights as Zettelkasten notes in Obsidian with automatic embedding indexing for semantic retrieval. Use this skill whenever the user says "/memo", "запомни это", "сохрани в базу", "save this", "remember this", "добавь в базу знаний", "log this pattern", or any variation of wanting to persist knowledge. Also trigger for "/memo find", "/memo search", "что я решал по X", "find in vault", "search vault", "найди в базе" to search notes — both keyword and semantic. Trigger for "/memo dedup", "/memo stats", "/memo project", "/memo reindex". This is the bridge between Claude Code sessions and long-term engineering memory across all projects.
Use when starting any new task to select the right GSD mode (fast/quick/plan-phase) and the right model tier (Haiku/Sonnet/Opus) for subagents — prevents 5-10× cost overpay on routine work
Pack the whole repo (or a remote one) into one AI-friendly file via Repomix. Use for architectural reviews, cross-repo handoff, full-codebase audits — not for single-file lookups. Triggers on "pack repo", "feed codebase to AI", "снапшот", "analyze remote repo".
花叔Design(Huashu-Design)——用HTML做高保真原型、交互Demo、幻灯片、动画、设计变体探索+设计方向顾问+专家评审的一体化设计能力。HTML是工具不是媒介,根据任务embody不同专家(UX设计师/动画师/幻灯片设计师/原型师),避免web design tropes。触发词:做原型、设计Demo、交互原型、HTML演示、动画Demo、设计变体、hi-fi设计、UI mockup、prototype、设计探索、做个HTML页面、做个可视化、app原型、iOS原型、移动应用mockup、导出MP4、导出GIF、60fps视频、设计风格、设计方向、设计哲学、配色方案、视觉风格、推荐风格、选个风格、做个好看的、评审、好不好看、review this design、带解说的动画、解说视频、概念解释视频、长视频科普、配音动画、voiceover、narration、TTS+动画、5分钟讲清楚什么是XX。**主干能力**:Junior Designer工作流(先给假设+reasoning+placeholder再迭代)、反AI slop清单、React+Babel最佳实践、Tweaks变体切换、Speaker Notes演示、Starter Components(幻灯片外壳/变体画布/动画引擎/设备边框/解说Stage)、App原型专属守则(默认从Wikimedia/Met/Unsplash取真图、每台iPhone包AppPhone状态管理器可交互、交付前跑Playwright点击测试)、Playwright验证、HTML动画→MP4/GIF视频导出(25fps基础 + 60fps插帧 + palette优化GIF + 6首场景化BGM + 自动fade)、**带解说的长动画pipeline**(豆包TTS生人声+实测时长生timeline.json+NarrationStage驱动画面+ducking混音→交付HTML实播+发布MP4双形态;铁律:整片是一个连续的运动叙事,禁PowerPoint切换)。**需求模糊时的Fallback**:设计方向顾问模式——从5流派×20种设计哲学(Pentagram信息建筑/Field.io运动诗学/Kenya Hara东方极简/Sagmeister实验先锋等)推荐3个差异化方向,展示24个预制showcase(8场景×3风格),并行生成3个视觉Demo让用户选
Use BEFORE coding any new feature, MVP, pricing/billing change, launch, or pivot. Acts as a product validation gate for solo founders — validates target user, JTBD, pain intensity, current alternative, success metric, distribution channel, structural advantage vs competitors, unit economics with SaaS-graveyard gate, cheapest experiment, and top risk. RIGID — do not proceed to technical planning until product context is documented in `.planning/product/<slug>.md` or risk is explicitly accepted. Triggers on "build", "ship", "add feature", "MVP", "launch", "pivot", "pricing", "billing", before /plan, /tdd, /gsd-plan-phase, /gsd-discuss-phase, or when user describes a feature without naming a measurable success metric.
| name | Go Expert |
| description | Deep expertise in Go - Gin/Chi patterns, concurrency, error handling, testing |
This skill provides deep Go expertise including Gin/Chi patterns, goroutines, error handling, table-driven tests, and security best practices.
// ✅ Correct - check and wrap
user, err := repo.GetByID(ctx, id)
if err != nil {
return nil, fmt.Errorf("failed to get user %s: %w", id, err)
}
// ❌ Wrong - ignoring error
user, _ := repo.GetByID(ctx, id) // Never do this!
type AppError struct {
StatusCode int `json:"-"`
Code string `json:"code"`
Message string `json:"message"`
Err error `json:"-"`
}
func (e *AppError) Error() string {
if e.Err != nil {
return fmt.Sprintf("%s: %v", e.Message, e.Err)
}
return e.Message
}
func (e *AppError) Unwrap() error {
return e.Err
}
// Constructors
func ErrNotFound(resource string) *AppError {
return &AppError{
StatusCode: http.StatusNotFound,
Code: "NOT_FOUND",
Message: fmt.Sprintf("%s not found", resource),
}
}
func ErrBadRequest(message string) *AppError {
return &AppError{
StatusCode: http.StatusBadRequest,
Code: "BAD_REQUEST",
Message: message,
}
}
// Check specific error types
func handleError(err error) {
var appErr *AppError
if errors.As(err, &appErr) {
// Handle app error
log.Printf("App error: %s", appErr.Code)
return
}
if errors.Is(err, context.DeadlineExceeded) {
// Handle timeout
log.Println("Request timed out")
return
}
if errors.Is(err, sql.ErrNoRows) {
// Handle not found
return
}
// Unknown error
log.Printf("Unexpected error: %v", err)
}
func ProcessItems(ctx context.Context, items []Item, workers int) error {
jobs := make(chan Item, len(items))
results := make(chan error, len(items))
// Start workers
var wg sync.WaitGroup
for i := 0; i < workers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for item := range jobs {
select {
case <-ctx.Done():
results <- ctx.Err()
return
default:
results <- processItem(ctx, item)
}
}
}()
}
// Send jobs
for _, item := range items {
jobs <- item
}
close(jobs)
// Wait and close results
go func() {
wg.Wait()
close(results)
}()
// Collect errors
var errs []error
for err := range results {
if err != nil {
errs = append(errs, err)
}
}
return errors.Join(errs...)
}
// ✅ Always pass context
func (s *UserService) Create(ctx context.Context, req *CreateUserRequest) (*User, error) {
// Check context before expensive operations
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
// Pass context to all operations
user, err := s.repo.Create(ctx, req)
if err != nil {
return nil, err
}
// For fire-and-forget, use WithoutCancel
go s.sendWelcomeEmail(context.WithoutCancel(ctx), user.Email)
return user, nil
}
// ✅ Handler with timeout
func (h *UserHandler) Create(c *gin.Context) {
ctx, cancel := context.WithTimeout(c.Request.Context(), 5*time.Second)
defer cancel()
user, err := h.service.Create(ctx, &req)
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
c.JSON(http.StatusRequestTimeout, gin.H{"error": "request timeout"})
return
}
// handle other errors
}
}
func FetchAll(ctx context.Context, urls []string) ([]Result, error) {
results := make(chan Result, len(urls))
errs := make(chan error, len(urls))
// Fan-out
for _, url := range urls {
go func(url string) {
result, err := fetch(ctx, url)
if err != nil {
errs <- err
return
}
results <- result
}(url)
}
// Fan-in
var allResults []Result
var allErrs []error
for i := 0; i < len(urls); i++ {
select {
case result := <-results:
allResults = append(allResults, result)
case err := <-errs:
allErrs = append(allErrs, err)
case <-ctx.Done():
return nil, ctx.Err()
}
}
if len(allErrs) > 0 {
return allResults, errors.Join(allErrs...)
}
return allResults, nil
}
func TestValidateEmail(t *testing.T) {
tests := []struct {
name string
email string
wantErr bool
}{
{"valid email", "test@example.com", false},
{"valid with subdomain", "test@mail.example.com", false},
{"missing @", "testexample.com", true},
{"missing domain", "test@", true},
{"empty string", "", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := ValidateEmail(tt.email)
if (err != nil) != tt.wantErr {
t.Errorf("ValidateEmail(%q) error = %v, wantErr %v",
tt.email, err, tt.wantErr)
}
})
}
}
func TestUserService_GetByID(t *testing.T) {
tests := []struct {
name string
userID string
mockSetup func(*MockUserRepository)
want *User
wantErr bool
errContains string
}{
{
name: "returns user when found",
userID: "1",
mockSetup: func(m *MockUserRepository) {
m.On("GetByID", mock.Anything, "1").
Return(&User{ID: "1", Email: "test@example.com"}, nil)
},
want: &User{ID: "1", Email: "test@example.com"},
wantErr: false,
},
{
name: "returns error when not found",
userID: "999",
mockSetup: func(m *MockUserRepository) {
m.On("GetByID", mock.Anything, "999").Return(nil, ErrNotFound)
},
wantErr: true,
errContains: "not found",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockRepo := new(MockUserRepository)
tt.mockSetup(mockRepo)
service := NewUserService(mockRepo)
got, err := service.GetByID(context.Background(), tt.userID)
if tt.wantErr {
require.Error(t, err)
if tt.errContains != "" {
assert.Contains(t, err.Error(), tt.errContains)
}
return
}
require.NoError(t, err)
assert.Equal(t, tt.want, got)
})
}
}
import "github.com/go-playground/validator/v10"
type CreateUserRequest struct {
Email string `json:"email" validate:"required,email"`
Name string `json:"name" validate:"required,min=2,max=100"`
Age *int `json:"age" validate:"omitempty,gte=0,lte=150"`
Password string `json:"password" validate:"required,min=8"`
}
var validate = validator.New()
func (r *CreateUserRequest) Validate() error {
return validate.Struct(r)
}
// In handler
func (h *UserHandler) Create(c *gin.Context) {
var req CreateUserRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if err := req.Validate(); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// proceed with creation
}
// ✅ Parameterized query
rows, err := db.QueryContext(ctx,
"SELECT * FROM users WHERE email = $1",
userInput,
)
// ✅ With sqlx
user := User{}
err := db.GetContext(ctx, &user,
"SELECT * FROM users WHERE id = $1", id)
// ❌ NEVER concatenate
query := fmt.Sprintf("SELECT * FROM users WHERE email = '%s'", userInput) // SQL INJECTION!
// ✅ Validate with go-playground/validator
type Request struct {
Email string `validate:"required,email"`
Age int `validate:"gte=0,lte=150"`
}
// ✅ Custom validation
func validateEmail(email string) error {
if email == "" {
return errors.New("email required")
}
if !strings.Contains(email, "@") {
return errors.New("invalid email format")
}
return nil
}
import "github.com/ulule/limiter/v3"
import mgin "github.com/ulule/limiter/v3/drivers/middleware/gin"
import "github.com/ulule/limiter/v3/drivers/store/memory"
func RateLimitMiddleware() gin.HandlerFunc {
rate := limiter.Rate{
Period: 1 * time.Minute,
Limit: 60,
}
store := memory.NewStore()
instance := limiter.New(store, rate)
return mgin.NewMiddleware(instance)
}
# Development
go run cmd/api/main.go
air # Hot reload
# Testing
go test ./...
go test ./... -v
go test -cover ./...
go test -race ./... # Race detection
# Code Quality
golangci-lint run
golangci-lint run --fix
go fmt ./...
go vet ./...
# Build
go build -o bin/api cmd/api/main.go
go mod tidy