harness/docs/gen_install_guide.py
ythong a5154e22bc docs: 설치가이드 전면 리디자인 — 나눔고딕 통일·표 디자인 개선·꽉 찬 레이아웃
- 폰트 나눔고딕 통일 (latin+eastasia 타입페이스 모두 명시)
- 표 재디자인: 네이비 헤더행 + 지브라 행 + 격자선 + 번호/체크/경고 아이콘 원
- 스텝 진행 셰브런(1→2→3)·다크 코드블록 카드·칩·통계 스트립 추가
- zioinfo_team.png(6인 파이프라인) 활용, 슬라이드 여백 제거·내용 보강
- 환경 확인 원라이너·구버전 정리·webhook 설정 등 실무 팁 추가

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 10:06:43 +09:00

463 lines
24 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
zioinfo(ythong 마켓플레이스) 사내전용 설치가이드 생성 — python-pptx.
(스크립트가 진실원천: 내용 수정 후 재생성)
실행: python docs/gen_install_guide.py → docs/zioinfo_사내전용_설치가이드.pptx
디자인 원칙: 여백 없이 꽉 찬 레이아웃 · 카드/아이콘/칩/스텝도형 · 표는 헤더행+지브라 · 폰트 나눔고딕 통일
"""
import os
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from pptx.enum.shapes import MSO_SHAPE
from pptx.oxml.ns import qn
# ── 테마 (ZIO INFOTECH 브랜드: 네이비 + 라이트블루 + 그레이) ──────────
NAVY = RGBColor(0x17, 0x35, 0x7F) # 브랜드 네이비 (배경/제목)
BLUE = RGBColor(0x2E, 0x9B, 0xD6) # 라이트블루 액센트
SKY = RGBColor(0x4F, 0xB3, 0xE8) # 밝은 하늘 포인트
STEEL = RGBColor(0xA7, 0xAB, 0xB0) # 브랜드 그레이
AMBER = RGBColor(0xE8, 0x9A, 0x2A) # 주의 액센트
INK = RGBColor(0x14, 0x2B, 0x66) # 본문 제목 네이비
GRAY = RGBColor(0x55, 0x5F, 0x70) # 본문 회색
LIGHT = RGBColor(0xE2, 0xED, 0xF8) # 연블루 배경
CARD = RGBColor(0xF6, 0xF9, 0xFD)
ZEBRA = RGBColor(0xEE, 0xF4, 0xFB) # 표 지브라 행
GRID = RGBColor(0xD5, 0xE2, 0xF0) # 표 격자선
DARK = RGBColor(0x0E, 0x22, 0x55) # 코드 블록 배경
WHITE = RGBColor(0xFF, 0xFF, 0xFF)
FONT = "나눔고딕" # 전 슬라이드 폰트 통일 (latin+ea)
HERE = os.path.dirname(os.path.abspath(__file__))
ROOT = os.path.dirname(HERE)
BANNER = os.path.join(ROOT, "zioinfo_banner.png")
ICON = os.path.join(ROOT, "zioinfo_icon.png")
TEAM = os.path.join(ROOT, "zioinfo_team.png")
prs = Presentation()
prs.slide_width = Inches(13.333)
prs.slide_height = Inches(7.5)
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
# 한글(eastasia) 폰트까지 명시해야 나눔고딕 통일이 실제 적용됨
rPr = run._r.get_or_add_rPr()
ea = rPr.find(qn('a:ea'))
if ea is None:
ea = rPr.makeelement(qn('a:ea'), {}); rPr.append(ea)
ea.set('typeface', 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):
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 shape_text(shp, label, size, bold, color, align=PP_ALIGN.CENTER):
tf = shp.text_frame; tf.word_wrap = True
tf.margin_left = tf.margin_right = Inches(0.03)
tf.margin_top = tf.margin_bottom = 0
tf.vertical_anchor = MSO_ANCHOR.MIDDLE
p = tf.paragraphs[0]; p.alignment = align
r = p.add_run(); r.text = label; _set(r, size, bold, color)
def icon_circle(slide, x, y, d, glyph, fill=BLUE, fg=WHITE, size=13):
shp = slide.shapes.add_shape(MSO_SHAPE.OVAL, Inches(x), Inches(y), Inches(d), Inches(d))
shp.shadow.inherit = False
shp.fill.solid(); shp.fill.fore_color.rgb = fill
shp.line.fill.background()
shape_text(shp, glyph, size, True, fg)
return shp
def chip(slide, x, y, w, h, label, fill, fg=WHITE, size=11, bold=True, line=None):
shp = box(slide, x, y, w, h, fill=fill, line=line, line_w=1.0, round_=True)
shape_text(shp, label, size, bold, fg)
return shp
def header(slide, title, kicker=None, n=None):
box(slide, 0, 0, 13.333, 1.15, fill=NAVY)
box(slide, 0, 1.15, 13.333, 0.06, fill=BLUE)
runs = []
if kicker:
runs.append([(kicker, 12, True, SKY)])
runs.append([(title, 25, 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, SKY)]],
align=PP_ALIGN.RIGHT, anchor=MSO_ANCHOR.MIDDLE)
def footer(slide):
text(slide, 0.6, 7.06, 9, 0.35, [[("ZIO INFOTECH · ythong 마켓플레이스 사내전용 설치가이드", 9, False, GRAY)]])
text(slide, 11.0, 7.06, 1.9, 0.35, [[("2026-07", 9, False, GRAY)]], align=PP_ALIGN.RIGHT)
def card(slide, x, y, w, h, title, lines, accent=BLUE, tsize=14, glyph="", lsize=11.5):
"""아이콘 원 + 좌측 액센트 바 + 불릿 라인 카드. lines 항목이 list면 런 스펙으로 그대로 사용."""
box(slide, x, y, w, h, fill=CARD, line=GRID, line_w=0.75, round_=True)
box(slide, x, y, 0.1, h, fill=accent)
icon_circle(slide, x + 0.26, y + 0.19, 0.36, glyph, fill=accent, size=13)
text(slide, x + 0.74, y + 0.17, w - 0.92, 0.45, [[(title, tsize, True, INK)]])
runs = []
for ln in lines:
if isinstance(ln, list):
runs.append(ln)
else:
runs.append([("", lsize - 1, True, accent), (ln, lsize, False, GRAY)])
text(slide, x + 0.34, y + 0.66, w - 0.58, h - 0.82, runs, space=4)
def codebox(slide, x, y, w, h, title, code_lines):
"""제목 + 다크 코드 블록 카드."""
box(slide, x, y, w, h, fill=CARD, line=GRID, line_w=0.75, round_=True)
box(slide, x, y, 0.1, h, fill=NAVY)
icon_circle(slide, x + 0.26, y + 0.15, 0.36, "", fill=NAVY, size=12)
text(slide, x + 0.74, y + 0.13, w - 0.92, 0.4, [[(title, 13.5, True, INK)]])
ch = h - 0.72
box(slide, x + 0.3, y + 0.56, w - 0.6, ch, fill=DARK, round_=True)
runs = [[("", 12.5, True, SKY), (ln, 12.5, True, WHITE)] for ln in code_lines]
text(slide, x + 0.58, y + 0.56, w - 1.1, ch, runs, anchor=MSO_ANCHOR.MIDDLE, space=3)
def table_head(slide, x, y, col_ws, labels, h=0.5):
box(slide, x, y, sum(col_ws), h, fill=NAVY)
cx = x
for w, lab in zip(col_ws, labels):
if lab:
text(slide, cx + 0.16, y, w - 0.24, h, [[(lab, 12.5, True, WHITE)]],
anchor=MSO_ANCHOR.MIDDLE)
cx += w
def table_row(slide, x, y, col_ws, cells, h, zebra=False):
box(slide, x, y, sum(col_ws), h, fill=(ZEBRA if zebra else WHITE), line=GRID, line_w=0.5)
cx = x
for w, cell in zip(col_ws, cells):
if cell:
text(slide, cx + 0.16, y, w - 0.26, h, cell, anchor=MSO_ANCHOR.MIDDLE, space=2)
cx += w
cx = x
for w in col_ws[:-1]:
cx += w
box(slide, cx, y, 0.014, h, fill=GRID)
def chevrons(slide, y, items, current=None, x=0.6, total_w=12.13, h=0.55, gap=0.08):
"""스텝 진행 도형. current 지정 시 완료=NAVY/현재=BLUE/예정=LIGHT, 미지정 시 전체 NAVY."""
n = len(items); w = (total_w - gap * (n - 1)) / n
for i, lab in enumerate(items):
if current is None:
fill, fg = NAVY, WHITE
elif i == current:
fill, fg = BLUE, WHITE
elif i < current:
fill, fg = NAVY, WHITE
else:
fill, fg = LIGHT, GRAY
shp = slide.shapes.add_shape(MSO_SHAPE.CHEVRON, Inches(x + i * (w + gap)),
Inches(y), Inches(w), Inches(h))
shp.shadow.inherit = False
shp.fill.solid(); shp.fill.fore_color.rgb = fill
shp.line.fill.background()
shape_text(shp, lab, 11.5, True, fg)
def stat(slide, x, y, w, h, num, label, accent=BLUE):
box(slide, x, y, w, h, fill=CARD, line=GRID, line_w=0.75, round_=True)
text(slide, x, y + 0.06, w, 0.42, [[(num, 19, True, accent)]], align=PP_ALIGN.CENTER)
text(slide, x, y + 0.46, w, 0.3, [[(label, 10.5, True, GRAY)]], align=PP_ALIGN.CENTER)
STEPS = ["STEP 1 · 마켓플레이스 등록", "STEP 2 · 설치 + 리로드", "STEP 3 · 설치 검증"]
# ── 1. 타이틀 ────────────────────────────────────────────────────────
s = prs.slides.add_slide(BLANK)
box(s, 0, 0, 13.333, 7.5, fill=NAVY)
box(s, 0, 0, 0.25, 7.5, fill=BLUE)
box(s, 0.25, 0, 0.1, 7.5, fill=SKY)
if os.path.exists(BANNER):
try: s.shapes.add_picture(BANNER, Inches(8.15), Inches(0.55), height=Inches(2.3))
except Exception: pass
text(s, 0.9, 1.75, 7.2, 2.2, [
[("사내전용 설치가이드", 50, True, WHITE)],
[("ythong 마켓플레이스 · zioinfo 통합 플러그인 v2.1.0", 20, True, SKY)],
[("ZIO INFOTECH Suite — 4개 트랙 · 에이전트 16 · 스킬 8 · 커맨드 3", 14, False, RGBColor(0xCF, 0xE4, 0xF5))],
], space=10)
bar = box(s, 0.9, 4.3, 11.53, 0.95, fill=DARK, line=SKY, line_w=1.0, round_=True)
text(s, 1.25, 4.3, 10.9, 0.95, [
[("", 16, True, SKY), ("/plugin install zioinfo@ythong", 16, True, WHITE),
("", 14, True, STEEL), ("/reload-plugins", 16, True, WHITE),
(" 설치 2줄이면 끝", 13, False, SKY)],
], anchor=MSO_ANCHOR.MIDDLE)
chips = ["① 풀스택 개발 (zio-harness)", "② 하네스 팩토리", "③ 제안서 자동완성", "④ PM·PMO · LLM wiki"]
cw = (11.53 - 3 * 0.13) / 4
for i, c in enumerate(chips):
chip(s, 0.9 + i * (cw + 0.13), 5.6, cw, 0.6, c, RGBColor(0x1F, 0x4A, 0x9E), fg=WHITE, size=11.5, line=SKY)
text(s, 0.9, 6.55, 11.5, 0.5, [[("ZIO INFOTECH · 사내 배포용 · 외부 공유 금지", 11, False, RGBColor(0x8A, 0x9F, 0xC0))]])
# ── 2. 개요: 무엇이 설치되나 (표 + 통계 스트립) ──────────────────────
s = prs.slides.add_slide(BLANK); header(s, "무엇이 설치되나 — zioinfo 통합 플러그인 (4개 트랙)", "OVERVIEW", 2)
col_ws = [0.75, 2.35, 2.75, 6.28]
table_head(s, 0.6, 1.42, col_ws, ["", "트랙", "진입점 · 트리거", "무엇을 해주나"], h=0.5)
rows = [
("1", BLUE, "풀스택 개발", "zio-harness 스킬\n\"기능 추가해줘\"",
[[("React + Spring Boot + Mobile 6인 팀 — analyst→designer→agent→bot→hermes", 11, False, GRAY)],
[("hermes 4대 특징: ", 11, True, INK), ("memory · skill 자가축적 · cron · External APIs(Gitea·webhook)", 11, False, GRAY)]]),
("2", NAVY, "하네스 팩토리", "harness 스킬\n\"하네스 구성해줘\"",
[[("도메인 한 문장 → 에이전트 팀 + 스킬 + CLAUDE.md 자동 생성", 11, False, GRAY)],
[("6개 팀 아키텍처 패턴(파이프라인·전문가 풀·감독자 등) 중 자동 선택", 11, False, GRAY)]]),
("3", SKY, "제안서", "/zioinfo:proposal\n\"제안서 작성해줘\"",
[[("RFP + PPT 디자인 템플릿 → 제안서 자동완성 (SI·SM 주력)", 11, False, GRAY)],
[("전문가 7인 + 아키텍처 풀 · PPTX 빌드 스크립트(inspect_template·build_deck)", 11, False, GRAY)]]),
("4", STEEL, "PM · PMO", "/zioinfo:pmo\n/zioinfo:wiki",
[[("계획(WBS·M/M) → 리스크 → 진척보고(EVM) → 감리 대응 → 포트폴리오 (8인)", 11, False, GRAY)],
[("LLM wiki(/zioinfo:wiki) · graphify 지식그래프 SessionStart 자동 분석", 11, False, GRAY)]]),
]
y = 1.92
for i, (num, accent, track, entry, desc) in enumerate(rows):
entry_runs = [[(ln, 11, True, BLUE)] for ln in entry.split("\n")]
table_row(s, 0.6, y, col_ws, [None, [[(track, 13.5, True, INK)]], entry_runs, desc],
h=1.02, zebra=(i % 2 == 1))
icon_circle(s, 0.6 + (0.75 - 0.42) / 2, y + (1.02 - 0.42) / 2, 0.42, num, fill=accent, size=14)
y += 1.02
stats = [("8", "스킬", BLUE), ("16", "에이전트", NAVY), ("3", "커맨드", SKY), ("1", "SessionStart 훅", BLUE), ("1", "ZIO WISE 테마", STEEL)]
sw = (12.13 - 4 * 0.13) / 5
for i, (num, lab, accent) in enumerate(stats):
stat(s, 0.6 + i * (sw + 0.13), 6.14, sw, 0.78, num, lab, accent)
footer(s)
# ── 3. 사전 준비 ─────────────────────────────────────────────────────
s = prs.slides.add_slide(BLANK); header(s, "사전 준비", "PREREQUISITES", 3)
card(s, 0.6, 1.42, 5.95, 2.2, "필수", [
"Claude Code v2.x 이상 — claude --version 으로 확인",
"사내 Gitea 접근 가능 (git.zioinfo.co.kr)",
"git 클라이언트 설치",
"(권장) SSH 키 ~/.ssh/id_ed25519 를 Gitea 계정에 등록",
], accent=NAVY, tsize=15, glyph="")
card(s, 6.78, 1.42, 5.95, 2.2, "선택 (기능별)", [
"Agent Teams 팀 모드: CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1",
"제안서 트랙 PPTX 생성: pip install python-pptx",
"지식그래프·LLM wiki: graphify — SessionStart 훅이 자동 설치",
"끄기 스위치: ZIO_HARNESS_NO_GRAPHIFY=1",
], accent=BLUE, tsize=15, glyph="")
card(s, 0.6, 3.77, 12.13, 1.95, "권한 · 보안 안내", [
"마켓플레이스는 사내 Gitea 저장소(ythong/harness) 기준 — 외부 공개 저장소 아님",
"플러그인 산출물에 자격증명·내부 IP 기재 금지 (하네스 공통 보안 원칙)",
"설치 범위는 기본 user scope — 개인 PC 단위 설치·관리 · hermes webhook URL은 /plugin configure 로만 입력",
], accent=AMBER, tsize=15, glyph="!")
codebox(s, 0.6, 5.85, 12.13, 1.05, "환경 확인 한 줄 (전부 통과하면 준비 끝)", [
"claude --version ; git --version ; ssh -T git@git.zioinfo.co.kr",
])
footer(s)
# ── 4. STEP 1: 마켓플레이스 등록 ─────────────────────────────────────
s = prs.slides.add_slide(BLANK); header(s, "STEP 1 — 마켓플레이스 등록 (최초 1회)", "INSTALL", 4)
chevrons(s, 1.36, STEPS, current=0)
codebox(s, 0.6, 2.1, 12.13, 1.3, "Claude Code 세션 안에서", [
"/plugin marketplace add https://git.zioinfo.co.kr/ythong/harness.git",
])
codebox(s, 0.6, 3.52, 12.13, 1.3, "또는 터미널에서 (PowerShell·bash 동일)", [
"claude plugin marketplace add https://git.zioinfo.co.kr/ythong/harness.git",
])
card(s, 0.6, 4.97, 5.95, 1.9, "주의 — URL 끝 .git 필수", [
"비-GitHub git 저장소는 .git 없으면 등록 실패",
"\"expected object, received string\" 오류 시 URL 재확인",
], accent=AMBER, tsize=14, glyph="!")
card(s, 6.78, 4.97, 5.95, 1.9, "성공 확인", [
[("", 10.5, True, BLUE), ("Successfully added marketplace: ythong", 11.5, True, INK)],
"/plugin marketplace list 로 등록 상태 재확인",
], accent=BLUE, tsize=14, glyph="")
footer(s)
# ── 5. STEP 2: 플러그인 설치 ─────────────────────────────────────────
s = prs.slides.add_slide(BLANK); header(s, "STEP 2 — 플러그인 설치 + 리로드", "INSTALL", 5)
chevrons(s, 1.36, STEPS, current=1)
codebox(s, 0.6, 2.1, 12.13, 1.5, "설치는 하나면 끝 (세션 안 2줄)", [
"/plugin install zioinfo@ythong",
"/reload-plugins",
])
card(s, 0.6, 3.75, 3.955, 1.65, "구버전 정리 (4→1 통합)", [
"구 4개 플러그인 사용 PC는 재설치 전",
"claude plugin uninstall <이름>@ythong",
], accent=AMBER, tsize=13, glyph="!", lsize=10.5)
card(s, 4.685, 3.75, 3.955, 1.65, "webhook 알림 (선택)", [
"/plugin configure zioinfo@ythong",
"notify_webhook_url 입력 — 비우면 알림만 생략",
], accent=SKY, tsize=13, glyph="", lsize=10.5)
card(s, 8.77, 3.75, 3.955, 1.65, "즉시 반영", [
"/reload-plugins 한 번이면 현재 세션 반영",
"Claude Code 재시작 불필요",
], accent=NAVY, tsize=13, glyph="", lsize=10.5)
card(s, 0.6, 5.55, 12.13, 1.32, "성공 메시지", [
[("", 11, True, BLUE), ("Successfully installed plugin: zioinfo@ythong (scope: user)", 12, True, INK),
(" — 커맨드 자동완성에 /zioinfo:pmo 가 보이면 로드 완료", 11.5, False, GRAY)],
], accent=BLUE, tsize=14, glyph="")
footer(s)
# ── 6. STEP 3: 설치 검증 ─────────────────────────────────────────────
s = prs.slides.add_slide(BLANK); header(s, "STEP 3 — 설치 검증", "VERIFY", 6)
chevrons(s, 1.36, STEPS, current=2)
codebox(s, 0.6, 2.1, 5.95, 1.45, "설치 목록·상태", [
"claude plugin list",
])
codebox(s, 6.78, 2.1, 5.95, 1.45, "컴포넌트 인벤토리", [
"claude plugin details zioinfo@ythong",
])
col_ws6 = [2.3, 9.83]
table_head(s, 0.6, 3.7, col_ws6, ["항목", "정상 판정 기준 (zioinfo v2.1.0 실측)"], h=0.48)
verify_rows = [
("Skills 8", [[("harness · hermes-delivery · pm-pmo-orchestrator · pmo · proposal · proposal-builder-orchestrator · wiki · zio-harness", 11, False, GRAY)]]),
("Agents 16\nHooks 1", [[("hermes(전령) + proposal 7인 + pm·pmo 8인 / SessionStart graphify 훅 / 오류 0", 11, False, GRAY)]]),
("커맨드 3", [[("/zioinfo:pmo · /zioinfo:proposal · /zioinfo:wiki — 세션 자동완성에 보이면 로드 완료", 11, False, GRAY)]]),
]
y = 4.18
for i, (item, desc) in enumerate(verify_rows):
item_runs = [[(" " + ln, 12, True, NAVY)] for ln in item.split("\n")]
table_row(s, 0.6, y, col_ws6, [item_runs, desc], h=0.88, zebra=(i % 2 == 1))
icon_circle(s, 0.72, y + (0.88 - 0.3) / 2, 0.3, "", fill=BLUE, size=10)
y += 0.88
footer(s)
# ── 7. 설치 후 바로 쓰기 ─────────────────────────────────────────────
s = prs.slides.add_slide(BLANK); header(s, "설치 후 바로 쓰기 — 트리거 한 마디", "USAGE", 7)
if os.path.exists(TEAM):
try:
s.shapes.add_picture(TEAM, Inches(0.6), Inches(1.42), width=Inches(4.6))
except Exception: pass
text(s, 0.6, 4.02, 4.6, 0.45, [
[("zio-harness 6인 파이프라인", 11, True, INK)],
[("analyst→designer→agent→bot→hermes", 10, False, GRAY)],
], align=PP_ALIGN.CENTER, space=1)
card(s, 5.45, 1.42, 7.28, 2.98, "풀스택 개발 (zio-harness 트랙)", [
"\"기능 추가해줘\" · \"버그 수정해줘\" · \"코드 분석해줘\" — 한 마디로 팀 가동",
"\"push하고 배포 확인해줘\" → hermes가 Gitea push·배포 확인·릴리즈 노트",
"\"매주 릴리즈 노트 예약해줘\" → cron 정기작업 + webhook 알림",
"\"PROJECT_MAP 업데이트해줘\" · Playwright E2E · DB MCP 작업",
"파이프라인: 분석 → 디자인(UI 시) → 구현 → 검증(bot PASS 게이트) → 전달",
], accent=BLUE, tsize=15, glyph="")
card(s, 0.6, 4.55, 3.955, 2.3, "하네스 팩토리", [
"\"이 프로젝트용 하네스 구성해줘\"",
"에이전트 팀 + 스킬 + CLAUDE.md 자동 생성",
"6개 팀 아키텍처 패턴 중 자동 선택",
], accent=NAVY, tsize=13.5, glyph="", lsize=10.5)
card(s, 4.685, 4.55, 3.955, 2.3, "제안서", [
"/zioinfo:proposal ○○사업 제안서",
"RFP 분석→전략→아키텍처→본문→PPTX→QA",
"시스템 구축(SI)·유지보수(SM) 제안 주력",
], accent=SKY, tsize=13.5, glyph="", lsize=10.5)
card(s, 8.77, 4.55, 3.955, 2.3, "PM·PMO + LLM wiki", [
"/zioinfo:pmo 프로젝트 계획·주간보고·감리",
"/zioinfo:wiki → graphify-out/wiki/ 코드 위키",
"AI 의뢰서(스티치 design.md·젠스파크) 생성",
], accent=STEEL, tsize=13.5, glyph="", lsize=10.5)
footer(s)
# ── 8. 업데이트 · 제거 ───────────────────────────────────────────────
s = prs.slides.add_slide(BLANK); header(s, "업데이트 · 제거", "MAINTAIN", 8)
codebox(s, 0.6, 1.42, 5.95, 2.0, "업데이트 (마켓플레이스 갱신 후 재설치)", [
"claude plugin marketplace update ythong",
"claude plugin install zioinfo@ythong",
"/reload-plugins",
])
codebox(s, 6.78, 1.42, 5.95, 2.0, "제거", [
"claude plugin uninstall zioinfo@ythong",
"claude plugin marketplace remove ythong (선택)",
])
chevrons(s, 3.6, ["marketplace update", "install zioinfo", "/reload-plugins", "plugin list 확인"], h=0.55)
card(s, 0.6, 4.35, 5.95, 2.5, "버전 정책", [
"plugins/zioinfo/.claude-plugin/plugin.json 이 버전 진실원천",
"Semantic Versioning — 현재 v2.1.0 (hermes 4대 특징)",
"marketplace.json 버전은 plugin.json과 동기 유지",
], accent=BLUE, tsize=14, glyph="")
card(s, 6.78, 4.35, 5.95, 2.5, "변경 이력 · 문의", [
"변경 이력: 저장소 CLAUDE.md 하네스 섹션 참조",
"카탈로그: docs/plugins.md",
"설치 문서: plugins/zioinfo/docs/INSTALL.md",
"문의: ZIO INFOTECH 인프라팀",
], accent=NAVY, tsize=14, glyph="")
footer(s)
# ── 9. 트러블슈팅 (표) ───────────────────────────────────────────────
s = prs.slides.add_slide(BLANK); header(s, "트러블슈팅", "FAQ", 9)
col_ws9 = [4.35, 7.78]
table_head(s, 0.6, 1.42, col_ws9, ["증상", "원인 · 조치"], h=0.5)
rows = [
("plugin not found", "마켓플레이스 add 여부 확인 → /plugin marketplace add ... (.git 포함)"),
("expected object, received string", "비-GitHub git URL에 .git 누락 — URL 끝에 .git 붙여 재시도"),
("invalid manifest", "plugins/zioinfo/.claude-plugin/plugin.json 존재·JSON 유효성 확인 (BOM 없는 UTF-8)"),
("설치했는데 커맨드가 안 보임", "/reload-plugins 실행 또는 세션 재시작"),
("스킬이 트리거되지 않음", "명시적 표현 사용(\"제안서 작성\", \"프로젝트 계획\") 또는 /zioinfo:proposal·pmo 직접 호출"),
("팀 모드가 동작 안 함", "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 환경변수 확인"),
]
y = 1.92
for i, (prob, fix) in enumerate(rows):
table_row(s, 0.6, y, col_ws9,
[[[(" " + prob, 11.5, True, NAVY)]],
[[("", 11, True, BLUE), (fix, 11, False, GRAY)]]],
h=0.82, zebra=(i % 2 == 1))
icon_circle(s, 0.74, y + (0.82 - 0.3) / 2, 0.3, "!", fill=AMBER, size=10)
y += 0.82
footer(s)
# ── 10. 마무리 ───────────────────────────────────────────────────────
s = prs.slides.add_slide(BLANK)
box(s, 0, 0, 13.333, 7.5, fill=NAVY)
box(s, 0, 0, 13.333, 0.18, fill=BLUE)
box(s, 0, 7.32, 13.333, 0.18, fill=SKY)
if os.path.exists(ICON):
try: s.shapes.add_picture(ICON, Inches(5.92), Inches(0.75), height=Inches(1.5))
except Exception: pass
text(s, 0.9, 2.5, 11.5, 1.6, [
[("설치 2줄, 팀이 도착한다.", 34, True, WHITE)],
], align=PP_ALIGN.CENTER)
bar = box(s, 2.7, 3.6, 7.93, 0.85, fill=DARK, line=SKY, line_w=1.0, round_=True)
shape_text(bar, "/plugin install zioinfo@ythong · /reload-plugins", 17, True, SKY)
cw = (11.53 - 3 * 0.13) / 4
for i, c in enumerate(["풀스택 개발 6인 팀", "하네스 팩토리", "제안서 자동완성", "PM·PMO · LLM wiki"]):
chip(s, 0.9 + i * (cw + 0.13), 4.9, cw, 0.6, c, RGBColor(0x1F, 0x4A, 0x9E), fg=WHITE, size=12, line=SKY)
text(s, 0.9, 5.95, 11.5, 1.1, [
[("문의: ZIO INFOTECH 인프라팀 · 저장소: git.zioinfo.co.kr/ythong/harness", 14, False, RGBColor(0xCF, 0xE4, 0xF5))],
[("사내 배포용 · 외부 공유 금지 · Apache-2.0", 12, False, RGBColor(0x9A, 0xAF, 0xD0))],
], align=PP_ALIGN.CENTER, space=8)
out = os.path.join(HERE, "zioinfo_사내전용_설치가이드.pptx")
try:
prs.save(out)
except PermissionError:
out = os.path.join(HERE, "zioinfo_사내전용_설치가이드-1.pptx")
prs.save(out)
print("(원본 파일이 열려 있어 -1로 저장)")
print("생성 완료:", out, "(", len(prs.slides._sldIdLst), "슬라이드 )")