Skip to content
Merged
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
27 changes: 27 additions & 0 deletions .claude/agents/issue-implementer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
name: issue-implementer
description: GitHub 이슈 하나를 처음부터 끝까지(브랜치 생성→구현→테스트→Draft PR) 격리된 컨텍스트에서 처리한다. 사용자가 이슈 번호를 주고 구현을 요청하거나, 여러 이슈를 병렬로 처리해야 할 때 사용한다.
tools: Read, Edit, Write, Bash, Grep, Glob
---

당신은 jump-section 저장소(TypeScript 스크롤 라이브러리, pnpm+turbo 모노레포)의 이슈 구현
전담 에이전트입니다. `.claude/skills/implement-issue/SKILL.md`에 정의된 절차를 그대로 따르되,
격리된 컨텍스트에서 이슈 하나를 처음부터 끝까지 완결합니다.

## 절차

1. `gh issue view <번호> --json number,title,body,labels,comments`로 이슈를 조회한다.
2. 이슈 내용을 읽고 관련 패키지(core/react/vue/svelte)와 수정 대상 파일을 파악한다.
3. `ai/issue-{번호}-{짧은-설명}` 브랜치를 생성한다.
4. CLAUDE.md와 `.claude/skills/pr-fix-conventions/SKILL.md`의 규칙을 준수하며 구현한다.
- 코드, 테스트(80% 이상 커버리지), 문서(`docs/`), 타입을 모두 함께 변경한다.
- 이슈에 명시되지 않은 설계 결정은 임의로 판단하지 말고 PR 본문에 질문으로 남긴다.
5. `pnpm build && pnpm test:coverage`를 실행하고, 커버리지가 80% 미달이면 테스트를 보완해 재실행한다.
6. `gh pr create --draft --title "[AI] feat: {이슈 제목}" --body "..."`로 Draft PR을 생성한다.
본문 하단에 `Closes #{이슈번호}`와 `🤖 Generated by jump-section AI Pipeline`을 반드시 포함한다.

## 안전 규칙

- `main`, `master`, `develop`, `release/**` 브랜치에는 절대 커밋/푸시하지 않는다.
- `--force`/`--force-with-lease` 푸시를 사용하지 않는다.
- 작업 종료 시 coverage 결과 테이블을 요약해 보고한다.
31 changes: 31 additions & 0 deletions .claude/agents/pr-comment-fixer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
name: pr-comment-fixer
description: 열려 있는 PR의 빌드 에러, Prettier 포맷, 리뷰 코멘트를 사람이 직접 시킬 때(슬래시 커맨드 없이 로컬에서) 처리한다. Hermes의 fix-build/fix-lint/fix-review-comment 스킬과 동일한 절차를 사람이 Claude Code에서 실행할 때 사용한다.
tools: Read, Edit, Write, Bash, Grep, Glob
---

당신은 jump-section 저장소의 PR 자동 수정 에이전트입니다. `/fix-build`, `/fix-lint`, `/fix` 슬래시
커맨드가 n8n→Hermes 경로로 처리하는 것과 **동일한 절차**를, 사람이 Claude Code 세션에서 직접 요청할
때 로컬에서 수행합니다. `.claude/skills/pr-fix-conventions/SKILL.md`의 규칙을 반드시 따릅니다 —
Hermes 쪽 `.hermes/skills/`의 같은 이름 스킬들과 결과가 달라지면 안 됩니다.

## 절차

1. 사용자가 지정한 PR 번호와 작업 종류(빌드 에러 수정 / 포맷 수정 / 리뷰 코멘트 반영)를 확인한다.
2. `gh pr view <번호>`로 PR의 head 브랜치를 확인하고 체크아웃한다.
3. 작업 종류에 맞춰 원인을 조사한다.
- **빌드 에러**: `gh pr checks <번호>` 또는 CI 로그로 실패 원인을 확인한다.
- **포맷**: `pnpm format`을 실행해 Prettier 규칙을 적용한다.
- **리뷰 코멘트**: 코멘트 본문과 `diff_hunk`를 읽고 의도를 파악한다. 의도가 불명확하면 수정하지
않고 왜 건너뛰는지 코멘트로 남긴다.
4. 수정 후 `pnpm format && pnpm build && pnpm test`가 모두 통과하는지 확인한다. 실패하면 커밋하지
않는다.
5. head 브랜치에 커밋한다(새 브랜치를 만들지 않는다). 커밋 메시지는 `fix: ...` 또는
`chore: ...` 형식.
6. `gh pr comment <번호>`로 변경 파일 목록과 커밋 SHA(7자리)를 보고한다.

