Skip to content

catalog-intake 스킬 추가: Google Forms 제보 검토·수용 자동화 + README 문서화#131

Merged
rkttu merged 1 commit into
mainfrom
feat/catalog-intake-skill
Jul 4, 2026
Merged

catalog-intake 스킬 추가: Google Forms 제보 검토·수용 자동화 + README 문서화#131
rkttu merged 1 commit into
mainfrom
feat/catalog-intake-skill

Conversation

@rkttu

@rkttu rkttu commented Jul 4, 2026

Copy link
Copy Markdown
Member

요약

GitHub에 익숙하지 않은 사용자를 위한 Google Forms 제보검토부터 카탈로그 수용까지 반자동으로 처리하는 Claude Code 스킬 catalog-intake를 추가하고, README.md에 문서화합니다. 그동안 수작업이던 제보 반영을 자동화합니다.

구성 (.claude/skills/catalog-intake/)

파일 역할 런타임
SKILL.md 오케스트레이션 + 판단 규칙 (pull → 정규화 → 검증 → PR) Claude Code
scripts/normalize_csv.py 제보 CSV → 스키마 정규화, Id 파생/충돌 검사, gap 플래그 uv (무의존)
scripts/check_urls.py 설치 URL을 Windows Edge로 위장해 생존/파일명 확인 (봇 차단 우회) uv (httpx)
scripts/fetch_logo.py 공식 로고 → 배경 투명 정사각 PNG uv (pillow/numpy/bs4)
references/field-mapping.md 폼→스키마 매핑, Category, 설치기·사일런트 스위치 시드표 문서

처리 철학 (site-health 워크플로 계승)

검출·정규화는 자동, 최종 판정과 merge는 사람. 결과는 3분류로만 정리하며, "수동 확인 필요"를 이유로 조용히 드롭하지 않습니다.

  • 완전 반영 → PR diff
  • 미완(수동 검토 필요) → 추적 이슈 생성 후 PR에 링크 (유실 방지)
  • 중복/무효 → 사유 기록 후 제외

검증됨

참고

🤖 Generated with Claude Code

Google Forms 제보를 검토·수용까지 반자동 처리하는 catalog-intake 스킬
(.claude/skills/catalog-intake/)을 추가한다. 그동안 수작업이던 제보 반영을 자동화.

- normalize_csv.py: 제보 CSV → 카탈로그 스키마 정규화, Id 파생/충돌 검사, gap 플래그
- check_urls.py: 설치 URL을 Windows Edge로 위장해 생존/파일명 확인 (봇 차단 우회)
- fetch_logo.py: 공식 로고 → 배경 투명 정사각 PNG
- SKILL.md: pull→정규화→검증→PR 오케스트레이션. 결과 3분류(완전반영/미완-추적이슈/중복제외)
- references/field-mapping.md: 폼→스키마 매핑, 설치기·사일런트 스위치 시드표

README.md의 카탈로그 품질 관리 도구 절에 스킬 사용법을 문서화.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 4, 2026 11:37
@rkttu
rkttu merged commit 5813dc0 into main Jul 4, 2026
2 checks passed
@rkttu
rkttu deleted the feat/catalog-intake-skill branch July 4, 2026 11:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Google Forms로 접수된 “식탁보 사이트 추가/수정/삭제 요청” 제보를 CSV로 받아 정규화 → URL 확인(Edge UA 스푸핑) → 로고 생성 → 검증/PR 후보 생성까지 반자동으로 처리하는 catalog-intake Claude Code 스킬을 추가하고, 사용법을 README에 문서화합니다. 기존 수작업 제보 반영 흐름을 site-health 워크플로 철학(자동 처리 + 사람 최종 판정)으로 일관되게 확장하는 변경입니다.

Changes:

  • catalog-intake 스킬 오케스트레이션 문서(SKILL.md) 및 폼→스키마 매핑/시드표 문서 추가
  • CSV 정규화, URL 생존/파일명 확인(Edge 헤더), 로고 자동 수집/투명 정사각 PNG 변환 스크립트 추가
  • README에 제보 인테이크 스킬 개요/절차/참고 링크 추가

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
README.md catalog-intake 스킬 목적/처리 단계/참고 문서를 사용자에게 안내
.claude/skills/catalog-intake/SKILL.md intake 파이프라인, 안전 경계선, 사람/자동 경계, 실행 커맨드 정의
.claude/skills/catalog-intake/scripts/normalize_csv.py Google Forms CSV를 스키마 친화 JSON으로 정규화하고 gap 플래그를 산출
.claude/skills/catalog-intake/scripts/check_urls.py Edge UA/Client Hints로 보안 플러그인 URL의 생존/리다이렉트/파일명/콘텐츠 타입 확인
.claude/skills/catalog-intake/scripts/fetch_logo.py 사이트에서 로고 후보를 수집해 배경 투명화 + 정사각 PNG로 변환
.claude/skills/catalog-intake/references/field-mapping.md 폼 필드 매핑, Category 매핑, gap 의미, 설치기 시드표 문서화

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +173 to +186
display_name_en = get("display_name_en")
category_ko = get("category_ko")
category = CATEGORY_MAP.get(category_ko, "Other")
package_urls = split_package_urls(get("package_urls"))
id_suggested = derive_id(display_name_en)

gaps: list[str] = []
# 폼은 사일런트 스위치를 받지 않으므로 항상 조사 필요
if package_urls:
gaps.append("arguments-missing")
gaps.append("package-name-derive")
if category_ko in CATEGORY_NEEDS_DECISION:
gaps.append("category-needs-decision")
id_collision = bool(id_suggested and id_suggested in existing_ids)
Comment on lines +101 to +121
try:
# HEAD 우선(가벼움). 일부 서버는 HEAD 미지원 → Range GET 으로 폴백.
resp = client.head(url, follow_redirects=True)
if resp.status_code >= 400 or resp.status_code == 405:
resp = client.get(url, follow_redirects=True,
headers={"Range": "bytes=0-0"})
except httpx.HTTPError as exc:
result["error"] = f"{type(exc).__name__}: {exc}"
return result

cd = resp.headers.get("content-disposition")
result.update({
"ok": resp.status_code < 400,
"status": resp.status_code,
"final_url": str(resp.url),
"redirected": str(resp.url) != url,
"content_type": resp.headers.get("content-type", ""),
"content_length": resp.headers.get("content-length", ""),
"filename": filename_from(str(resp.url), cd),
})
return result
Comment on lines +77 to +82
for info in infos:
ip = ipaddress.ip_address(info[4][0])
if (ip.is_private or ip.is_loopback or ip.is_link_local
or ip.is_reserved or ip.is_multicast):
return False, f"blocked-ip: {ip}"
return True, "ok"
Comment on lines +255 to +256
out = trim_and_square(im, args.size)
out.save(args.output, format="PNG", optimize=True)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants