genetics-mcp 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +43 -0
- package/genetics.mjs +2141 -0
- package/package.json +23 -0
- package/skills/genetics-report/SKILL.md +290 -0
- package/skills/genetics-report/assets/example/cases.json +24 -0
- package/skills/genetics-report/assets/example/report.json +55 -0
- package/skills/genetics-report/assets/generate-illustrations.py +159 -0
- package/skills/genetics-report/assets/qa.py +183 -0
- package/skills/genetics-report/assets/render.py +161 -0
- package/skills/genetics-report/assets/subset_font.py +93 -0
- package/skills/genetics-report/assets/visual-baseline.py +459 -0
- package/skills/genetics-report/references/content-pipeline.md +891 -0
- package/skills/genetics-report/references/design-system.md +733 -0
- package/skills/genetics-report/references/font-embedding.md +161 -0
- package/skills/genetics-report/references/illustrations.md +473 -0
- package/skills/genetics-report/references/lim-portal-format.md +272 -0
- package/skills/genetics-report/references/process.md +634 -0
- package/skills/genetics-report/references/qa.md +722 -0
- package/skills/genetics-report/references/textbook-mode.md +111 -0
- package/skills/genetics-report/references/wysiwyg.md +751 -0
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "genetics-mcp",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "문서 포털 발행·세션 이식·사례 기억 MCP — 데이터를 주면 규격 문서가 나온다 (짝인 스킬 동봉)",
|
|
5
|
+
"author": "주식회사 LIM (LIM Inc.)",
|
|
6
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"bin": {
|
|
9
|
+
"genetics-mcp": "./genetics.mjs"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"genetics.mjs",
|
|
13
|
+
"LICENSE",
|
|
14
|
+
"skills"
|
|
15
|
+
],
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=18"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"test": "node smoke.mjs",
|
|
21
|
+
"prepack": "find skills -name __pycache__ -type d -prune -exec rm -rf {} + 2>/dev/null || true"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: genetics-report
|
|
3
|
+
description: |
|
|
4
|
+
Lim 문서 포털(cs.lim.software)에 발행하거나 단일 HTML 로 전달하는 고객 문서를 만듭니다 —
|
|
5
|
+
수정 내역 보고, 검수 요청, 진행 현황, 장애 사후 보고, 납품 보고, 기술보고서·인수인계.
|
|
6
|
+
다음과 같은 요청 시 사용하세요:
|
|
7
|
+
- "이번에 고친 것들 고객사에 보낼 보고서로 정리해줘" / "Lim 에 보고서 올려줘"
|
|
8
|
+
- "수정 내역을 케이스별로 정리해서 문서로 만들어줘"
|
|
9
|
+
- "고객이 우리가 뭘 했는지 모른다, 정리해서 보내야 한다"
|
|
10
|
+
- "검수 요청 문서 만들어줘" / "장애 사후 보고서 / 릴리스 노트를 고객용으로"
|
|
11
|
+
- "기술보고서/인수인계/온보딩 문서/포스트모템 만들어줘" (→ 교과서 모드: 원리→붕괴→처방→적용→연습)
|
|
12
|
+
내용 설계(독자 정의 → 등급 → 고객 언어 치환)가 본체이고, 전달은 둘 중 하나입니다:
|
|
13
|
+
Lim MCP 발행(서버 렌더, 링크 공유) 또는 단일 HTML(폰트 임베드·위지윅 편집기·자동 검수).
|
|
14
|
+
[책임 경계] vs moai-officer:doc-pptx: 저 스킬=발표용 .pptx 파일, 이 스킬=읽고 검수하는 문서. 색·타이포·아키타입 체계는 doc-pptx 를 계승합니다.
|
|
15
|
+
version: "2.0.0"
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# Lim 보고서 생성 (genetics-report)
|
|
19
|
+
|
|
20
|
+
## 개요
|
|
21
|
+
|
|
22
|
+
**개발 내부 기록**을 **고객이 읽고 검수할 수 있는 문서**로 바꿉니다.
|
|
23
|
+
이 둘은 같은 사실을 다루지만 완전히 다른 문서입니다. 이 스킬의 본체는
|
|
24
|
+
**무엇을 빼고 무엇을 고쳐 쓰는가**(내용 설계)이고, 전달 형태는 그 다음입니다.
|
|
25
|
+
|
|
26
|
+
## 렌더러는 레포에 하나뿐이다 (2026-08-18)
|
|
27
|
+
|
|
28
|
+
`assets/` 에 `build.py`·`report.css`·`palette.py`·`editor.js` 사본이 있었고 **지웠다.**
|
|
29
|
+
|
|
30
|
+
커밋 `f1a2725`(08-13) 가 SSOT 를 레포로 옮겼는데 옛 사본을 안 지웠다. 그 뒤 레포만
|
|
31
|
+
나흘 자라(20,361 → 51,754B · 887줄 차이) **이 스킬로 만든 고객 문서에 브랜드 서식이
|
|
32
|
+
통째로 빠졌다** — `cover-frame`·`ch-head`·`cindex`·`eof-endcard`·`Spoqa` 가 전부 0회.
|
|
33
|
+
`lim-report.css` 는 스킬에 존재하지도 않았다. 둘을 대조하는 관문이 없어 아무도 몰랐다.
|
|
34
|
+
|
|
35
|
+
**여기에 렌더러 파일을 다시 두지 마라.** `render.py` 가 그걸 감지하면 멈춘다.
|
|
36
|
+
고칠 것은 레포(`private/builder/`)에서 고치고 배포한다.
|
|
37
|
+
|
|
38
|
+
## 전달 경로 — 먼저 고른다
|
|
39
|
+
|
|
40
|
+
| 경로 | 언제 | 방법 |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| **Lim 포털 발행 · LIM 서식** (고객사에 나가는 브랜드 문서) | 링크로 열람시키고, 일러스트와 챕터가 있는 정식 고객 보고서 | `publish_report` + `template: "lim-report"` · `palette: "lim"` → **`references/lim-portal-format.md` 필독** |
|
|
43
|
+
| 단일 HTML 파일 | 메일 첨부·사내망 열람·Lim 밖 고객, 또는 교과서 모드처럼 포털 서식에 없는 구성 | `assets/render.py` — **레포 렌더러를 부른다**(사본 없음). `--template lim-report` 로 브랜드 서식도 낸다. 외부 리소스 0, 폰트 임베드, 위지윅 편집기 포함 |
|
|
44
|
+
|
|
45
|
+
> **도구 이름은 접두사 없이 적는다.** 실제로는 `mcp__<설정키>__publish_report` 로 보이는데,
|
|
46
|
+
> 그 `<설정키>` 는 **사람이 MCP 를 붙일 때 정한 값**이라 기계마다 다르다. 여기 박아 두면
|
|
47
|
+
> 이 스킬은 패키지에 실려 **자동으로 덮어써지는데** 설정 키는 아무도 안 고치므로, 판을 올릴
|
|
48
|
+
> 때마다 「없는 도구를 부르는 스킬」이 남의 기계로 배급된다(2026-08-20 개명 때 드러난 자리).
|
|
49
|
+
> 도구 목록은 어차피 보이니 **목록에서 그 이름을 찾아 부른다.**
|
|
50
|
+
|
|
51
|
+
> **LIM 서식은 데이터가 더 필요하다.** `chapters[]`(챕터 오프닝) · `assets[]`(일러스트) ·
|
|
52
|
+
> 요약 문단마다 `§n.n` 근거 · `cases[].source_text`. 이 넷 중 **요약 근거와 자산 출처는
|
|
53
|
+
> 없으면 서버가 발행을 거절한다.** 자세한 것은 `references/lim-portal-format.md`,
|
|
54
|
+
> 일러스트 만드는 법은 `references/illustrations.md`.
|
|
55
|
+
|
|
56
|
+
**Lim 발행 규칙** (어겨도 서버가 거부하지만, 알고 시작할 것):
|
|
57
|
+
- `tenant` 는 키가 가리키는 고객사와 일치해야 한다(오발행 방지 가드).
|
|
58
|
+
- `sections[].kind` 는 `summary|status|cases|asks|note` 다섯 가지뿐 — 그 밖의 값은 절이 통째로 사라지므로 서버가 거절한다.
|
|
59
|
+
- `cases[].grade` 는 `grades` 에 정의된 이름이어야 한다.
|
|
60
|
+
- **`update_report` 는 같은 주소를 덮어쓴다** — 이미 고객에게 나간 링크가 새 내용을 가리키므로, 발행 후 수정은 재발행이 아니라 update 로 한다. 기존 문서는 `list_reports`/`get_report` 로 받아 고쳐 되돌려 보낸다.
|
|
61
|
+
- 초안 상태로 올려 두고 다듬어도 된다(발행이 막히지 않는다). 단, 고객에게 링크를 보내기 전에 완성할 것.
|
|
62
|
+
- 내용 설계 규칙(아래 0~2단계, 고객 언어 치환, 카피 게이트)은 **전달 경로와 무관하게 동일하게** 적용된다 — 서버가 렌더한다고 해서 원고 품질 단계를 건너뛰지 않는다.
|
|
63
|
+
|
|
64
|
+
단일 HTML 산출물의 성질:
|
|
65
|
+
|
|
66
|
+
| 성질 | 이유 |
|
|
67
|
+
|---|---|
|
|
68
|
+
| 단일 HTML · 외부 리소스 0 | 메일 첨부·사내망 열람이 목적이다. CDN 은 방화벽·오프라인에서 죽는다 |
|
|
69
|
+
| 한글 폰트 임베드 (300KB) | 고객 PC 에 Pretendard 가 없으면 맑은 고딕으로 떨어져 인상이 통째로 달라진다 |
|
|
70
|
+
| 문서 안에 위지윅 편집기 | 문구 수정 요청이 반드시 온다. 매번 다시 빌드하지 않고 그 자리에서 고쳐 저장한다 |
|
|
71
|
+
| 고객본 / 내부본 분리 | 내부 메모를 남겨 두고도 고객본에서는 빠진 채로 내보낸다 |
|
|
72
|
+
| 자동 검수 통과 필수 | 대비 미달·빠진 블록·용어 유출은 **눈으로 못 잡는다** |
|
|
73
|
+
|
|
74
|
+
## 두 가지 문서 모드 — 뼈대가 다르다
|
|
75
|
+
|
|
76
|
+
| 독자의 일 | 모드 | 뼈대 | 사양 |
|
|
77
|
+
|---|---|---|---|
|
|
78
|
+
| 판정한다 (검수·회신·결재) | 검수 보고서 | 요약 → 등급·판정표 → 건별 카드 → 확인 요청 | 이 문서 아래 전체 |
|
|
79
|
+
| **배운다** (유지보수·인수·온보딩·사후분석) | **교과서** | 학습목표 → 원리 → 붕괴(예제 풀이) → 처방 → 적용 → 연습 → 부록 대장 | `references/textbook-mode.md` **필독** |
|
|
80
|
+
|
|
81
|
+
교과서 모드에서 검수 뼈대에 스타일만 입히는 것은 반려된 실패 사례다(부분 개선 ≠ 전체 개선).
|
|
82
|
+
독자 정의(0단계) 직후 모드부터 판별하고, 교과서 모드면 **절 구성 확정 전에 스타일을 만지지 않는다.**
|
|
83
|
+
|
|
84
|
+
## 트리거 키워드
|
|
85
|
+
|
|
86
|
+
Lim 보고서, Lim 에 올려줘, 고객 보고서, 수정 내역, 검수 요청, 처리 현황, 릴리스 노트,
|
|
87
|
+
사후 보고, 납품 보고, QC 보고, 이슈 정리, 대응 결과, 제안서 수준 문서, **기술보고서,
|
|
88
|
+
인수인계, 온보딩 문서, 포스트모템, "다음 사람이 읽고 이해해야 하는 문서"** (→ 교과서 모드)
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 0단계 — 독자를 먼저 못 박는다 (건너뛰지 말 것)
|
|
93
|
+
|
|
94
|
+
**이 스킬에서 유일하게 반드시 사용자에게 확인해야 하는 것.** 나머지는 스스로 판단해도 된다.
|
|
95
|
+
|
|
96
|
+
물어볼 것:
|
|
97
|
+
|
|
98
|
+
1. **이 문서를 누가 읽는가** — 고객사 검수 담당? 결정권자? 실무 매장 인원?
|
|
99
|
+
2. **읽고 나서 무엇을 해야 하는가** — 확인만? 판단해서 회신? 결재?
|
|
100
|
+
|
|
101
|
+
독자가 정해지면 나머지가 전부 따라 정해진다. 같은 사실이 이렇게 갈린다.
|
|
102
|
+
|
|
103
|
+
| 내부 기록 | 고객 보고서 |
|
|
104
|
+
|---|---|
|
|
105
|
+
| "저장 경로 6곳을 수정" | "저장이 일어나는 모든 경로를 수정" |
|
|
106
|
+
| "2026-08-09 운영 배포 완료" | "2026년 8월 9일 운영 환경에 반영" |
|
|
107
|
+
| "별도 티켓으로 분리" | "4.3에 따로 정리했습니다" |
|
|
108
|
+
| "회귀 테스트 2건 추가" | "같은 문제가 다시 나타나면 곧바로 걸러지도록 상시 점검을 걸어 두었습니다" |
|
|
109
|
+
| "RN 앱 토큰 부활 경합" | "로그아웃 직후 이전 로그인 상태로 되돌아가던 문제" |
|
|
110
|
+
| 티켓 번호 `1217296144360763` | (싣지 않는다. 고객이 아는 참조는 본인이 제보한 원본 링크뿐) |
|
|
111
|
+
|
|
112
|
+
독자를 안 정하고 만들면 개발 로그에 색만 칠한 물건이 나온다. 실제로 그렇게 한 번 만들었고,
|
|
113
|
+
"개발팀 내부적인 작업 시나리오까지 섞어서 보고서로 보내고 있다"는 지적을 받았다.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## 워크플로우
|
|
118
|
+
|
|
119
|
+
### 1. 자료 모으기 → `cases.json`
|
|
120
|
+
|
|
121
|
+
건별로 **증상 · 원인 · 조치 · 확인 방법 · 확인 요청** 다섯 칸을 채운다.
|
|
122
|
+
`확인 방법`은 **고객이 자기 화면에서 직접 밟을 수 있는 순서**여야 한다. 이게 없으면 검수 요청이 아니라 통보다.
|
|
123
|
+
`확인 요청`은 우리가 끝낼 수 없어 상대의 판단·확인이 필요한 것만 적는다. 없으면 비운다.
|
|
124
|
+
|
|
125
|
+
→ 필드 정의와 작성 규칙: `references/content-pipeline.md`
|
|
126
|
+
|
|
127
|
+
### 2. 등급 체계 정하기
|
|
128
|
+
|
|
129
|
+
각 건을 **어디까지 확인했는지**로 등급을 매긴다. 이건 고객에게 "얼마나 믿어도 되는가"를 알려주는 장치다.
|
|
130
|
+
등급 이름은 고객 언어로 쓴다 — "코드 확인", "자동 검사" 같은 내부 절차 용어를 그대로 쓰지 않는다.
|
|
131
|
+
|
|
132
|
+
예시(4단계, 강한 순):
|
|
133
|
+
|
|
134
|
+
| 등급 | 뜻 |
|
|
135
|
+
|---|---|
|
|
136
|
+
| 운영 환경 확인 | 운영 환경에서 실제로 동작시켜 결과까지 확인 |
|
|
137
|
+
| 실데이터 대조 | 운영 데이터를 건별로 대조해 확인 |
|
|
138
|
+
| 재발 점검 적용 | 같은 문제가 다시 생기면 걸러지도록 상시 점검 적용 |
|
|
139
|
+
| 반영 확인 | 수정이 운영에 반영된 것까지 확인 (화면 재현은 미실시) |
|
|
140
|
+
|
|
141
|
+
**확인 못 한 것을 확인한 것처럼 쓰지 않는다.** 등급이 낮아 보이는 게 싫어서 올리면, 나중에 그 한 건이 문서 전체의 신뢰를 깎는다.
|
|
142
|
+
|
|
143
|
+
### 3. 팔레트·판형 고르기
|
|
144
|
+
|
|
145
|
+
| 독자·성격 | 팔레트 |
|
|
146
|
+
|---|---|
|
|
147
|
+
| 격식 보고·검수 요청·임원 열람 | `korean-navy` (기본) |
|
|
148
|
+
| 일반 B2B·신뢰 | `claude-classic` |
|
|
149
|
+
| 공문·법무·최대한 담백하게 | `claude-mono` |
|
|
150
|
+
| 데이터·금융 | `claude-blue-calm` |
|
|
151
|
+
| 한국 중견기업 B2B | `korean-brick` |
|
|
152
|
+
| 헬스·교육 | `korean-sage` |
|
|
153
|
+
| 임팩트 | `high-contrast` |
|
|
154
|
+
|
|
155
|
+
색은 **한 문서에 한 팔레트**. `report.json` 의 `palette` 한 줄만 바꾸면 문서 전체가 따라온다 —
|
|
156
|
+
빌드가 대비 기준을 만족하는 토큰 세트를 **유도**하므로 손으로 색을 고를 일이 없다.
|
|
157
|
+
|
|
158
|
+
→ 유도 규칙과 토큰 역할: `references/design-system.md`
|
|
159
|
+
|
|
160
|
+
### 4. 뼈대 쓰기 → `report.json`
|
|
161
|
+
|
|
162
|
+
표지·요약·처리 현황·건별·확인 요청 다섯 절이 기본 구성이다.
|
|
163
|
+
요약은 **4칸**으로 쓴다. 검수자가 첫 화면에서 판단을 끝낼 수 있어야 한다.
|
|
164
|
+
|
|
165
|
+
1. 접수한 것은 어떻게 됐는가
|
|
166
|
+
2. 우리가 추가로 찾은 것 중 중요한 것
|
|
167
|
+
3. 여러 건을 관통하는 공통 원인 (있으면 도형으로)
|
|
168
|
+
4. 상대가 무엇을 하면 되는가
|
|
169
|
+
|
|
170
|
+
### 5. 전달 — Lim 발행 (기본 경로)
|
|
171
|
+
|
|
172
|
+
report.json + cases.json 을 하나로 합쳐 `publish_report` 에 준다
|
|
173
|
+
(스키마가 곧 이 스킬의 데이터 모델이다 — `tenant`·`slug` 필수, kind 5종·grade 정합 주의).
|
|
174
|
+
서버가 렌더하므로 **HTML 을 만들지 않는다.**
|
|
175
|
+
|
|
176
|
+
발행 후 확인: 돌아온 열람 주소를 실제로 열어 본다(절 누락·등급 배지·목차).
|
|
177
|
+
수정은 `get_report` → 데이터 수정 → `update_report` — **같은 링크가 새 내용을 가리킨다.**
|
|
178
|
+
|
|
179
|
+
### 5′. 전달 — 단일 HTML (Lim 밖 고객·교과서 모드)
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
python3 ~/.claude/skills/genetics-report/assets/render.py report.json
|
|
183
|
+
# 브랜드 서식으로:
|
|
184
|
+
python3 ~/.claude/skills/genetics-report/assets/render.py report.json --template lim-report
|
|
185
|
+
python3 ~/.claude/skills/genetics-report/assets/qa.py report.json # 실패 0 이어야 통과
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
자동 검수가 통과해도 **반드시 브라우저로 열어 본다.** 자동 검수는 "안 보이는 결함"을 잡고,
|
|
189
|
+
눈은 "보이는 결함"을 잡는다. 실제로 자동 검수를 다 통과한 문서에서 눈으로만 잡힌 것들:
|
|
190
|
+
안 보이는 배지, 빈 목록 항목, 한국어 낱말 중간 줄바꿈, 편집기가 판정표를 지워버리는 버그.
|
|
191
|
+
→ 검수 항목 전체: `references/qa.md`
|
|
192
|
+
|
|
193
|
+
파일 하나를 그대로 보낸다. 상대는 더블클릭만 하면 된다.
|
|
194
|
+
문구 수정 요청이 오면 **다시 빌드하지 말고** 문서를 브라우저에서 열어 고친 뒤 `저장`을 누른다(원본 덮어쓰기).
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## 자산
|
|
199
|
+
|
|
200
|
+
| 파일 | 역할 |
|
|
201
|
+
|---|---|
|
|
202
|
+
| `assets/render.py` | `report.json` → 단일 HTML. **레포 `private/builder/build.py` 를 부른다** — 여기 사본을 두지 마라 |
|
|
203
|
+
| `assets/palette.py` | 팔레트 6색 → 대비 보장 CSS 토큰 유도. 단독 실행하면 전 팔레트 감사 |
|
|
204
|
+
| `assets/report.css` | 구조 스타일시트. 색 토큰은 선언하지 않는다 |
|
|
205
|
+
| `assets/editor.js` / `editor.css` | 위지윅 편집 레이어 |
|
|
206
|
+
| `assets/qa.py` | 자동 검수 |
|
|
207
|
+
| `assets/subset_font.py` | 폰트 서브셋 생성 (보통 실행 불필요) |
|
|
208
|
+
| 폰트 | **레포** `private/builder/fonts/` 에 있다(Pretendard 서브셋 · Spoqa 3면) |
|
|
209
|
+
|
|
210
|
+
새 프로젝트에서 손대는 파일은 **`report.json` 과 `cases.json` 둘뿐**이다.
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## 반드시 지키는 것
|
|
215
|
+
|
|
216
|
+
1. **티켓 번호·이슈 트래커 이름을 싣지 않는다.** 고객이 아는 참조는 본인이 제보한 원본 링크다.
|
|
217
|
+
2. **`§` 같은 기호를 본문에 노출하지 않는다.** 원고에서는 참조 마커로 쓰되 렌더에서 지운다(빌드가 처리).
|
|
218
|
+
3. **개발 절차 서술을 빼거나 고쳐 쓴다.** 배포·티켓 분리·테스트 추가·설계 검토안은 고객의 관심사가 아니다.
|
|
219
|
+
4. **확인 못 한 범위를 반드시 적는다.** 감추면 다음 보고서까지 못 믿게 된다.
|
|
220
|
+
5. **한 문서 한 팔레트, 강조색은 헤딩과 "상대가 해야 할 일"에만.**
|
|
221
|
+
6. **`--accent`(팔레트 보조색)를 글자색으로 쓰지 않는다.** 배경·괘선 전용이다. 글자는 `--accent-mid`/`--accent-ink`.
|
|
222
|
+
7. **파괴적 버튼을 안내할 때는 결과를 같이 적는다.** (예: "이 버튼은 실제로 고객에게 알림톡이 나갑니다")
|
|
223
|
+
8. **포털이 이미 주는 것을 문서 안에 만들지 않는다.** 검토 회신(댓글)·핵심 요약·진척률 바·상단 바는
|
|
224
|
+
포털 셸이 문서 **둘레**에 놓는다. 문서에 「댓글로 알려 주세요」를 쓰거나 요약 팝업을 흉내 내면
|
|
225
|
+
같은 것이 두 번 있게 된다 — `references/lim-portal-format.md` §2b.
|
|
226
|
+
9. **이 스킬에 특정 고객사의 도메인 지식을 적지 않는다.** 다음 문서는 다른 업종·다른 테넌트·다른
|
|
227
|
+
팔레트다. 스킬은 **어떻게 할지**만 정하고 **무엇을 쓸지**는 그 문서의 데이터가 정한다.
|
|
228
|
+
|
|
229
|
+
| 스킬에 적는 것 | 그 문서(브리프·`report.json`)에 적는 것 |
|
|
230
|
+
|---|---|
|
|
231
|
+
| 브리프 표의 **형식**, 프롬프트 **구조**, 검수 **절차** | 장면, 노드에 들어갈 사물, 아이콘 |
|
|
232
|
+
| 「오브젝트를 이름으로 적어라」는 **규칙** | 그 이름들 |
|
|
233
|
+
| 기준선을 **뜨는 방법**(`freeze --ref … --palette …`) | 밴드 **수치** |
|
|
234
|
+
|
|
235
|
+
실패 사례(2026-08-17): 스킬의 조형 문법 표에 체중계·팔근육 같은 업종 아이콘을 적어 두었고,
|
|
236
|
+
시각 관문은 라임 hex 를 상수로 박아 두었다. 강조색이 금색인 `korean-navy` 테넌트에서는
|
|
237
|
+
**전량 오판정**한다(같은 금색 픽셀을 `lim` 은 `other`, `korean-navy` 는 `accent` 로 읽는다 — 실측).
|
|
238
|
+
색·밴드·아이콘은 전부 **팔레트와 그 문서에서** 온다.
|
|
239
|
+
|
|
240
|
+
## 흔한 실패 모드
|
|
241
|
+
|
|
242
|
+
| 증상 | 원인 | 처방 |
|
|
243
|
+
|---|---|---|
|
|
244
|
+
| "플랫하게 해달라" → 밋밋해짐 | 그림자를 없애랬더니 색면까지 없앰 | 플랫은 깊이(그림자·베벨)를 없애는 것이지 **색면을 없애는 게 아니다** |
|
|
245
|
+
| 색을 칠했는데도 안 읽힘 | 산문이 한 덩어리 | 열거는 번호를 세우고 긴 산문은 두 문장씩 끊는다 (빌드가 처리) |
|
|
246
|
+
| "웹앱 같다" | 목차를 본문 위에 얹음 | 목차는 **옆으로** 뺀다. 좌측 고정 내비 |
|
|
247
|
+
| 강조색이 흐릿하다 | 팔레트 보조색을 글자로 씀 | 2.25:1 이다. `--accent-mid`(3:1) / `--accent-ink`(4.5:1) 를 쓴다 |
|
|
248
|
+
| 낱말이 한가운데서 갈라짐 | `word-break` 기본값 | `word-break:keep-all` (구조 CSS 에 이미 있음) |
|
|
249
|
+
| 인용문이 문장 중간에서 잘림 | 근거를 자동 발췌 | 발췌 인용을 **아예 넣지 않는다.** 조치 본문에 이미 있다 |
|
|
250
|
+
| 개발 서술이 새어 들어감 | 원고를 그대로 씀 | `report.json` 의 `text_fix` + `forbid` 로 막고 검수로 잡는다 |
|
|
251
|
+
| 목차·판정표가 비어버림 | 편집기 선택자와 마크업이 어긋남 | `references/wysiwyg.md` 의 마크업 계약을 지킨다 |
|
|
252
|
+
|
|
253
|
+
## 카피 게이트
|
|
254
|
+
|
|
255
|
+
doc-pptx 의 슬롭 3종 중 **대시 대비 헤드라인**("복붙에서 위임으로 — 목표만 주면")과
|
|
256
|
+
**"A에서 B로" 전환 공식**은 이 문서에서도 그대로 금지다. 검수기가 잡는다.
|
|
257
|
+
|
|
258
|
+
다만 **조사·체언 종결 조각문** 규칙은 보고서 문맥에서 완화한다. 목차 항목과 건 제목은
|
|
259
|
+
"…던 문제", "…내역 정리"처럼 명사구로 끝나는 것이 한국어 보고서의 관례다. 슬라이드 헤드라인과
|
|
260
|
+
달리 목록으로 훑는 항목이라 완전문으로 바꾸면 오히려 읽기 어렵다.
|
|
261
|
+
대신 **요약의 각 리드 문장과 확인 요청의 제목은 완전문**으로 쓴다 — 여기가 판단이 일어나는 자리다.
|
|
262
|
+
|
|
263
|
+
## 폰트
|
|
264
|
+
|
|
265
|
+
기본으로 Pretendard 서브셋(KS X 1001 2,350자, 가변 wght 400–700, 300KB)이 임베드된다.
|
|
266
|
+
`report.json` 의 `font.embed: false` 로 끌 수 있다(시안 반복 중에는 끄면 빠르다).
|
|
267
|
+
|
|
268
|
+
커버리지를 넓히거나 다른 폰트로 바꾸려면 `references/font-embedding.md`.
|
|
269
|
+
|
|
270
|
+
## 상세 레퍼런스
|
|
271
|
+
|
|
272
|
+
| 파일 | 로드 조건 |
|
|
273
|
+
|---|---|
|
|
274
|
+
| `references/lim-portal-format.md` | **LIM 서식으로 포털 발행할 때 — 챕터·자산·요약 근거 게이트·운영 제약** |
|
|
275
|
+
| `references/illustrations.md` | **hero·챕터 일러스트를 만들 때 — GPT Image 2·참조 이미지·프롬프트 규격·시각 검수** |
|
|
276
|
+
| `references/process.md` | 1단계 — 자료 수집·독자 정의·작업 순서 |
|
|
277
|
+
| `references/content-pipeline.md` | 1·4단계 — 데이터 모델, 절 번호, 한국어 조판 규칙, 고객 언어 치환 |
|
|
278
|
+
| `references/design-system.md` | 3단계 — 토큰 역할, 아키타입→HTML 매핑, 팔레트 교체 |
|
|
279
|
+
| `references/wysiwyg.md` | 편집기를 붙이거나 마크업을 바꿀 때 — 마크업 계약 |
|
|
280
|
+
| `references/qa.md` | 6단계 — 자동·시각 검수 전체 목록 |
|
|
281
|
+
| `references/font-embedding.md` | 폰트를 바꾸거나 커버리지를 조정할 때 |
|
|
282
|
+
|
|
283
|
+
## 계보
|
|
284
|
+
|
|
285
|
+
색·타이포·슬라이드 아키타입 체계는 `moai-officer:doc-pptx`(modu-ai/moai-cowork)에서 계승했다.
|
|
286
|
+
그 스킬은 **발표 슬라이드**를 전제하므로, 글자가 작고 많은 문서로 옮기면서 다음을 바꿨다.
|
|
287
|
+
|
|
288
|
+
- 팔레트 6색을 그대로 쓰지 않고 **문서용 토큰으로 유도**한다 (슬라이드 기준으로는 통과하는 색이 문서 본문에서는 대비 미달)
|
|
289
|
+
- 9가지 슬라이드 아키타입을 **스크롤 문서 블록으로 재해석**한다 (Title→표지 슬래브, Agenda→좌측 고정 목차, Stats→지표 밴드, Features→건별 카드, CTA→확인 요청)
|
|
290
|
+
- 카피 게이트의 "조사·체언 종결" 규칙을 목차·제목에서 완화한다
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"group": "reported",
|
|
4
|
+
"title": "주문서를 저장해도 목록에 반영되지 않던 문제",
|
|
5
|
+
"grade": "운영 환경 확인",
|
|
6
|
+
"symptom": "주문서를 작성하고 저장을 눌렀는데 목록으로 돌아오면 그 주문이 보이지 않았습니다. 새로고침을 한 번 더 하면 나타났습니다.",
|
|
7
|
+
"cause": "저장이 끝나기 전에 목록을 다시 불러오고 있어, 저장 직전 상태가 화면에 남았습니다.",
|
|
8
|
+
"fix": "저장이 끝난 뒤에 목록을 불러오도록 순서를 바꿨습니다. 운영 환경에서 주문서를 실제로 작성·저장해 목록에 바로 나타나는 것을 확인했습니다.",
|
|
9
|
+
"how_to_check": "주문서를 하나 작성해 저장하고 목록으로 돌아와 보시면 됩니다. 새로고침 없이 바로 보이면 정상입니다.",
|
|
10
|
+
"remaining": "",
|
|
11
|
+
"source_url": ""
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"group": "own",
|
|
15
|
+
"title": "목록이 500건을 넘으면 첫 화면이 느려지던 문제",
|
|
16
|
+
"grade": "실데이터 대조",
|
|
17
|
+
"symptom": "주문이 많이 쌓인 지점에서 목록 첫 화면이 뜨는 데 10초 넘게 걸렸습니다.",
|
|
18
|
+
"cause": "필요한 건만 골라내지 않고 전체를 한 건씩 훑는 방식으로 조회하고 있었습니다.",
|
|
19
|
+
"fix": "조회 조건을 바로잡아 필요한 건만 걸러내도록 수정했습니다. 운영 데이터로 대조해 10.4초에서 0.3초로 바뀐 것을 확인했습니다.",
|
|
20
|
+
"how_to_check": "주문이 많은 지점에서 목록 첫 화면이 1~2초 안에 뜨는지 보시면 됩니다.",
|
|
21
|
+
"remaining": "목록 위에 표시되는 총 건수는 예전 데이터 때문에 실제보다 몇 건 많게 나올 수 있습니다. 목록 내용 자체는 정상입니다.",
|
|
22
|
+
"source_url": ""
|
|
23
|
+
}
|
|
24
|
+
]
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"out": "예시-보고서.html",
|
|
3
|
+
"cases": "cases.json",
|
|
4
|
+
"palette": "korean-navy",
|
|
5
|
+
"lang": "ko",
|
|
6
|
+
"editor": true,
|
|
7
|
+
"title_tag": "예시 · 수정 내역 보고",
|
|
8
|
+
"description": "genetics-report 최소 예제",
|
|
9
|
+
"kicker": "우리회사 · 고객사",
|
|
10
|
+
"title": "시스템 수정 내역 보고",
|
|
11
|
+
"subtitle": "이번 주에 처리한 2건의 수정 내역과, 확인해 주셔야 마무리되는 사항을 정리했습니다.",
|
|
12
|
+
"meta": [["보고 기간", "2026.08.03 – 08.10"], ["처리 건수", "2건 (접수 1 · 자체 발견 1)"],
|
|
13
|
+
["수신", "고객사 검수 담당"], ["작성", "2026.08.10 · 우리회사"]],
|
|
14
|
+
"grades": {
|
|
15
|
+
"운영 환경 확인": ["m-live", "운영 환경에서 실제로 동작시켜 결과까지 확인"],
|
|
16
|
+
"실데이터 대조": ["m-data", "운영 데이터를 건별로 대조해 확인"],
|
|
17
|
+
"재발 점검 적용": ["m-test", "같은 문제가 다시 생기면 걸러지도록 상시 점검 적용"],
|
|
18
|
+
"반영 확인": ["m-code", "수정이 운영에 반영된 것까지 확인 (화면 재현은 미실시)"]
|
|
19
|
+
},
|
|
20
|
+
"stats": [
|
|
21
|
+
{"value": "2", "unit": "건", "label": "이번 기간에 처리한 건수"},
|
|
22
|
+
{"value": "1", "unit": "건", "label": "접수하신 건 — 조치 후 운영 반영"},
|
|
23
|
+
{"value": "1", "unit": "건", "label": "확인 과정에서 함께 발견해 처리"},
|
|
24
|
+
{"value": "1", "unit": "가지", "label": "확인을 부탁드리는 사항", "accent": true}
|
|
25
|
+
],
|
|
26
|
+
"groups": [{"key": "reported", "no": "3"}, {"key": "own", "no": "4"}],
|
|
27
|
+
"sections": [
|
|
28
|
+
{"no": "01", "id": "sec-1", "name": "요약", "kind": "summary"},
|
|
29
|
+
{"no": "02", "id": "sec-2", "name": "처리 현황", "kind": "status",
|
|
30
|
+
"lede": "2건 전부의 처리 결과와 확인 등급입니다. 등급은 저희가 어디까지 직접 확인했는지를 뜻합니다."},
|
|
31
|
+
{"no": "03", "id": "sec-3", "name": "접수하신 1건", "kind": "cases", "group": "reported",
|
|
32
|
+
"lede": "제보해 주신 건입니다. 증상·원인·조치와 함께 직접 확인하실 수 있는 방법을 적었습니다."},
|
|
33
|
+
{"no": "04", "id": "sec-4", "name": "함께 발견해 처리한 1건", "kind": "cases", "group": "own",
|
|
34
|
+
"lede": "접수 건을 확인하는 과정에서 저희가 함께 발견해 처리한 건입니다."},
|
|
35
|
+
{"no": "05", "id": "sec-5", "name": "확인 요청 드리는 사항", "kind": "asks"}
|
|
36
|
+
],
|
|
37
|
+
"summary": [
|
|
38
|
+
{"lead": "접수해 주신 1건은 조치를 마치고 운영에 반영했습니다.",
|
|
39
|
+
"body": "저장 순서가 어긋나 목록에 반영되지 않던 문제였습니다(§3.1). 확인 방법은 각 건에 적어 두었습니다."},
|
|
40
|
+
{"lead": "확인 과정에서 목록 속도 문제를 함께 찾아 고쳤습니다.",
|
|
41
|
+
"body": "주문이 많은 지점에서 첫 화면이 10초 넘게 걸리던 것을 0.3초로 줄였습니다(§4.1). 접수 목록에는 없던 건입니다."}
|
|
42
|
+
],
|
|
43
|
+
"open_items": [
|
|
44
|
+
{"title": "총 건수 표시는 한 번 더 확인이 필요합니다",
|
|
45
|
+
"body": "목록 위 총 건수가 예전 데이터 때문에 실제보다 몇 건 많게 나올 수 있습니다. 목록에 표시되는 내용 자체는 정상입니다.",
|
|
46
|
+
"refs": ["§4.1"]}
|
|
47
|
+
],
|
|
48
|
+
"cta": ["<strong>검토는 각 건에 적어 둔 확인 방법대로 한 번씩 열어보시는 것으로 충분합니다.</strong> 아래 1가지는 저희 쪽에서 마무리할 수 없어 확인이 필요한 부분입니다."],
|
|
49
|
+
"footer": ["예시 · 수정 내역 보고", "2026.08.10 · 우리회사"],
|
|
50
|
+
"text_fix": [
|
|
51
|
+
{"from": "운영 배포를 마쳤습니다", "to": "운영 환경에 반영했습니다"}
|
|
52
|
+
],
|
|
53
|
+
"forbid": ["[Aa]sana", "운영 배포", "별도 티켓"],
|
|
54
|
+
"allow_external": []
|
|
55
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""브리프 표를 읽어 그 문서의 일러스트를 만든다.
|
|
3
|
+
|
|
4
|
+
python3 generate-illustrations.py --brief <문서>/image-briefs.md --out <폴더>
|
|
5
|
+
|
|
6
|
+
**이 스크립트는 장면을 모른다.** 장면과 비율은 전부 브리프 표에서 온다.
|
|
7
|
+
그래야 다른 고객사·다른 도메인의 문서에도 같은 스크립트가 쓰인다.
|
|
8
|
+
|
|
9
|
+
여기 박힌 것은 **스타일 계약**뿐이고, 그 계약은 정본 8장 실측에서 나왔다.
|
|
10
|
+
앞 판들이 실패한 자리를 그대로 적어 둔다 — 지우면 같은 실패가 돌아온다:
|
|
11
|
+
|
|
12
|
+
· 장면을 스크립트에 박아 넣어 문서가 바뀌어도 같은 그림이 나왔다(2026-08-16 반려)
|
|
13
|
+
· 정본을 안 보고 만든 규칙(「큰 면으로」·「표면을 비워라」)이 조형 문법을 지웠다
|
|
14
|
+
· **한쪽을 비우라고 줬다**(2026-08-17~18 반려). 글이 그림 **위에** 얹힌다고 오독한
|
|
15
|
+
결과였다. 실제 배치는 하나의 테두리 안에서 글과 그림이 **좌우로 따로** 서는 것이라
|
|
16
|
+
(계약 4.2절 v1.4) 그림 위에는 아무것도 앉지 않는다 — 비우면 칸의 절반이 죽는다.
|
|
17
|
+
|
|
18
|
+
판정은 `visual-baseline.py check 그림.png` 로 한다.
|
|
19
|
+
"""
|
|
20
|
+
import argparse
|
|
21
|
+
import os
|
|
22
|
+
import re
|
|
23
|
+
import subprocess
|
|
24
|
+
import sys
|
|
25
|
+
import time
|
|
26
|
+
|
|
27
|
+
# ── 스타일 계약 — 정본 `image-briefs.md` 「공통 스타일 계약」의 조형 문법을 영어로 옮긴 것.
|
|
28
|
+
# 요약하지 않는다. 요약이 2026-08-17 사고의 시작이었다.
|
|
29
|
+
STYLE = """Flat line illustration in one consistent set. Clean uniform thin dark outlines (#191A23)
|
|
30
|
+
on pure white, with light gray (#F3F3F3) fills. People have black hair, white shirts with a small
|
|
31
|
+
lime accent garment, and simple drawn faces; keep figure proportions identical across the set.
|
|
32
|
+
Build the scene as a small diagram: icons inside outlined circles, connected by dotted and solid
|
|
33
|
+
curved lines, with small badges - a dark circle holding an X, a lime circle holding a check.
|
|
34
|
+
The one accent colour is lime, exactly the hex #B9FF66 - a bright yellow-green highlighter tone,
|
|
35
|
+
much lighter and more yellow than grass green. Never use a darker or duller green, and never wash it
|
|
36
|
+
out toward pale mint, sage or pastel yellow-green: it must stay vivid and fully saturated. Place the
|
|
37
|
+
lime in several small spots - a garment accent, a filled badge, a small figure, a ground shadow -
|
|
38
|
+
never as one large filled area.
|
|
39
|
+
Draw the outlines confidently thick and solid - the line weight of a printed editorial illustration,
|
|
40
|
+
not a faint hairline sketch. The strokes must be clearly heavy, closer to a bold marker than a pencil.
|
|
41
|
+
Add enough elements that the drawing carries real visual weight: extra small dotted connectors, thin
|
|
42
|
+
arcs and tiny diamonds clustered around the main objects.
|
|
43
|
+
Use the light gray fill only inside a few small shapes such as a badge or a single icon body.
|
|
44
|
+
Never fill a background, a panel, a board, a large circle or a garment with gray - those stay pure
|
|
45
|
+
white with an outline. Gray must cover only a small fraction of the picture.
|
|
46
|
+
Draw the people at a readable size: cropped at the waist, each figure about half the frame height,
|
|
47
|
+
with visible hands and facial features. Keep every figure on the SAME side of the frame - never one
|
|
48
|
+
at each edge.
|
|
49
|
+
The drawing reaches across the whole frame - this is not a half-empty picture.
|
|
50
|
+
No text, no numbers, no logos, no watermarks, no photographic or 3D style, no gradients, no neon,
|
|
51
|
+
no fake UI."""
|
|
52
|
+
|
|
53
|
+
# ── 구도 — 그림은 **자기 칸을 꽉 채운다.**
|
|
54
|
+
#
|
|
55
|
+
# ⚠ 여기 한때 「글이 앉는 쪽 46% 를 연하게 비워 두라」는 문단이 있었다. 글을 그림 위에
|
|
56
|
+
# 얹는 배치를 전제한 것인데, 그 전제가 **오독이었다**(사용자 정정 2026-08-18):
|
|
57
|
+
# 글과 그림은 하나의 테두리 안에서 **좌우로 따로** 선다(계약 4.2절 v1.4).
|
|
58
|
+
# 그림 위에는 아무것도 앉지 않으므로 한쪽을 비울 이유가 없다 — 비우면 칸의 절반이 죽는다.
|
|
59
|
+
COMPOSITION = """Composition: the drawing fills its frame edge to edge as one balanced scene.
|
|
60
|
+
Do not leave one half of the frame empty - there is no text on top of this picture."""
|
|
61
|
+
|
|
62
|
+
MODEL = 'gpt_image_2' # 6종 비교에서 유일하게 정본 선화 품질에 도달 (§2.3)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def rows(brief):
|
|
66
|
+
"""브리프의 자산 표만 읽는다. 네 번째 칸(배치 쪽)은 사람이 대조할 때만 쓴다."""
|
|
67
|
+
out = []
|
|
68
|
+
with open(brief, encoding='utf-8') as fh:
|
|
69
|
+
for ln in fh:
|
|
70
|
+
if not ln.startswith('| **'):
|
|
71
|
+
continue
|
|
72
|
+
c = [x.strip() for x in ln.strip().strip('|').split('|')]
|
|
73
|
+
if len(c) < 4:
|
|
74
|
+
continue
|
|
75
|
+
name = re.sub(r'\*|\s', '', c[0]).lower().replace('chapter', 'ch')
|
|
76
|
+
ar = '16:9' if '16:9' in c[1] else '4:3'
|
|
77
|
+
# 「**왼쪽** — 인물은 오른쪽에」처럼 **한 칸에 두 단어가 다 있다.** 그래서
|
|
78
|
+
# `in` 으로 찾으면 앞에 쓴 쪽이 이겨 3/7 이 반대로 나간다(2026-08-18 드라이런이
|
|
79
|
+
# 잡았다 — 그대로 돌렸으면 21크레딧을 버렸다). **굵게 표시된 첫 토큰**만 읽는다.
|
|
80
|
+
head = re.match(r'\*\*(왼쪽|오른쪽)\*\*', c[3])
|
|
81
|
+
side = None if not head else ('l' if head.group(1) == '왼쪽' else 'r')
|
|
82
|
+
out.append((name, ar, c[2], side))
|
|
83
|
+
return out
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def gen(name, ar, scene, side, out_dir, ref):
|
|
87
|
+
dst = os.path.join(out_dir, f'{name}.png')
|
|
88
|
+
if os.path.exists(dst) and os.path.getsize(dst) > 0:
|
|
89
|
+
print(f' [{name}] 이미 있음')
|
|
90
|
+
return dst
|
|
91
|
+
# `side` 는 **렌더러가 정하는 배치**(홀수 왼쪽/짝수 오른쪽)라 그림에는 영향이 없다.
|
|
92
|
+
# 브리프에 남겨 두는 것은 사람이 장 순서를 바꿀 때 대조하기 위해서다.
|
|
93
|
+
prompt = f'{STYLE}\n\nScene: {scene}\n\n{COMPOSITION}'
|
|
94
|
+
cmd = ['higgsfield', 'generate', 'create', MODEL, '--wait',
|
|
95
|
+
'--aspect_ratio', ar, '--resolution', '2k', '--json']
|
|
96
|
+
if ref:
|
|
97
|
+
cmd += ['--image', ref]
|
|
98
|
+
t = time.time()
|
|
99
|
+
# `rate_limit_reached`(동시 작업 한도 8)는 **기다리면 풀린다.** 재시도가 없던 판은
|
|
100
|
+
# 3장이 통째로 비었다(2026-08-17) — 교보재에 적혀 있던 함정을 스크립트가 안 지켰다.
|
|
101
|
+
blob = ''
|
|
102
|
+
for attempt in range(4):
|
|
103
|
+
r = subprocess.run(cmd + ['--prompt', prompt], capture_output=True, text=True)
|
|
104
|
+
blob = (r.stdout or '') + (r.stderr or '')
|
|
105
|
+
if 'rate_limit_reached' not in blob:
|
|
106
|
+
break
|
|
107
|
+
wait = 45 * (attempt + 1)
|
|
108
|
+
print(f' [{name}] 한도에 걸렸다 — {wait}초 뒤 재시도 ({attempt + 1}/4)', flush=True)
|
|
109
|
+
time.sleep(wait)
|
|
110
|
+
with open(os.path.join(out_dir, f'{name}.json'), 'w', encoding='utf-8') as fh:
|
|
111
|
+
fh.write(blob)
|
|
112
|
+
m = re.search(r'https://[^"\s]+\.(?:png|jpg|jpeg|webp)', blob)
|
|
113
|
+
if not m:
|
|
114
|
+
print(f' [{name}] 실패 {time.time() - t:.0f}초: {blob.strip()[:150]}')
|
|
115
|
+
return None
|
|
116
|
+
subprocess.run(['curl', '-sL', '-o', dst, m.group(0)], check=True)
|
|
117
|
+
print(f' [{name}] 완료 {time.time() - t:.0f}초 {os.path.getsize(dst):,}B', flush=True)
|
|
118
|
+
return dst
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def upload_ref(path):
|
|
122
|
+
u = subprocess.run(['higgsfield', 'upload', 'create', path, '--json'],
|
|
123
|
+
capture_output=True, text=True)
|
|
124
|
+
m = re.search(r'[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}', u.stdout or '')
|
|
125
|
+
return m.group(0) if m else None
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def main():
|
|
129
|
+
ap = argparse.ArgumentParser()
|
|
130
|
+
ap.add_argument('--brief', required=True, help='그 문서의 image-briefs.md')
|
|
131
|
+
ap.add_argument('--out', required=True, help='그림을 받을 폴더')
|
|
132
|
+
ap.add_argument('--only', default='', help='쉼표로 끊은 이름만 만든다 (예: ch02,ch05)')
|
|
133
|
+
ap.add_argument('--ref', default='', help='세트 참조 UUID. 주면 첫 장을 올리지 않는다')
|
|
134
|
+
a = ap.parse_args()
|
|
135
|
+
|
|
136
|
+
os.makedirs(a.out, exist_ok=True)
|
|
137
|
+
os.chmod(a.out, 0o700) # 고객 문서용 그림이다. 남이 읽게 두지 않는다.
|
|
138
|
+
rs = rows(a.brief)
|
|
139
|
+
only = {x.strip() for x in a.only.split(',') if x.strip()}
|
|
140
|
+
print(f'브리프에서 {len(rs)}장을 읽었다 — 스크립트는 장면을 모른다')
|
|
141
|
+
print(' 배치(참고): ' + ' '.join(f'{n}:{s or "-"}' for n, _, _, s in rs))
|
|
142
|
+
|
|
143
|
+
ref = a.ref or None
|
|
144
|
+
for i, (name, ar, scene, side) in enumerate(rs):
|
|
145
|
+
if only and name not in only:
|
|
146
|
+
continue
|
|
147
|
+
p = gen(name, ar, scene, side, a.out, ref)
|
|
148
|
+
# 첫 장을 세트 기준으로 올린다. **단 관문을 통과한 뒤에.**
|
|
149
|
+
# 6차에서 hero 의 흐린 라임(#C8E997·거리 55.8)이 참조로 올라가 뒤 6장이 전부
|
|
150
|
+
# 그 색을 물려받았다 — 참조 UUID 는 첫 장의 결함까지 나른다.
|
|
151
|
+
if i == 0 and p and not ref and not only:
|
|
152
|
+
print(f' ↑ 이 장을 먼저 판정할 것: visual-baseline.py check {p}')
|
|
153
|
+
ref = upload_ref(p)
|
|
154
|
+
print(f' 참조 UUID: {ref or "없음(세트 일관성 없이 진행)"}', flush=True)
|
|
155
|
+
print('끝')
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
if __name__ == '__main__':
|
|
159
|
+
sys.exit(main())
|