## 안전 규칙

- 대상 브랜치가 `main`, `master`, `develop`, `release/**` 패턴이면 즉시 중단하고 사용자에게 알린다.
- `--force`/`--force-with-lease` 푸시를 사용하지 않는다.
- 여러 파일 수정이 필요하면 한 번에 모아서 커밋한다(부분 커밋 금지).
63 changes: 0 additions & 63 deletions .claude/commands/implement-issue.md

This file was deleted.

44 changes: 44 additions & 0 deletions .claude/scripts/block-protected-push.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env bash
# PreToolUse hook: 보호 브랜치(main/master/develop/release/**)로의 git push를 차단한다.
# CLAUDE.md "AI 파이프라인 거버넌스" 공통 규칙 — Hermes 쪽은 scripts/hermes-safe-push.sh가 동일하게 강제한다.
set -euo pipefail

INPUT=$(cat)

if command -v jq &>/dev/null; then
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // ""')
else
CMD=$(echo "$INPUT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('tool_input',{}).get('command',''))" 2>/dev/null || echo "")
fi

# git push가 포함되지 않은 명령은 통과
if [[ "$CMD" != *"git push"* ]]; then
exit 0
fi

# --force / --force-with-lease / -f는 무조건 차단
if [[ "$CMD" == *"--force"* || "$CMD" =~ \ -f(\ |$) ]]; then
echo "[block-protected-push] --force/--force-with-lease/-f 푸시는 허용되지 않습니다." >&2
exit 2
fi

PROTECTED_PATTERN='^(main|master|develop|release/.*)$'

# "git push <remote> <branch>" 형태에서 옵션(-u, --tags 등)을 제외한 마지막 인자를 브랜치로 간주
CLEANED_CMD=$(echo "$CMD" | grep -oE 'git push[^&|;]*' | head -1 | tr ' ' '\n' | grep -v '^-' | tr '\n' ' ')
TARGET_BRANCH=$(echo "$CLEANED_CMD" | awk '{print $NF}')

# refspec (예: local:remote) 형태인 경우 remote 브랜치만 추출
TARGET_BRANCH=${TARGET_BRANCH#*:}

# 브랜치가 명시되지 않은 "git push"/"git push origin" 형태면 현재 체크아웃된 브랜치를 확인
if [[ -z "$TARGET_BRANCH" || "$TARGET_BRANCH" == "push" || "$TARGET_BRANCH" == "origin" ]]; then
TARGET_BRANCH=$(git branch --show-current 2>/dev/null || echo "")
fi

if [[ "$TARGET_BRANCH" =~ $PROTECTED_PATTERN ]]; then
echo "[block-protected-push] 보호 브랜치(${TARGET_BRANCH})에는 직접 push할 수 없습니다. PR을 통해 머지하세요." >&2
exit 2
fi

exit 0
11 changes: 11 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash .claude/scripts/block-protected-push.sh"
}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write",
Expand Down
75 changes: 75 additions & 0 deletions .claude/skills/implement-issue/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
---
name: implement-issue
description: GitHub 이슈를 읽고 jump-section 컨벤션에 맞춰 구현한 뒤 Draft PR을 생성한다. 사용자가 "이슈 N번 구현해줘", "이 이슈 처리해줘" 등 이슈 기반 구현을 요청할 때 사용한다.
metadata:
author: jump-section
version: '1.0.0'
---

# 이슈 구현

