diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f30728b..d1a0c12 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -90,6 +90,8 @@ claude plugin list | grep harness Unlink with `claude plugin unlink harness` when you're done. +> **Authoring a new harness plugin?** See [`docs/plugin-guide.md`](docs/plugin-guide.md) — full walkthrough of plugin anatomy, `plugin.json`/`marketplace.json`, skill/agent layout, local testing, and versioning. + ### Running the meta-skill ```bash diff --git a/PROJECT_MAP.md b/PROJECT_MAP.md index e4dc2fe..c1eb86d 100644 --- a/PROJECT_MAP.md +++ b/PROJECT_MAP.md @@ -1,8 +1,8 @@ # PROJECT_MAP -> 마지막 업데이트: 2026-06-17 +> 마지막 업데이트: 2026-06-23 > 프로젝트: harness — Claude Code Plugin (Team-Architecture Factory) -> 버전: 1.2.0 (plugin.json 기준) +> 버전: 1.3.1 (plugin.json 기준) > 업데이트 방법: "PROJECT_MAP 업데이트해줘" 또는 구조 변경 후 analyst 에이전트 자동 갱신 --- @@ -44,7 +44,7 @@ Claude Code용 **팀 아키텍처 팩토리** 플러그인 저장소. | 파일 | 용도 | |------|------| | `plugin.json` | harness 플러그인 메타 (name, version, author, keywords) | -| `marketplace.json` | 마켓플레이스 등록 정보. 현재 등록 플러그인: `harness` (v1.2.0), `zio-harness` (v1.0.0) | +| `marketplace.json` | 마켓플레이스(`ythong-harness`) 등록 정보. 현재 등록 플러그인: `harness` (v1.3.1), `zio-harness` (v1.0.1) | --- @@ -101,6 +101,7 @@ zio-harness 풀스택 개발 에이전트 팀. 4종 에이전트가 파이프라 |------|------| | `quickstart.md` | 5분 퀵스타트 (마켓플레이스 설치 → 하네스 생성 → 샘플 실행) | | `experimental-dependency.md` | `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` 플래그 설명 및 변경 대응 계획 | +| `plugin-guide.md` | **하네스 플러그인 작성 가이드** — 플러그인 구조·`plugin.json`/`marketplace.json`·스킬/에이전트 레이아웃·로컬 테스트·버전 관리 (작성자용) | --- @@ -150,7 +151,7 @@ zio-harness 풀스택 개발 에이전트 팀. 4종 에이전트가 파이프라 |------|-----| | Claude Code 버전 | v2.x 이상 | | 필수 환경 변수 | `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` | -| 설치 명령 | `claude plugin marketplace add revfactory/harness` | +| 설치 명령(이 저장소) | `claude plugin marketplace add https://git.zioinfo.co.kr/ythong/harness` → `claude plugin install harness@ythong-harness` | --- @@ -158,6 +159,7 @@ zio-harness 풀스택 개발 에이전트 팀. 4종 에이전트가 파이프라 | 날짜 | 변경 내용 | 담당 | |------|----------|------| +| 2026-06-23 | `docs/plugin-guide.md` 신설(하네스 플러그인 작성 가이드) + CONTRIBUTING 포인터 + PROJECT_MAP 현행화(버전 1.3.1·마켓플레이스명·설치명령) | - | | 2026-06-17 | PROJECT_MAP.md 초기 생성 | analyst | | 2026-06-17 | zio-harness 스킬 + 에이전트 4종 신규 생성 | orchestrator | | 2026-04-18 | v1.2.0/1.2.1 릴리즈: 버전 정합성·포지셔닝·CONTRIBUTING·docs/ 신설 | - | diff --git a/docs/gen_intro_deck.py b/docs/gen_intro_deck.py new file mode 100644 index 0000000..bf29d9c --- /dev/null +++ b/docs/gen_intro_deck.py @@ -0,0 +1,319 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Harness 소개 덱 생성 — python-pptx. (스크립트가 진실원천: 내용 수정 후 재생성) +실행: python docs/gen_intro_deck.py → docs/Harness_소개덱_v1.pptx +""" +import os +from pptx import Presentation +from pptx.util import Inches, Pt, Emu +from pptx.dml.color import RGBColor +from pptx.enum.text import PP_ALIGN, MSO_ANCHOR +from pptx.enum.shapes import MSO_SHAPE + +# ── 테마(하네스 브랜드: 딥 인디고 + 퍼플/시안 액센트) ───────────────── +INK = RGBColor(0x0F, 0x14, 0x2E) # 딥 인디고(배경/제목) +INDIGO = RGBColor(0x31, 0x2E, 0x81) # 인디고 +PURPLE = RGBColor(0x7C, 0x3A, 0xED) # 퍼플 액센트 +CYAN = RGBColor(0x06, 0xB6, 0xD4) # 시안 액센트 +AMBER = RGBColor(0xF5, 0x9E, 0x0B) # 앰버 포인트 +LIGHT = RGBColor(0xEE, 0xEC, 0xFB) # 연보라 배경 +CARD = RGBColor(0xF6, 0xF7, 0xFB) +GRAY = RGBColor(0x55, 0x5A, 0x6B) +WHITE = RGBColor(0xFF, 0xFF, 0xFF) +FONT = "맑은 고딕" + +HERE = os.path.dirname(os.path.abspath(__file__)) +ROOT = os.path.dirname(HERE) +BANNER = os.path.join(ROOT, "harness_banner.png") +ICON = os.path.join(ROOT, "harness_icon.png") + +prs = Presentation() +prs.slide_width = Inches(13.333) +prs.slide_height = Inches(7.5) +SW, SH = prs.slide_width, prs.slide_height +BLANK = prs.slide_layouts[6] + + +def _set(run, size, bold=False, color=INK, font=FONT): + run.font.size = Pt(size); run.font.bold = bold + run.font.color.rgb = color; run.font.name = font + + +def box(slide, x, y, w, h, fill=None, line=None, line_w=1.0, round_=False): + shp = slide.shapes.add_shape( + MSO_SHAPE.ROUNDED_RECTANGLE if round_ else MSO_SHAPE.RECTANGLE, + Inches(x), Inches(y), Inches(w), Inches(h)) + shp.shadow.inherit = False + if fill is None: shp.fill.background() + else: shp.fill.solid(); shp.fill.fore_color.rgb = fill + if line is None: shp.line.fill.background() + else: shp.line.color.rgb = line; shp.line.width = Pt(line_w) + return shp + + +def text(slide, x, y, w, h, runs, align=PP_ALIGN.LEFT, anchor=MSO_ANCHOR.TOP, + space=4, line_spacing=None): + """runs: list of paragraphs; each para = list of (txt, size, bold, color).""" + tb = slide.shapes.add_textbox(Inches(x), Inches(y), Inches(w), Inches(h)) + tf = tb.text_frame; tf.word_wrap = True; tf.vertical_anchor = anchor + tf.margin_left = tf.margin_right = Inches(0.05) + tf.margin_top = tf.margin_bottom = Inches(0.02) + for i, para in enumerate(runs): + p = tf.paragraphs[0] if i == 0 else tf.add_paragraph() + p.alignment = align; p.space_after = Pt(space) + if line_spacing: p.line_spacing = line_spacing + for spec in para: + txt, size, bold, color = spec[0], spec[1], spec[2], spec[3] + fnt = spec[4] if len(spec) > 4 else FONT + r = p.add_run(); r.text = txt; _set(r, size, bold, color, fnt) + return tb + + +def header(slide, title, kicker=None, n=None): + """상단 헤더 바 + 제목.""" + box(slide, 0, 0, 13.333, 1.15, fill=INK) + box(slide, 0, 1.15, 13.333, 0.06, fill=PURPLE) + runs = [] + if kicker: + runs.append([(kicker, 12, True, CYAN)]) + runs.append([(title, 26, True, WHITE)]) + text(slide, 0.6, 0.12, 11.5, 0.95, runs, anchor=MSO_ANCHOR.MIDDLE, space=2) + if n is not None: + text(slide, 12.2, 0.12, 0.9, 0.95, [[(f"{n:02d}", 22, True, AMBER)]], + align=PP_ALIGN.RIGHT, anchor=MSO_ANCHOR.MIDDLE) + + +def footer(slide): + text(slide, 0.6, 7.05, 8, 0.35, [[("Harness — Claude Code Team-Architecture Factory", 9, False, GRAY)]]) + text(slide, 11.0, 7.05, 1.9, 0.35, [[("v1.3.1", 9, False, GRAY)]], align=PP_ALIGN.RIGHT) + + +def card(slide, x, y, w, h, title, lines, accent=PURPLE, tsize=15): + box(slide, x, y, w, h, fill=CARD, line=RGBColor(0xE2,0xE3,0xEE), line_w=0.75, round_=True) + box(slide, x, y, 0.12, h, fill=accent) + runs = [[(title, tsize, True, INK)]] + for ln in lines: + runs.append([("· ", 11, True, accent), (ln, 11.5, False, GRAY)]) + text(slide, x+0.3, y+0.22, w-0.45, h-0.4, runs, space=4) + + +# ── 슬라이드 1: 타이틀 ─────────────────────────────────────────────── +s = prs.slides.add_slide(BLANK) +box(s, 0, 0, 13.333, 7.5, fill=INK) +box(s, 0, 0, 0.25, 7.5, fill=PURPLE) +box(s, 0.25, 0, 0.1, 7.5, fill=CYAN) +if os.path.exists(BANNER): + try: s.shapes.add_picture(BANNER, Inches(8.0), Inches(0.7), height=Inches(2.2)) + except Exception: pass +text(s, 0.9, 2.0, 9.5, 2.4, [ + [("Harness", 60, True, WHITE)], + [("Claude Code를 위한 팀 아키텍처 팩토리", 22, True, CYAN)], +], space=10) +text(s, 0.95, 4.5, 11, 1.6, [ + [("도메인 한 문장을 ", 16, False, LIGHT), ("에이전트 팀 + 스킬", 16, True, AMBER), + ("로 변환하는 메타 스킬", 16, False, LIGHT)], + [("\"이 프로젝트용 하네스 구성해줘\" → 전문 에이전트 팀이 자동 생성·협업", 13, False, RGBColor(0xB9,0xBD,0xD6))], +], space=8) +text(s, 0.95, 6.6, 11, 0.5, [[("소개 덱 · 2026-06 · Apache-2.0", 11, False, RGBColor(0x8A,0x8F,0xB0))]]) + +# ── 슬라이드 2: 왜 필요한가 ────────────────────────────────────────── +s = prs.slides.add_slide(BLANK); header(s, "왜 Harness인가?", "THE PROBLEM", 2) +text(s, 0.6, 1.5, 12.1, 1.0, [ + [("복잡한 작업일수록 ", 16, False, GRAY), ("여러 전문 에이전트의 협업", 16, True, INK), + ("이 필요하지만 —", 16, False, GRAY)], +], space=4) +card(s, 0.6, 2.6, 3.95, 3.6, "매번 수작업", [ + "에이전트 역할을 매번 즉석에서 prompt에 작성", + "다음 세션에서 재사용 불가", + "팀 통신·협업 프로토콜이 매번 제각각", +], accent=AMBER) +card(s, 4.75, 2.6, 3.95, 3.6, "구성 일관성 부재", [ + "누가(에이전트) / 어떻게(스킬) 가 뒤섞임", + "패턴 없이 임기응변 → 누락·중복", + "품질이 작성자 역량에 좌우", +], accent=AMBER) +card(s, 8.9, 2.6, 3.85, 3.6, "Harness의 답", [ + "한 문장 → 에이전트 팀 + 스킬 자동 생성", + "파일로 영속화 → 세션 간 재사용", + "6가지 검증된 아키텍처 패턴 적용", +], accent=PURPLE) +footer(s) + +# ── 슬라이드 3: Harness란 ──────────────────────────────────────────── +s = prs.slides.add_slide(BLANK); header(s, "Harness란 무엇인가", "THE FACTORY", 3) +text(s, 0.6, 1.5, 12.1, 1.4, [ + [("Harness는 ", 18, False, GRAY), ("메타 스킬(스킬을 만드는 스킬)", 18, True, PURPLE), + ("입니다.", 18, False, GRAY)], + [("도메인 설명을 입력하면 → 전문 에이전트 팀과 그들이 사용할 스킬 세트를 ", + 14, False, GRAY), ("코드(.md 파일)로 생성", 14, True, INK), ("합니다.", 14, False, GRAY)], +], space=8) +# 플로우 4단계 +labels = [("도메인 한 문장", CYAN), ("팩토리(메타 스킬)", PURPLE), + ("에이전트 팀 + 스킬", INDIGO), ("작업 자동 수행", AMBER)] +x = 0.6 +for i, (lab, col) in enumerate(labels): + box(s, x, 3.3, 2.7, 1.5, fill=col, round_=True) + text(s, x+0.15, 3.3, 2.4, 1.5, [[(lab, 14, True, WHITE)]], + align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) + if i < 3: + text(s, x+2.75, 3.3, 0.5, 1.5, [[("→", 26, True, GRAY)]], + align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) + x += 3.18 +text(s, 0.6, 5.3, 12.1, 1.4, [ + [("핵심: ", 14, True, INK), ("에이전트(누가) 와 스킬(어떻게) 를 분리", 14, True, PURPLE), + ("한다. 에이전트는 역할·협업을, 스킬은 방법·절차를 담는다.", 14, False, GRAY)], + [("→ 다른 도메인에서도 스킬을 재사용하고, 에이전트만 갈아끼울 수 있다.", 13, False, GRAY)], +], space=6) +footer(s) + +# ── 슬라이드 4: 핵심 가치 ──────────────────────────────────────────── +s = prs.slides.add_slide(BLANK); header(s, "핵심 가치 세 가지", "WHY IT WORKS", 4) +card(s, 0.6, 1.6, 3.95, 4.6, "1. 분리(Separation)", [ + "에이전트 = 누가 (역할·원칙·통신)", + "스킬 = 어떻게 (절차·패턴·스크립트)", + "오케스트레이터 = 언제·어떤 순서로", + "→ 책임이 명확, 변경이 국소적", +], accent=CYAN, tsize=16) +card(s, 4.75, 1.6, 3.95, 4.6, "2. 재사용(Reuse)", [ + ".claude/agents·skills 파일로 영속", + "세션이 바뀌어도 그대로 재사용", + "스킬은 여러 에이전트가 공유 가능", + "→ 빌드할수록 자산이 쌓인다", +], accent=PURPLE, tsize=16) +card(s, 8.9, 1.6, 3.85, 4.6, "3. 진화(Evolution)", [ + "고정물이 아니라 살아있는 시스템", + "실행 후 피드백 → 에이전트·스킬 갱신", + "CLAUDE.md 변경 이력으로 추적", + "→ 퇴행 없이 점점 좋아진다", +], accent=AMBER, tsize=16) +footer(s) + +# ── 슬라이드 5: 6가지 아키텍처 패턴 ────────────────────────────────── +s = prs.slides.add_slide(BLANK); header(s, "6가지 팀 아키텍처 패턴", "PATTERNS", 5) +pats = [ + ("파이프라인", "순차 의존 작업 — A→B→C 단계별 전달"), + ("팬아웃/팬인", "병렬 독립 작업 후 결과 통합"), + ("전문가 풀", "상황별로 필요한 전문가만 선택 호출"), + ("생성-검증", "생성 후 독립 에이전트가 품질 검수"), + ("감독자", "중앙 에이전트가 상태 관리·동적 분배"), + ("계층적 위임", "상위가 하위에 재귀적으로 위임"), +] +gx, gy = 0.6, 1.55 +for i, (name, desc) in enumerate(pats): + r, c = divmod(i, 3) + x = gx + c*4.18; y = gy + r*2.65 + card(s, x, y, 3.95, 2.4, name, [desc], accent=[CYAN,PURPLE,INDIGO][c], tsize=16) +footer(s) + +# ── 슬라이드 6: 7-Phase 워크플로우 ─────────────────────────────────── +s = prs.slides.add_slide(BLANK); header(s, "구성 워크플로우 (7 Phase)", "HOW", 6) +phases = [ + ("0", "현황 감사", "기존 에이전트·스킬·CLAUDE.md 확인, drift 감지"), + ("1", "도메인 분석", "작업 유형·기술스택·데이터모델 파악"), + ("2", "팀 아키텍처 설계", "실행 모드(팀/서브/하이브리드) + 패턴 선택"), + ("3", "에이전트 정의", ".claude/agents/*.md (역할·협업·model:opus)"), + ("4", "스킬 생성", ".claude/skills/*/SKILL.md (방법·references)"), + ("5", "통합·오케스트레이션", "오케스트레이터 스킬 + CLAUDE.md 포인터"), + ("6", "검증·테스트", "구조·트리거·드라이런 검증"), +] +y = 1.5 +for num, title, desc in phases: + box(s, 0.6, y, 0.62, 0.62, fill=PURPLE, round_=True) + text(s, 0.6, y, 0.62, 0.62, [[(num, 18, True, WHITE)]], align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) + text(s, 1.45, y, 3.2, 0.62, [[(title, 15, True, INK)]], anchor=MSO_ANCHOR.MIDDLE) + text(s, 4.7, y, 8.0, 0.62, [[(desc, 12.5, False, GRAY)]], anchor=MSO_ANCHOR.MIDDLE) + y += 0.72 +text(s, 0.6, 6.55, 12, 0.4, [[("이후 Phase 7(진화): 매 실행 후 피드백을 반영해 지속 갱신", 12, True, CYAN)]]) +footer(s) + +# ── 슬라이드 7: 설계가 먼저다 ──────────────────────────────────────── +s = prs.slides.add_slide(BLANK); header(s, "설계가 먼저다", "DESIGN FIRST", 7) +text(s, 0.6, 1.5, 12.1, 1.3, [ + [("플러그인은 하네스를 ", 16, False, GRAY), ("배포·증폭하는 그릇", 16, True, INK), + ("일 뿐, 설계를 대신 잘해주지 않는다.", 16, False, GRAY)], + [("허술한 설계는 ", 13.5, False, GRAY), ("잘 배포될수록 더 큰 부채", 13.5, True, AMBER), + ("가 된다 — 결함이 여러 사람·세션·프로젝트로 그대로 퍼진다.", 13.5, False, GRAY)], +], space=6) +box(s, 0.6, 2.95, 5.85, 1.0, fill=CYAN, round_=True) +text(s, 0.8, 2.95, 5.5, 1.0, [[("① 제대로 설계하고 동작·검증한 뒤", 15, True, WHITE)]], anchor=MSO_ANCHOR.MIDDLE) +text(s, 6.5, 2.95, 0.6, 1.0, [[("→", 26, True, GRAY)]], align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) +box(s, 7.2, 2.95, 5.55, 1.0, fill=PURPLE, round_=True) +text(s, 7.4, 2.95, 5.2, 1.0, [[("② 비로소 플러그인으로 묶는다", 15, True, WHITE)]], anchor=MSO_ANCHOR.MIDDLE) +text(s, 0.6, 4.2, 12, 0.4, [[("패키징 전 점검 — 좋은 설계의 4축", 13, True, INK)]]) +axes = [("에이전트 분리", "누가? 역할 겹침·과부하 없게", CYAN), + ("스킬 경계", "어떻게? 절차와 역할 분리", PURPLE), + ("아키텍처 패턴", "6패턴 중 작업에 맞게", INDIGO), + ("트리거 설계", "발동/비발동 정확히", AMBER)] +x = 0.6 +for name, desc, col in axes: + card(s, x, 4.65, 2.92, 1.8, name, [desc], accent=col, tsize=14) + x += 3.07 +footer(s) + +# ── 슬라이드 8: 플러그인 생태계 ────────────────────────────────────── +s = prs.slides.add_slide(BLANK); header(s, "플러그인 생태계", "ECOSYSTEM", 8) +text(s, 0.6, 1.45, 12, 0.7, [ + [("Harness는 ", 15, False, GRAY), ("Claude Code 플러그인 마켓플레이스", 15, True, INK), + ("로 배포됩니다.", 15, False, GRAY)], +]) +card(s, 0.6, 2.3, 6.0, 3.9, "harness (메타 스킬)", [ + "도메인 → 에이전트 팀 + 스킬 자동 생성", + "6가지 아키텍처 패턴 내장", + "설치: harness@ythong-harness", + "용도: 새 하네스를 처음부터 설계", +], accent=PURPLE, tsize=17) +card(s, 6.8, 2.3, 5.95, 3.9, "zio-harness (도메인 플러그인)", [ + "React + Spring Boot + Mobile 풀스택", + "orchestrator·analyst·bot·agent 팀", + "PROJECT_MAP.md로 폴더구조 기억", + "용도: 이미 완성된 풀스택 하네스 즉시 사용", +], accent=CYAN, tsize=17) +text(s, 0.6, 6.45, 12, 0.5, [ + [("직접 도메인 플러그인 작성 → ", 12.5, False, GRAY), ("docs/plugin-guide.md", 12.5, True, PURPLE), + (" 참조", 12.5, False, GRAY)], +]) +footer(s) + +# ── 슬라이드 9: 사용법 ─────────────────────────────────────────────── +s = prs.slides.add_slide(BLANK); header(s, "시작하기 — 3단계", "GET STARTED", 9) +steps = [ + ("1. 마켓플레이스 추가", "claude plugin marketplace add\n https://git.zioinfo.co.kr/ythong/harness"), + ("2. 설치 + 플래그", "claude plugin install harness@ythong-harness\nexport CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1"), + ("3. 한 문장으로 실행", 'claude "핀테크 리스크 평가 팀용 하네스 구성해줘"'), +] +y = 1.6 +for title, code in steps: + box(s, 0.6, y, 12.1, 1.45, fill=CARD, line=RGBColor(0xE2,0xE3,0xEE), line_w=0.75, round_=True) + box(s, 0.6, y, 0.12, 1.45, fill=PURPLE) + text(s, 0.95, y+0.18, 11.5, 0.5, [[(title, 15, True, INK)]]) + text(s, 0.95, y+0.68, 11.5, 0.7, [[(code, 13, False, INDIGO, "Consolas")]]) + y += 1.65 +text(s, 0.6, 6.7, 12, 0.4, [[("요구사항: Claude Code v2.x 이상 · Agent Teams 실험 플래그", 11.5, False, GRAY)]]) +footer(s) + +# ── 슬라이드 9: 마무리 ─────────────────────────────────────────────── +s = prs.slides.add_slide(BLANK) +box(s, 0, 0, 13.333, 7.5, fill=INK) +box(s, 0, 0, 13.333, 0.18, fill=PURPLE) +box(s, 0, 7.32, 13.333, 0.18, fill=CYAN) +if os.path.exists(ICON): + try: s.shapes.add_picture(ICON, Inches(5.9), Inches(0.9), height=Inches(1.7)) + except Exception: pass +text(s, 0.9, 2.9, 11.5, 2.2, [ + [("한 문장으로, 팀을 짓는다.", 34, True, WHITE)], + [("Harness — 도메인을 에이전트 팀과 스킬로.", 18, True, CYAN)], +], align=PP_ALIGN.CENTER, space=12) +text(s, 0.9, 5.4, 11.5, 1.2, [ + [("\"하네스 구성해줘\" · \"build a harness for this project\"", 15, False, LIGHT)], + [("git.zioinfo.co.kr/ythong/harness · Apache-2.0", 12, False, RGBColor(0x9A,0x9F,0xC0))], +], align=PP_ALIGN.CENTER, space=8) + +out = os.path.join(HERE, "Harness_소개덱_v1.pptx") +try: + prs.save(out) +except PermissionError: + out = os.path.join(HERE, "Harness_소개덱_v1.1.pptx") + prs.save(out) + print("(원본 파일이 열려 있어 v1.1로 저장)") +print("생성 완료:", out, "(", len(prs.slides._sldIdLst), "슬라이드 )") diff --git a/docs/plugin-guide.md b/docs/plugin-guide.md new file mode 100644 index 0000000..cea46d6 --- /dev/null +++ b/docs/plugin-guide.md @@ -0,0 +1,246 @@ +# 하네스 플러그인 가이드 + +> 도메인 전용 하네스(에이전트 팀 + 스킬)를 **Claude Code 플러그인**으로 패키징·배포하는 방법. +> 대상: 자신의 도메인(예: 풀스택 개발, 데이터 마이그레이션, 보안 점검)에 맞는 하네스를 만들어 팀·조직에 재사용시키려는 작성자. + +[English](plugin-guide.en.md)(예정) · **한국어** + +--- + +## 0. 두 가지를 먼저 구분하자 + +| 개념 | 무엇 | 예시 | +|------|------|------| +| **harness 메타 스킬** | 도메인 한 문장 → 에이전트 팀 + 스킬을 **생성**하는 팩토리 | `claude "이 프로젝트용 하네스 구성해줘"` | +| **하네스 플러그인** | 특정 도메인에 맞춰 **이미 완성된** 하네스(오케스트레이터 스킬·에이전트·레퍼런스)를 **배포 단위로 묶은 것** | `zio-harness`(React+Spring+모바일 풀스택) | + +이 가이드는 **후자**(완성된 하네스를 플러그인으로 만들기)를 다룹니다. 하네스 자체를 처음 설계하는 방법은 메타 스킬(`skills/harness/SKILL.md`)을 참고하세요. + +**워크플로우 요약:** +``` +① harness 메타 스킬로 하네스 초안 생성 → ② 플러그인 구조로 패키징 → ③ 로컬 link 테스트 → ④ 마켓플레이스 등록·버전 배포 +``` + +--- + +## 원칙 0 — 설계(디자인)가 먼저다 ⚠️ + +**플러그인 작성에서 가장 중요한 건 `plugin.json`도 폴더 구조도 아니라, 그 안에 담길 하네스의 *설계 품질*이다.** + +플러그인은 하네스를 **배포·증폭하는 그릇**일 뿐, 설계를 대신 잘해주지 않는다. 에이전트 분리·스킬 경계·아키텍처 패턴·트리거 description이 허술하면 그 결함이 **그대로 패키징되어 여러 사람·여러 세션·여러 프로젝트로 퍼진다.** 잘못된 설계는 잘 배포될수록 더 큰 부채가 된다. + +그래서 순서를 반드시 지킨다: + +> **① 하네스를 제대로 설계하고(메타 스킬 활용) → 실제 작업으로 동작·검증한 뒤 → ② 비로소 플러그인으로 묶는다.** +> 검증되지 않은 하네스를 먼저 패키징하지 않는다. + +**좋은 설계의 4축(패키징 전에 점검):** +- **에이전트 분리** — "누가"가 명확한가? 역할이 겹치거나 한 에이전트가 너무 많은 일을 하지 않는가? (전문성·병렬성·컨텍스트·재사용성) +- **스킬 경계** — "어떻게"가 에이전트와 분리됐는가? 스킬은 절차·패턴만, 에이전트는 역할·협업만 담는가? +- **아키텍처 패턴** — 6가지 패턴(파이프라인/팬아웃·인/전문가 풀/생성-검증/감독자/계층 위임) 중 작업 성격에 맞는 것을 골랐는가? +- **트리거 설계** — description이 발동/비발동을 정확히 가르는가? 후속 표현까지 포함하는가? + +> 설계 기준의 상세는 메타 스킬의 `references/agent-design-patterns.md`·`skill-writing-guide.md`를 따른다. **이 가이드(아래 §1~)는 "잘 설계된 하네스"를 전제로 그것을 어떻게 플러그인으로 포장·배포하는가**만 다룬다. + +--- + +## 1. 플러그인 해부 (디렉토리 구조) + +이 저장소는 **플러그인 마켓플레이스**입니다. 루트가 마켓플레이스이자 기본 플러그인(`harness`)이고, `plugins/` 아래에 도메인 플러그인이 들어갑니다. + +``` +harness/ ← 마켓플레이스 저장소 루트 +├── .claude-plugin/ +│ ├── marketplace.json ← 마켓플레이스 매니페스트(플러그인 목록) +│ └── plugin.json ← 루트 플러그인(harness) 매니페스트 +├── skills/ +│ └── harness/SKILL.md ← 루트 플러그인이 제공하는 스킬 +└── plugins/ + └── zio-harness/ ← 도메인 플러그인 1개 = 폴더 1개 + ├── .claude-plugin/ + │ └── plugin.json ← 이 플러그인의 매니페스트 + ├── skills/ + │ └── zio-harness/ + │ ├── SKILL.md ← 오케스트레이터 스킬(필수) + │ └── references/ ← 조건부 로딩 참조 문서 + │ ├── orchestrator.md + │ ├── analyst.md + │ ├── react.md + │ └── ... + ├── agents/ ← (선택) 에이전트 정의 .md + └── commands/ ← (선택) 슬래시 커맨드 +``` + +**핵심 규칙: 플러그인 1개 = `plugins//` 폴더 1개**, 그 안에 `.claude-plugin/plugin.json` 1개. 나머지(`skills/`·`agents/`·`commands/`)는 표준 Claude Code 레이아웃을 그대로 따릅니다. + +--- + +## 2. plugin.json — 플러그인 매니페스트 + +각 플러그인은 `/.claude-plugin/plugin.json`을 가집니다. (루트 플러그인은 저장소 루트의 `.claude-plugin/plugin.json`.) + +```json +{ + "name": "zio-harness", + "description": "React + Spring Boot + Mobile App 풀스택 개발 하네스. orchestrator·analyst·bot·agent 에이전트 팀이 기능 개발·테스트·배포를 파이프라인으로 처리.", + "version": "1.0.1", + "author": { "name": "ythong", "url": "https://git.zioinfo.co.kr/ythong" }, + "homepage": "https://git.zioinfo.co.kr/ythong/harness", + "repository": "https://git.zioinfo.co.kr/ythong/harness", + "license": "Apache-2.0", + "keywords": ["harness", "zio-harness", "fullstack", "react", "spring-boot", "mobile", "claude-code-plugin", "multi-agent"] +} +``` + +| 필드 | 필수 | 설명 | +|------|------|------| +| `name` | ✅ | 플러그인 식별자(kebab-case). 설치 시 `@`로 참조됨. 폴더명과 일치 권장. | +| `description` | ✅ | 한 줄 요약. **무엇을·어떤 도메인·어떤 에이전트 구성**인지 구체적으로. 마켓플레이스 목록에 노출. | +| `version` | ✅ | SemVer(`MAJOR.MINOR.PATCH`). 배포마다 올림(§7). | +| `author` | 권장 | `{ name, url }` (이메일은 마켓플레이스 owner에만). | +| `homepage` / `repository` | 권장 | 문서·소스 위치. | +| `license` | 권장 | 예: `Apache-2.0`. | +| `keywords` | 권장 | 검색·분류용. `harness`·`claude-code-plugin`은 공통으로 포함. | + +--- + +## 3. marketplace.json — 마켓플레이스에 등록 + +새 플러그인을 만들면 루트 `.claude-plugin/marketplace.json`의 `plugins[]`에 항목을 추가해야 사용자가 설치할 수 있습니다. + +```json +{ + "name": "ythong-harness", + "owner": { "name": "ythong", "email": "ythong86@gmail.com", "url": "https://git.zioinfo.co.kr/ythong" }, + "plugins": [ + { "name": "harness", "source": "./", "description": "에이전트 팀 & 스킬 아키텍트 메타 스킬.", "version": "1.3.1" }, + { "name": "zio-harness", "source": "./plugins/zio-harness", "description": "React+Spring+모바일 풀스택 하네스.", "version": "1.0.1" } + ] +} +``` + +| 필드 | 설명 | +|------|------| +| `name` | 마켓플레이스 이름(`claude plugin marketplace add` 후 노출). | +| `owner` | `{ name, email, url }`. | +| `plugins[].name` | 플러그인 `plugin.json`의 `name`과 **정확히 일치**. | +| `plugins[].source` | 저장소 내 상대경로. 루트 플러그인은 `"./"`, 서브플러그인은 `"./plugins/"`. | +| `plugins[].description` / `version` | plugin.json과 동기화(불일치 시 혼란). | + +> ⚠️ **버전 3중 동기화**: `plugins//.claude-plugin/plugin.json`, `marketplace.json`의 해당 항목, (있다면) README 뱃지의 버전을 **함께** 올리세요. + +--- + +## 4. 스킬 — 플러그인의 본체 + +하네스 플러그인의 핵심은 **오케스트레이터 스킬** 하나입니다. `/skills//SKILL.md`에 둡니다. + +```markdown +--- +name: zio-harness +description: "React + Spring Boot + Mobile App 풀스택 개발 하네스. (1) 'zio 실행', '풀스택 개발 시작' 요청 시, (2) React/Spring/모바일 개발 요청 시, (3) '프로젝트 분석', 'PROJECT_MAP 업데이트' 시 … 반드시 이 스킬을 사용하라. 다시 실행·재실행·업데이트·보완 요청도 포함." +--- + +# zio-harness — Full-Stack Dev Orchestrator +…본문(워크플로우·에이전트 구성·데이터 흐름)… +``` + +**작성 원칙(요약 — 상세는 메타 스킬의 `skill-writing-guide` 참조):** +- **description은 유일한 트리거**다. "무엇을 한다 + 어떤 표현일 때 발동"을 적극적으로 나열하고, **후속 표현**("다시/재실행/업데이트/보완")을 반드시 포함한다. +- **SKILL.md 본문은 500줄 이내.** 도메인별 세부(예: `react.md`, `database.md`)는 `references/`로 분리하고 본문엔 "언제 이 파일을 읽으라"는 포인터만 남긴다(단계적 정보 공개). +- 반복 실행 코드는 `references/` 옆 `scripts/`에 번들링한다(로딩 없이 실행 가능). + +**에이전트·커맨드(선택):** +- 에이전트 정의가 필요하면 `/agents/.md`에 둔다(역할·원칙·입출력·협업·`model: opus`). +- 슬래시 커맨드가 필요하면 `/commands/.md`. (하네스는 보통 스킬 트리거만으로 충분 — 커맨드는 선택.) + +> zio-harness는 에이전트 정의를 `skills/zio-harness/references/`(orchestrator·analyst·bot·agent·react·mobile·database·playwright)에 두고 오케스트레이터가 필요 시 로딩하는 방식을 씁니다. 어느 쪽이든 일관되게만 하세요. + +--- + +## 5. 새 플러그인 만들기 — 단계별 + +### 5-1. 하네스 초안 생성 (메타 스킬 활용) +먼저 도메인 하네스를 설계합니다. 빈손으로 쓰지 말고 메타 스킬에 맡기세요: +```bash +claude "<도메인> 개발/검증/운영을 위한 하네스 구성해줘" +``` +→ `.claude/agents/`, `.claude/skills/`에 에이전트·스킬 초안이 생깁니다. 이게 플러그인의 원재료입니다. + +### 5-2. 플러그인 폴더로 패키징 +```bash +mkdir -p plugins//.claude-plugin plugins//skills/ +# 초안 스킬을 옮기고 +cp -r .claude/skills//* plugins//skills// +# (에이전트가 별도 .md면) plugins//agents/ 로 +``` +`plugins//.claude-plugin/plugin.json`을 §2 형식으로 작성합니다. + +### 5-3. 마켓플레이스에 등록 +루트 `.claude-plugin/marketplace.json`의 `plugins[]`에 항목 추가(§3). + +### 5-4. 로컬 link 테스트 (배포 전 필수) +```bash +# 실험 플래그(에이전트 팀) 활성화 +export CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 + +# 마켓플레이스 게시 없이 로컬 체크아웃을 연결 +claude plugin link ./plugins/ +claude plugin list | grep # 등록 확인 +``` +새 세션에서 트리거 문장을 입력해 스킬이 발동·동작하는지 확인합니다. 끝나면 `claude plugin unlink `. + +### 5-5. 배포 +버전을 올리고(§7) 커밋·푸시하면, 마켓플레이스를 add한 사용자가 설치할 수 있습니다. + +--- + +## 6. 설치·사용 (사용자 입장) + +```bash +# 1) 마켓플레이스 추가 (저장소 지정) +claude plugin marketplace add ythong/harness # 또는 git URL + +# 2) 플러그인 설치 +claude plugin install zio-harness@ythong-harness + +# 3) 에이전트 팀 플래그 (하네스는 멀티에이전트 API에 의존) +export CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 +``` +이후 트리거 문장(예: "풀스택 개발 시작")으로 발동합니다. 플래그가 필요한 이유는 [`experimental-dependency.md`](./experimental-dependency.md) 참조. + +--- + +## 7. 버전·네이밍 규칙 + +- **이름**: kebab-case, 도메인을 드러내게(`zio-harness`, `sec-audit-harness`). 폴더명 = plugin.json `name` = marketplace 항목 `name`. +- **버전(SemVer)**: + - `PATCH` — 문구·버그·레퍼런스 보강(동작 동일) + - `MINOR` — 에이전트/스킬/레퍼런스 추가(하위호환) + - `MAJOR` — 트리거·워크플로우·산출물 구조의 호환 깨짐 +- 배포 시 **plugin.json + marketplace.json + README 뱃지** 버전을 함께 올린다. +- 변경은 `CHANGELOG.md`에 기록한다(작성자/사용자 모두 추적). + +--- + +## 8. 작성자 체크리스트 + +- [ ] `plugins//.claude-plugin/plugin.json` — name·description·version·license·keywords +- [ ] `marketplace.json`의 `plugins[]`에 항목 추가, `source` 경로·`name` 일치 +- [ ] 오케스트레이터 `SKILL.md` — description이 적극적 트리거 + 후속 표현 포함, 본문 500줄 이내 +- [ ] 도메인 세부는 `references/`로 분리, 본문에 로딩 포인터 +- [ ] 에이전트 정의 파일 존재(빌트인 타입이라도), `model: opus` +- [ ] `commands/`에 불필요한 커맨드 생성 안 함(스킬 트리거 우선) +- [ ] `claude plugin link`로 로컬 발동·동작 검증 +- [ ] should-trigger / should-NOT-trigger 문장으로 트리거 확인(기존 플러그인과 충돌 없는지) +- [ ] 버전 3중 동기화 + CHANGELOG 기록 + +--- + +## 9. 참고 + +- 하네스 설계(에이전트·스킬 패턴): `skills/harness/SKILL.md` 및 그 `references/` +- 빠른 시작(사용자용): [`quickstart.md`](./quickstart.md) +- 실험 플래그 의존성: [`experimental-dependency.md`](./experimental-dependency.md) +- 실제 예시 플러그인: `plugins/zio-harness/` (오케스트레이터 스킬 + 도메인별 레퍼런스 구성) +- 기여 절차: [`../CONTRIBUTING.md`](../CONTRIBUTING.md)