Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions .claude/agents/figma-to-component.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
---
name: figma-to-component
description: Figma 디자인을 Weeth 디자인 시스템에 맞는 React 컴포넌트로 변환. 피그마 URL이나 스크린샷이 주어졌을 때 사용.
tools: Read, Write, Edit, Glob, Grep, Bash
---

Weeth 프로젝트의 디자인 시스템을 완벽히 이해하는 프론트엔드 전문가입니다.
Figma 디자인을 받아 프로젝트 컨벤션에 맞는 컴포넌트를 생성합니다.

## Step 1. 디자인 분석

Figma 속성을 토큰으로 매핑한 표를 먼저 출력합니다:

```
Figma Property | Value | Mapped Token/Class
--------------- | ---------- | -------------------------
Background | #1E2125 | bg-container-neutral
Border Radius | 8px | rounded-lg
Font | Sub1 Bold | typo-sub1 text-text-strong
Padding | 20px | p-500
Gap | 12px | gap-300
```

**토큰 매칭 우선순위:**
1. Tailwind 토큰 클래스 (`bg-container-neutral`, `text-text-strong`)
2. CSS 변수 (`var(--color-primary)`)
3. 신규 토큰 필요 시 → 사용자에게 제안 후 확인

## Step 2. 기존 패턴 확인

`src/components/ui/` 기존 컴포넌트를 먼저 읽어 패턴을 파악합니다.

## Step 3. 컴포넌트 생성

```tsx
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/cn';

const variants = cva('base-styles', {
variants: {
variant: { primary: '...', secondary: '...' },
size: { lg: '...', md: '...', sm: '...' },
},
defaultVariants: { variant: 'primary', size: 'md' },
});

interface Props extends React.HTMLAttributes<HTMLElement>, VariantProps<typeof variants> {}

function Component({ className, variant, size, ...props }: Props) {
return (
<div className={cn(variants({ variant, size }), className)} {...props} />
);
}

export { Component, variants, type Props };
```

**원칙:**
- 하드코딩 값 사용 금지
- `className` 항상 노출
- Radix UI 사용 시 `asChild` 지원
- 생성 후 `src/components/ui/index.ts`에 export 추가

## Step 4. 결과 요약

```
✅ 파일 생성: src/components/ui/ComponentName.tsx
✅ 디자인 토큰: N개 사용
⚠️ 신규 토큰 필요: --token-name (제안값)
```
84 changes: 84 additions & 0 deletions .claude/agents/pr-writer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
name: pr-writer
description: 코드 변경사항을 분석해 PR 템플릿에 맞는 문서를 작성. PR 올리기 전에 사용.
tools: Bash, Read, Glob
---

Weeth 프로젝트의 PR 문서를 작성하는 전문가입니다.
변경된 코드를 분석해 `.github/pull_request_template.md` 형식에 맞는 PR 본문을 생성합니다.

## 분석 순서

### 1. 변경사항 파악

```bash
git diff main...HEAD --stat # 변경된 파일 목록
git log main...HEAD --oneline # 커밋 메시지 목록
git diff main...HEAD # 실제 변경 내용
```

### 2. 브랜치명에서 이슈번호 추출

```bash
git branch --show-current
# 예: feat/WTH-42-button-component → 이슈 #42
```

### 3. PR 유형 판단 기준

| 변경 내용 | PR 유형 |
|----------|---------|
| 새 파일 생성, 새 기능 | 새로운 기능 추가 |
| 버그 수정, 오동작 해결 | 버그 수정 |
| 리팩토링, 구조 변경 | 코드 리팩토링 |
| 오타, 변수명, 탭 사이즈 | 코드에 영향 없는 변경사항 |
| 주석 추가/수정 | 주석 추가 및 수정 |
| README, md 파일 | 문서 수정 |
| package.json, CI 등 | 빌드/패키지 매니저 수정 |
| 파일/폴더 이름 변경 | 파일 혹은 폴더명 수정 |
| 파일/폴더 삭제 | 파일 혹은 폴더 삭제 |

## 출력 형식

분석 후 아래 템플릿을 채워서 출력합니다.
스크린샷 섹션은 사용자가 직접 추가해야 하므로 안내 문구만 남깁니다.

```markdown
## ✅ PR 유형

어떤 변경 사항이 있었나요?

- [x] 새로운 기능 추가 ← 해당하는 항목에 x 표시
- [ ] 버그 수정
- [ ] 코드에 영향을 주지 않는 변경사항(오타 수정, 탭 사이즈 변경, 변수명 변경)
- [ ] 코드 리팩토링
- [ ] 주석 추가 및 수정
- [ ] 문서 수정
- [ ] 빌드 부분 혹은 패키지 매니저 수정
- [ ] 파일 혹은 폴더명 수정
- [ ] 파일 혹은 폴더 삭제

---

### 📌 관련 이슈번호

- Closed #이슈번호 ← 브랜치명 또는 커밋에서 추출, 없으면 생략

---

### ✅ Key Changes

- 변경사항 1
- 변경사항 2

---

### 📸 스크린샷 or 실행영상

<!-- 이해하기 쉽도록 스크린샷을 첨부해주세요. -->

---

## 🎸 기타 사항 or 추가 코멘트

```
107 changes: 107 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
name: CI

on:
pull_request:
branches: [main, develop]

jobs:
ci:
name: Lint & Build
runs-on: ubuntu-latest
permissions:
pull-requests: write

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: latest

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: TypeScript
id: typescript
continue-on-error: true
run: pnpm typecheck

- name: ESLint
id: eslint
continue-on-error: true
run: pnpm lint

- name: Prettier
id: prettier
continue-on-error: true
run: pnpm format:check

- name: Build
id: build
continue-on-error: true
run: pnpm build
Comment on lines +32 to +50
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

모든 단계가 continue-on-error: true이므로 CI가 실패를 차단하지 않습니다.

현재 TypeScript, ESLint, Prettier, Build 모든 단계에 continue-on-error: true가 설정되어 있어, 모든 검증이 실패하더라도 워크플로우는 항상 성공으로 표시됩니다. PR 병합을 차단하는 게이트 역할을 하지 못합니다.

워크플로우 마지막에 최종 판정 단계를 추가하세요:

💡 최종 실패 단계 추가 제안
+      - name: Fail if any check failed
+        if: always()
+        run: |
+          if [[ "${{ steps.typescript.outcome }}" == "failure" || \
+                "${{ steps.eslint.outcome }}" == "failure" || \
+                "${{ steps.prettier.outcome }}" == "failure" || \
+                "${{ steps.build.outcome }}" == "failure" ]]; then
+            echo "One or more checks failed"
+            exit 1
+          fi

이 단계를 PR 코멘트 단계 뒤에 추가하면, 코멘트는 항상 작성되면서도 실패 시 워크플로우가 적절히 실패합니다.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: TypeScript
id: typescript
continue-on-error: true
run: pnpm typecheck
- name: ESLint
id: eslint
continue-on-error: true
run: pnpm lint
- name: Prettier
id: prettier
continue-on-error: true
run: pnpm format:check
- name: Build
id: build
continue-on-error: true
run: pnpm build
- name: TypeScript
id: typescript
continue-on-error: true
run: pnpm typecheck
- name: ESLint
id: eslint
continue-on-error: true
run: pnpm lint
- name: Prettier
id: prettier
continue-on-error: true
run: pnpm format:check
- name: Build
id: build
continue-on-error: true
run: pnpm build
- name: Fail if any check failed
if: always()
run: |
if [[ "${{ steps.typescript.outcome }}" == "failure" || \
"${{ steps.eslint.outcome }}" == "failure" || \
"${{ steps.prettier.outcome }}" == "failure" || \
"${{ steps.build.outcome }}" == "failure" ]]; then
echo "One or more checks failed"
exit 1
fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/ci.yml around lines 32 - 50, Add a final "verdict" step
that runs unconditionally (if: always()) after the PR comment step and inspects
the outcomes of the earlier steps by their IDs (typescript, eslint, prettier,
build); if any of steps.typescript.outcome, steps.eslint.outcome,
steps.prettier.outcome, or steps.build.outcome is not "success", exit non‑zero
to fail the workflow (give the step an id like final-verdict and a clear error
message). This preserves the per-step continue-on-error behavior for
comment/logging but enforces a blocking CI failure if any check actually failed.


- name: PR 검증 결과 코멘트
if: always()
uses: actions/github-script@v7
with:
script: |
const results = {
typescript: '${{ steps.typescript.outcome }}',
eslint: '${{ steps.eslint.outcome }}',
prettier: '${{ steps.prettier.outcome }}',
build: '${{ steps.build.outcome }}',
};

const icon = (outcome) =>
outcome === 'success' ? '✅' : outcome === 'failure' ? '❌' : '⏭️';

const label = (outcome) =>
outcome === 'success' ? '통과' : outcome === 'failure' ? '실패' : '건너뜀';

const allPassed = Object.values(results).every((r) => r === 'success');

const body = [
'## PR 검증 결과',
'',
`${icon(results.typescript)} **TypeScript:** ${label(results.typescript)}`,
`${icon(results.eslint)} **ESLint:** ${label(results.eslint)}`,
`${icon(results.prettier)} **Prettier:** ${label(results.prettier)}`,
`${icon(results.build)} **Build:** ${label(results.build)}`,
'',
allPassed ? '🎉 모든 검증을 통과했습니다!' : '⚠️ 일부 검증에 실패했습니다. 확인 후 수정해주세요.',
].join('\n');

const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});

const existing = comments.find(
(c) => c.user.login === 'github-actions[bot]' && c.body.includes('PR 검증 결과'),
);

if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body,
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body,
});
}
Loading
Loading