GitHub 이슈를 조회해 jump-section 저장소 컨벤션에 맞춰 구현하고 Draft PR을 생성한다. 이슈 번호가
주어지지 않으면 열린 이슈 목록을 먼저 보여주고 어떤 이슈를 구현할지 확인한다.

이 스킬의 절차는 `.claude/agents/issue-implementer.md` 에이전트가 격리된 컨텍스트에서 그대로
수행할 수도 있다 — 단발성으로 직접 실행할 때는 이 스킬을, 여러 이슈를 병렬로 처리하거나 메인
세션 컨텍스트를 아끼고 싶을 때는 해당 에이전트를 사용한다.

## 실행 절차

### 1. 이슈 조회

- 이슈 번호가 주어진 경우: `gh issue view <번호> --json number,title,body,labels,comments`
- 번호가 없는 경우: `gh issue list --limit 20 --json number,title,labels,assignees` 로 목록을
보여주고 어떤 이슈를 구현할지 물어본다.

### 2. 컨텍스트 파악

이슈 내용을 읽고 관련 파일을 파악한다:

- 어떤 패키지(core/react/vue/svelte)에 해당하는지
- 기존 코드에서 수정/추가가 필요한 파일
- 관련 테스트 파일

### 3. 브랜치 생성

```
ai/issue-{번호}-{짧은-설명}
```

예: `ai/issue-42-add-scroll-offset`

### 4. 구현

CLAUDE.md의 규칙을 준수하며 구현한다. `.claude/skills/pr-fix-conventions/SKILL.md`에 정리된
Prettier/TypeScript/테스트+문서+타입 동반 규칙을 그대로 따른다.

> **구현 원칙**: 이슈의 의도가 명확하고, 어떤 코드를 어떻게 수정해야 할지 확신이 있는 부분만
> 변경한다. 불확실한 설계 결정이나 이슈에 명시되지 않은 동작 변경은 임의로 판단하지 말고, PR
> 본문 또는 이슈 코멘트에 질문으로 남긴다.

- **코드** 수정/추가
- **테스트** 케이스 추가 또는 업데이트 (80% 이상 커버리지)
- **문서** `docs/` 해당 페이지 반영 (새 옵션/메서드/동작 변경 포함)
- **타입** Public API 변경 시 인터페이스/타입 정의 업데이트

구현 완료 후 아래 순서로 확인한다:

```bash
pnpm build
pnpm test:coverage
```

`pnpm test:coverage` 결과에서 coverage 테이블을 그대로 출력하고, 80% 미달 항목이 있으면 테스트를
보완한 뒤 다시 실행한다. 최종적으로 coverage 결과를 대화창에 코드블록으로 붙여넣는다.

### 5. Draft PR 생성

```
gh pr create --draft --title "[AI] feat: {이슈 제목}" --body "..."
```

PR 본문 하단에 반드시 포함:

- `Closes #{이슈번호}`
- `🤖 Generated by jump-section AI Pipeline`
41 changes: 41 additions & 0 deletions .claude/skills/pr-fix-conventions/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
name: pr-fix-conventions
description: jump-section PR의 코드를 자동으로 고칠 때(빌드 에러, 린트, 리뷰 코멘트 반영) 지켜야 할 공통 규칙. issue-implementer, pr-comment-fixer 에이전트와 Hermes의 fix-build/fix-lint/fix-review-comment 스킬이 공유하는 기준점이다.
metadata:
author: jump-section
version: '1.0.0'
---

# PR 수정 컨벤션

기존 PR의 코드를 자동으로 고치는 모든 작업(빌드 에러 수정, Prettier 포맷 수정, 리뷰 코멘트 반영)이
지켜야 할 규칙이다. 이 문서는 `.hermes/skills/pr-fix-conventions/SKILL.md`와 내용을 동기화한다 — 한쪽을
고치면 다른 쪽도 반드시 같이 갱신한다.

## 브랜치 규칙

- **오직 PR의 head 브랜치에만** 커밋한다. `main`, `master`, `develop`, `release/**` 패턴에는 절대
커밋/푸시하지 않는다.
- `--force`/`--force-with-lease` 푸시는 절대 하지 않는다 — 항상 fast-forward 1커밋만 추가한다.

## 코드 컨벤션 (CLAUDE.md 발췌)

- TypeScript **strict mode** 준수, `any` 타입 사용 금지(불가피하면 주석으로 이유 명시)
- Prettier: 2 스페이스 들여쓰기, 세미콜론, 싱글쿼트, `printWidth: 100`, `trailingComma: 'all'`
- Public API는 반드시 TypeScript 인터페이스/타입으로 명시, optional 필드는 기본값 제공
- 기존 Public API 하위 호환성 유지 — 시그니처 제거/변경 금지

## 수정 원칙

- 원인이 명확하고 수정 방향이 확실한 경우에만 수정한다. 불확실하면 해당 파일은 건너뛰고, PR
코멘트에 왜 건너뛰었는지 남긴다.
- 수정이 여러 파일에 걸쳐 필요하면 한 번에 모아서 커밋한다(부분 커밋 금지).
- 수정 후 반드시 `pnpm format && pnpm build && pnpm test`가 통과하는 것을 확인한 뒤에만 커밋한다.
통과하지 못하면 커밋하지 않고 실패 상태를 그대로 보고한다.

## 완료 보고

- 커밋 메시지: `fix: ...` 또는 `chore: ...` (Conventional Commits, 한국어/영어 무관)
- PR 코멘트/답장에는 변경 파일 목록과 커밋 SHA(짧게 7자리)를 포함한다.
- `🤖 Generated by jump-section AI Pipeline`를 명시한다(신규 PR을 만드는 경우에 한함 — 기존 PR에
커밋만 추가하는 경우는 커밋 메시지/코멘트로 충분하며 이 문구를 강제하지 않는다).
49 changes: 6 additions & 43 deletions .github/workflows/slash-fix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,52 +85,15 @@ jobs:
env:
EVENT_PAYLOAD: ${{ toJSON(github.event) }}
EVENT_NAME: ${{ github.event_name }}
WEBHOOK_SECRET: ${{ secrets.GITHUB_WEBHOOK_SECRET }}
run: |
SIGNATURE="sha256=$(printf '%s' "$EVENT_PAYLOAD" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" | sed 's/^.* //')"
curl -s -X POST "${{ secrets.N8N_WEBHOOK_URL }}/webhook/comment-fix" \
-H "Content-Type: application/json" \
-H "X-GitHub-Event: ${EVENT_NAME}" \
-H "X-Hub-Signature-256: ${SIGNATURE}" \
-d "${EVENT_PAYLOAD}"

- name: ✅ 완료 반응 추가
if: steps.detect.outputs.skip == 'false' && success()
uses: actions/github-script@v7
with:
script: |
const isReview = context.eventName === 'pull_request_review_comment';
if (isReview) {
await github.rest.reactions.createForPullRequestReviewComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: context.payload.comment.id,
content: 'rocket',
});
} else {
await github.rest.reactions.createForIssueComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: context.payload.comment.id,
content: 'rocket',
});
}

- name: ❌ 실패 반응 추가
if: steps.detect.outputs.skip == 'false' && failure()
uses: actions/github-script@v7
with:
script: |
const isReview = context.eventName === 'pull_request_review_comment';
if (isReview) {
await github.rest.reactions.createForPullRequestReviewComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: context.payload.comment.id,
content: '-1',
});
} else {
await github.rest.reactions.createForIssueComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: context.payload.comment.id,
content: '-1',
});
}
# 완료(🚀)/실패(-1) 반응은 더 이상 여기서 달지 않는다. n8n이 웹훅을 "접수"한 것과 Hermes가
# 실제로 수정을 "완료"한 것은 별개이므로(비동기), 정확한 결과 반응은
# .n8n/workflows/hermes-fix-callback.json이 Hermes의 완료 콜백을 받은 뒤에 단다.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@ coverage/
# n8n
.n8n/.env
.n8n/n8n_data/

# hermes
.hermes/.env
.hermes/hermes_data/
Loading
Loading