oh-my-design-cli 0.1.2 → 1.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/.claude/hooks/post-edit-watch.cjs +99 -0
- package/.claude/hooks/session-end-foldin.cjs +96 -0
- package/.claude/hooks/session-state-loader.cjs +64 -0
- package/.claude/hooks/skill-activation.cjs +73 -0
- package/.claude/settings.json +55 -0
- package/.claude/skills/skill-rules.json +87 -0
- package/AGENTS.md +111 -0
- package/README.md +75 -202
- package/agents/AGENT.md +53 -0
- package/agents/omd-3d-blender.md +269 -0
- package/agents/omd-a11y-auditor.md +97 -0
- package/agents/omd-asset-curator.md +260 -0
- package/agents/omd-critic.md +181 -0
- package/agents/omd-master.md +548 -0
- package/agents/omd-microcopy.md +63 -0
- package/agents/omd-persona-tester.md +118 -0
- package/agents/omd-ui-junior.md +129 -0
- package/agents/omd-ux-engineer.md +265 -0
- package/agents/omd-ux-researcher.md +62 -0
- package/agents/omd-ux-writer.md +181 -0
- package/data/opt-out-corpus.json +141 -0
- package/data/reference-fingerprints.json +1495 -0
- package/dist/bin/oh-my-design.js +3 -818
- package/dist/bin/oh-my-design.js.map +1 -1
- package/dist/install-skills-SVIYKXOE.js +442 -0
- package/dist/install-skills-SVIYKXOE.js.map +1 -0
- package/package.json +23 -23
- package/scripts/context.cjs +91 -0
- package/scripts/postinstall.cjs +54 -0
- package/skills/omd-apply/SKILL.md +64 -53
- package/skills/omd-harness/SKILL.md +271 -0
- package/skills/omd-learn/SKILL.md +55 -35
- package/skills/omd-remember/SKILL.md +93 -15
- package/skills/omd-sync/SKILL.md +140 -16
- package/dist/chunk-6YNSV3VY.js +0 -35
- package/dist/chunk-6YNSV3VY.js.map +0 -1
- package/dist/chunk-MHFYGZSO.js +0 -337
- package/dist/chunk-MHFYGZSO.js.map +0 -1
- package/dist/chunk-N2JG6N4Q.js +0 -264
- package/dist/chunk-N2JG6N4Q.js.map +0 -1
- package/dist/chunk-OOQQEUGX.js +0 -46
- package/dist/chunk-OOQQEUGX.js.map +0 -1
- package/dist/chunk-OR5DHENY.js +0 -250
- package/dist/chunk-OR5DHENY.js.map +0 -1
- package/dist/customizer-CM76752R.js +0 -8
- package/dist/customizer-CM76752R.js.map +0 -1
- package/dist/index.d.ts +0 -559
- package/dist/index.js +0 -3113
- package/dist/index.js.map +0 -1
- package/dist/init-UMM4XIV5.js +0 -675
- package/dist/init-UMM4XIV5.js.map +0 -1
- package/dist/install-skills-CM6VXFZJ.js +0 -152
- package/dist/install-skills-CM6VXFZJ.js.map +0 -1
- package/dist/learn-33LHKEJA.js +0 -140
- package/dist/learn-33LHKEJA.js.map +0 -1
- package/dist/reference-YMNAOXJQ.js +0 -47
- package/dist/reference-YMNAOXJQ.js.map +0 -1
- package/dist/reference-parser-TM3CJPNE.js +0 -10
- package/dist/reference-parser-TM3CJPNE.js.map +0 -1
- package/dist/remember-UAFA5B2O.js +0 -78
- package/dist/remember-UAFA5B2O.js.map +0 -1
- package/dist/sync-FDYRKNFE.js +0 -417
- package/dist/sync-FDYRKNFE.js.map +0 -1
- package/dist/templates/templates/design-md.hbs +0 -44
- package/dist/templates/templates/partials/agent-prompt-guide.hbs +0 -28
- package/dist/templates/templates/partials/color-palette.hbs +0 -49
- package/dist/templates/templates/partials/component-stylings.hbs +0 -28
- package/dist/templates/templates/partials/depth-elevation.hbs +0 -31
- package/dist/templates/templates/partials/dos-donts.hbs +0 -13
- package/dist/templates/templates/partials/layout.hbs +0 -30
- package/dist/templates/templates/partials/responsive.hbs +0 -25
- package/dist/templates/templates/partials/shadcn-tokens.hbs +0 -64
- package/dist/templates/templates/partials/typography.hbs +0 -43
- package/dist/templates/templates/partials/visual-theme.hbs +0 -26
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: omd:harness
|
|
3
|
+
description: "디자인 하네스 진입점. 사용자가 '디자인 하네스 돌려줘', '시니어 디자이너처럼 ~ 만들어줘', '/omd-harness <task>'를 호출하면 run 디렉토리를 부트스트랩하고 omd-master 오케스트레이터를 spawn해서 10-phase 파이프라인(Discovery / Asset / Research / IA / Wireframe / System / Components / AssetSourcing / Microcopy / Validation / Handoff)을 실행, 10 specialist를 dispatch해서 brief + DESIGN.md + wireframes + components + assets + persona-feedback 패키지를 emit합니다."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# omd:harness — Design Harness Entry
|
|
7
|
+
|
|
8
|
+
이 스킬은 **omd-master 오케스트레이터**를 호출하는 단일 진입점이다. 본 스킬은 launcher + 사전체크 + run 디렉토리 부트스트랩 책임만 가지고, phase 로직은 `agents/omd-master.md`에 있다.
|
|
9
|
+
|
|
10
|
+
CLI 의존 없음. 모든 부트스트랩은 Bash + Write 툴로 직접 실행한다.
|
|
11
|
+
|
|
12
|
+
## 트리거
|
|
13
|
+
|
|
14
|
+
- `/omd-harness <task>` 명시 호출
|
|
15
|
+
- 사용자가 자연어로 "디자인 하네스 / 시니어 디자이너처럼 / 알아서 디자인" 요청
|
|
16
|
+
|
|
17
|
+
## Step 0 — task 추출
|
|
18
|
+
|
|
19
|
+
슬래시에 task 같이 적었으면 (`/omd-harness 물 음용 유도 메인 화면`) 그 자연어 부분이 task. 빈 슬래시면 한 번 묻기:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
어떤 디자인 작업을 진행할까요?
|
|
23
|
+
shape: "[도메인] + [톤/스타일] + [핵심 화면]" — 예: "토스 스타일 가족용 식단 앱 메인 화면"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Step 1 — Subagent registration 사전체크 (CRITICAL)
|
|
27
|
+
|
|
28
|
+
`omd-master` subagent가 이 세션에 dispatch 가능한지 verify. Agent tool의 사용 가능 subagent 목록에 `omd-master`가 있으면 진행. 없으면:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
omd-master subagent가 이 세션에 등록되어 있지 않아요. Claude Code는 세션 시작 시점에만 .claude/agents/*.md를 로드합니다 — install-skills를 세션 띄운 후에 돌렸으면 이 케이스.
|
|
32
|
+
|
|
33
|
+
해결 (가장 빠른 순서):
|
|
34
|
+
1. 현재 세션에서 /agents 실행 — Claude Code가 .claude/agents/*.md 강제 재스캔. omd-* 8개가 목록에 나타나면 /omd-harness 재호출.
|
|
35
|
+
2. 안 되면: Claude Code 앱 완전 종료 (Cmd+Q / 터미널 자체 quit) → 새 터미널 → claude 재실행 → /omd-harness <task> 재호출.
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Step 2 — Run 디렉토리 부트스트랩 (인라인 Bash)
|
|
39
|
+
|
|
40
|
+
이전엔 `omd harness "<task>" --internal` CLI를 호출했지만 1.0.0부터는 스킬이 직접 한다. 결정론적 hard verify gate:
|
|
41
|
+
|
|
42
|
+
### 2.1 기존 run 재사용 체크
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
ls -t .omd/runs 2>/dev/null | head -1
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
출력 있으면 그 디렉토리의 `task.md`를 Read해서 사용자 task와 의미적으로 일치하는지 확인. 일치하면 그 run 재사용 — Step 3으로 점프.
|
|
49
|
+
|
|
50
|
+
### 2.2 신규 run 부트스트랩
|
|
51
|
+
|
|
52
|
+
다음을 **반드시 정확히 이 순서로** Bash 툴로 실행:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# 2.2.1 — timestamp + slug 결정 (한국어 보존)
|
|
56
|
+
TS=$(node -e "console.log(new Date().toISOString().replace(/[:.]/g,'-'))")
|
|
57
|
+
SLUG=$(node -e "
|
|
58
|
+
const s = process.argv[1].toLowerCase().trim()
|
|
59
|
+
.replace(/[^a-z0-9가-힣\s-]+/g,'')
|
|
60
|
+
.replace(/\s+/g,'-')
|
|
61
|
+
.replace(/-+/g,'-')
|
|
62
|
+
.replace(/^-|-$/g,'');
|
|
63
|
+
console.log(s.slice(0,40) || 'untitled');
|
|
64
|
+
" "<EXTRACTED_TASK>")
|
|
65
|
+
RUN_ID="run-${TS}-${SLUG}"
|
|
66
|
+
RUN_DIR=".omd/runs/${RUN_ID}"
|
|
67
|
+
|
|
68
|
+
# 2.2.2 — 표준 서브폴더 생성
|
|
69
|
+
mkdir -p "${RUN_DIR}"/{wireframes,components,assets/briefs,assets/fallback,assets/pinterest-refs,eval/screenshots,persona-feedback,handoff,checkpoints}
|
|
70
|
+
|
|
71
|
+
# 2.2.3 — task.md
|
|
72
|
+
cat > "${RUN_DIR}/task.md" <<EOF
|
|
73
|
+
# Harness Task
|
|
74
|
+
|
|
75
|
+
<EXTRACTED_TASK>
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
- run_id: \`${RUN_ID}\`
|
|
80
|
+
- started_at: $(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
81
|
+
- cwd: \`$(pwd)\`
|
|
82
|
+
EOF
|
|
83
|
+
|
|
84
|
+
# 2.2.4 — run.log
|
|
85
|
+
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] run initialized" > "${RUN_DIR}/run.log"
|
|
86
|
+
|
|
87
|
+
# 2.2.5 — .omd/.gitignore (idempotent)
|
|
88
|
+
mkdir -p .omd
|
|
89
|
+
[ -f .omd/.gitignore ] || printf "runs/\ncache/\n" > .omd/.gitignore
|
|
90
|
+
|
|
91
|
+
# 2.2.6 — INDEX.md (idempotent header + append)
|
|
92
|
+
INDEX=".omd/runs/INDEX.md"
|
|
93
|
+
[ -f "${INDEX}" ] || cat > "${INDEX}" <<EOF
|
|
94
|
+
# Harness Runs Index
|
|
95
|
+
|
|
96
|
+
One line per run. Append-only.
|
|
97
|
+
|
|
98
|
+
EOF
|
|
99
|
+
TASK_ONELINE=$(echo "<EXTRACTED_TASK>" | tr '\n' ' ' | cut -c1-120)
|
|
100
|
+
echo "- $(date -u +%Y-%m-%dT%H:%M:%SZ) \`${RUN_ID}\` — ${TASK_ONELINE}" >> "${INDEX}"
|
|
101
|
+
|
|
102
|
+
# 2.2.7 — 결과 출력 (이 스킬이 파싱)
|
|
103
|
+
echo "RUN_DIR=${RUN_DIR}"
|
|
104
|
+
echo "RUN_ID=${RUN_ID}"
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### 2.3 Hard verify gate (master spawn 차단 조건)
|
|
108
|
+
|
|
109
|
+
부트스트랩 다음, master spawn 전에 반드시:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
test -d "${RUN_DIR}" && test -f "${RUN_DIR}/task.md" && echo "OK" || echo "FAIL"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
`OK`가 출력되지 않으면 master는 절대 spawn하지 않는다. 사용자에게:
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
하네스 부트스트랩이 실패했어요 (run dir or task.md 누락). 디스크 권한·경로 문제일 수 있어요. 다시 시도하거나 .omd/ 디렉토리를 정리해주세요.
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
이 gate를 통과해야만 Step 3로.
|
|
122
|
+
|
|
123
|
+
## Step 3 — DESIGN.md 존재 확인 + reference 의미 매칭
|
|
124
|
+
|
|
125
|
+
프로젝트 루트에 DESIGN.md 없으면 reference를 직접 추천한다. 외부 API 호출 없음.
|
|
126
|
+
|
|
127
|
+
### 3.1 카탈로그 로드
|
|
128
|
+
|
|
129
|
+
다음 파일을 Read 툴로 전체 로드:
|
|
130
|
+
|
|
131
|
+
- `.claude/data/reference-fingerprints.json` — 67개 reference의 fingerprint (tone keywords, visual theme, antipatterns, signature motion, has_personas, category)
|
|
132
|
+
- `.claude/data/reference-tags.md` — 사람-읽기용 keyword 매트릭스
|
|
133
|
+
- `.claude/data/vocabulary.json` — controlled vocab
|
|
134
|
+
|
|
135
|
+
`.claude/data/`에 없으면 `node_modules/oh-my-design-cli/data/` 또는 패키지 root `data/` 에서 fallback.
|
|
136
|
+
|
|
137
|
+
### 3.2 사용자 task 분석 (silent)
|
|
138
|
+
|
|
139
|
+
- controlled-vocab 키워드 추출 (예: "헬스/웰니스 / calm-blue / 차분" → `[calm, minimal, approachable, warm]`)
|
|
140
|
+
- 명시 brand hint (예: "토스 같은" → `["toss"]`)
|
|
141
|
+
- 카테고리 추측 (Consumer / Productivity / Fintech / AI / Developer Tools / Design Tools / Automotive / Aerospace / SaaS / Enterprise)
|
|
142
|
+
|
|
143
|
+
### 3.3 점수 계산 (in-head, 결정론적)
|
|
144
|
+
|
|
145
|
+
- 각 ref의 `tone_keywords` ∩ task keywords → 1점/매칭
|
|
146
|
+
- brand hint match → +5점
|
|
147
|
+
- 카테고리 일치 → +1점
|
|
148
|
+
- top 5 정렬
|
|
149
|
+
|
|
150
|
+
### 3.4 검증 (hallucination 방지)
|
|
151
|
+
|
|
152
|
+
추천하는 모든 id는 `reference-fingerprints.json`의 `items[].id`에 **반드시** 존재해야 한다. 없는 id는 만들어내지 않는다.
|
|
153
|
+
|
|
154
|
+
### 3.5 사용자에게 제시 (자연어 prose)
|
|
155
|
+
|
|
156
|
+
라벨 없이, 추천을 statement로:
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
DESIGN.md가 없어서 reference 한 개를 골라 부트스트랩할게요. <task 핵심 한 줄>을 보니 <top1.id>가 가장 잘 맞을 것 같아요 — <visual_theme 핵심 + 매칭 키워드 1-2개를 한 줄로>.
|
|
160
|
+
|
|
161
|
+
이대로 가시려면 go (또는 <top1.id>).
|
|
162
|
+
다른 후보: <top2.id> (한 줄 이유) · <top3.id> (...) · <top4.id> (...) · <top5.id> (...)
|
|
163
|
+
본인이 아는 다른 reference면 한 줄로 id만 (예: vercel) — 67개 카탈로그에 없으면 알려드립니다.
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### 3.6 사용자 응답 처리
|
|
167
|
+
|
|
168
|
+
- `go` 또는 reference id (top-5 안) → 그 id로 master spawn
|
|
169
|
+
- 다른 reference id (top-5 밖이지만 카탈로그 안) → 동일하게 진행
|
|
170
|
+
- 카탈로그에 없는 id → "해당 id는 67개 카탈로그에 없어요. top-5 중에서 골라주세요."
|
|
171
|
+
- `중단` → 종료
|
|
172
|
+
|
|
173
|
+
## Step 4 — Master 호출 (handoff loop)
|
|
174
|
+
|
|
175
|
+
Subagent (master)는 AskUserQuestion 직접 호출 불가 (main-thread 전용). file-based handoff 패턴으로 돌린다.
|
|
176
|
+
|
|
177
|
+
### 루프 의사코드
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
spawn_count = 0
|
|
181
|
+
prompt = "<RUN_DIR + task + chosen_ref_id>. Phase 1부터 시작."
|
|
182
|
+
|
|
183
|
+
while spawn_count < 12 (safety cap):
|
|
184
|
+
result = Agent({
|
|
185
|
+
subagent_type: "omd-master",
|
|
186
|
+
description: "Run design harness round N",
|
|
187
|
+
prompt: prompt
|
|
188
|
+
})
|
|
189
|
+
spawn_count += 1
|
|
190
|
+
|
|
191
|
+
handoff_path = "<RUN_DIR>/.handoff.json"
|
|
192
|
+
if not exists(handoff_path):
|
|
193
|
+
relay result text to user; halt
|
|
194
|
+
|
|
195
|
+
handoff = JSON.parse(Read(handoff_path))
|
|
196
|
+
|
|
197
|
+
if handoff.user_prose:
|
|
198
|
+
print handoff.user_prose to user
|
|
199
|
+
|
|
200
|
+
if handoff.status == "done": halt
|
|
201
|
+
if handoff.status == "error": halt + show
|
|
202
|
+
if handoff.status == "ask_user":
|
|
203
|
+
questions = JSON.parse(Read(handoff.questions_file))
|
|
204
|
+
answers = AskUserQuestion({ questions: questions.questions })
|
|
205
|
+
answers_file = "<RUN_DIR>/checkpoints/<handoff.checkpoint_id>.answers.json"
|
|
206
|
+
Write(answers_file, JSON.stringify({checkpoint_id, answers}))
|
|
207
|
+
prompt = "continue checkpoint:" + handoff.checkpoint_id + " — answers at " + answers_file
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Safety cap
|
|
211
|
+
|
|
212
|
+
한 번의 `/omd-harness` 호출에 최대 12 spawn. 초과 시 사용자에게 escalate ("master가 12 spawn 초과, 멈춥니다 — run dir 보존").
|
|
213
|
+
|
|
214
|
+
### 재진입
|
|
215
|
+
|
|
216
|
+
사용자가 자연어로 "go" / "fix X" 답하면 동일 loop 재시작. master는 `.handoff.json` 보고 어디까지 갔는지 파악.
|
|
217
|
+
|
|
218
|
+
## 사용자 체크포인트 처리
|
|
219
|
+
|
|
220
|
+
Master가 체크포인트에서 turn을 종료한 후 다음 사용자 메시지가:
|
|
221
|
+
|
|
222
|
+
- **하네스 컨텍스트 안의 응답** (예: "go", "fix the home screen IA", "stop") → master 재spawn + 그대로 전달
|
|
223
|
+
- **다른 작업으로 바뀐 메시지** → run 디렉토리에 `paused.flag` 생성. 나중에 `/omd-harness resume` 하면 재개
|
|
224
|
+
|
|
225
|
+
## 산출물 위치 (master가 emit, 이 스킬은 안내만)
|
|
226
|
+
|
|
227
|
+
```
|
|
228
|
+
.omd/runs/run-<ts>-<slug>/
|
|
229
|
+
├── task.md
|
|
230
|
+
├── brief.md
|
|
231
|
+
├── references-cited.md
|
|
232
|
+
├── journey.mmd
|
|
233
|
+
├── wireframes/
|
|
234
|
+
├── DESIGN.md.patch
|
|
235
|
+
├── components/
|
|
236
|
+
│ ├── manifest.json
|
|
237
|
+
│ └── microcopy.json
|
|
238
|
+
├── assets/
|
|
239
|
+
│ ├── brief.md
|
|
240
|
+
│ ├── manifest.json
|
|
241
|
+
│ ├── briefs/
|
|
242
|
+
│ ├── fallback/
|
|
243
|
+
│ └── pinterest-refs/
|
|
244
|
+
├── eval/
|
|
245
|
+
│ ├── deterministic.json
|
|
246
|
+
│ ├── jury.json
|
|
247
|
+
│ └── screenshots/
|
|
248
|
+
├── persona-feedback/
|
|
249
|
+
│ └── <persona>.json
|
|
250
|
+
├── critique.md
|
|
251
|
+
├── handoff/
|
|
252
|
+
│ ├── v0.zip
|
|
253
|
+
│ ├── cursor.zip
|
|
254
|
+
│ └── subframe.zip
|
|
255
|
+
├── run.log
|
|
256
|
+
└── postmortem.md
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## 이 스킬이 하지 않는 것
|
|
260
|
+
|
|
261
|
+
- Phase 로직 실행 (master)
|
|
262
|
+
- Sub-agent 직접 spawn (master)
|
|
263
|
+
- 사용자 응답 해석/라우팅 (master)
|
|
264
|
+
- DESIGN.md 직접 수정 (Phase 5에서 master)
|
|
265
|
+
|
|
266
|
+
## 금지
|
|
267
|
+
|
|
268
|
+
- Master 없이 phase를 직접 수행하지 말 것
|
|
269
|
+
- 사용자 체크포인트를 자동 승인하지 말 것
|
|
270
|
+
- Run 디렉토리를 임의로 정리/삭제하지 말 것
|
|
271
|
+
- Step 2.3 verify gate 통과 전에 master spawn 절대 금지
|
|
@@ -5,19 +5,18 @@ description: "누적된 preference 교정사항을 DESIGN.md에 반영합니다.
|
|
|
5
5
|
|
|
6
6
|
# omd:learn — Preference Fold into DESIGN.md
|
|
7
7
|
|
|
8
|
-
`.omd/preferences.md`에 누적된 `status: pending` 교정사항을 DESIGN.md에 반영하고, 반영된 엔트리의 상태를 `applied`로 플립한다.
|
|
8
|
+
`.omd/preferences.md`에 누적된 `status: pending` 교정사항을 DESIGN.md에 반영하고, 반영된 엔트리의 상태를 `applied`로 플립한다. **CLI 호출 없음** — Read/Edit 툴로 직접 처리.
|
|
9
9
|
|
|
10
10
|
## Phase 1 — 검토
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
`Read .omd/preferences.md` → frontmatter + 엔트리들 파싱:
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
omd
|
|
16
|
-
|
|
14
|
+
- 엔트리 분리: `## ` heading 기준 split
|
|
15
|
+
- 각 엔트리의 `omd-meta` 코드블록에서 `id`, `scope`, `status` 추출
|
|
16
|
+
- `status: pending`만 필터
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
scope별로 그룹화해서 사용자에게 요약:
|
|
19
19
|
|
|
20
|
-
예시 출력 형식:
|
|
21
20
|
```
|
|
22
21
|
components.button (3 pending):
|
|
23
22
|
- CTAs never uppercase (pref_xxx, pref_yyy)
|
|
@@ -27,55 +26,76 @@ spacing (1 pending):
|
|
|
27
26
|
- 8pt grid, not 4pt (pref_aaa)
|
|
28
27
|
```
|
|
29
28
|
|
|
29
|
+
엔트리당 한 줄이 아니라 **scope당 2-3줄로 의도 정리**.
|
|
30
|
+
|
|
30
31
|
## Phase 2 — 사용자 확인
|
|
31
32
|
|
|
32
|
-
"이 교정들을 DESIGN.md에 반영할까요?"
|
|
33
|
+
"이 교정들을 DESIGN.md에 반영할까요?" 묻기. 동의 → Phase 3.
|
|
34
|
+
|
|
35
|
+
거부 → 어떤 scope를 reject할지 묻고 Phase 4 reject 분기로.
|
|
33
36
|
|
|
34
|
-
## Phase 3 — 적용
|
|
37
|
+
## Phase 3 — DESIGN.md 적용
|
|
35
38
|
|
|
36
|
-
1.
|
|
37
|
-
2. scope별로 묶어서 **하나의 coherent edit
|
|
38
|
-
3. Edit 툴로 DESIGN.md의 해당
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
1. `Read DESIGN.md` 로드
|
|
40
|
+
2. scope별로 묶어서 **하나의 coherent edit** 생성 (엔트리당 하나가 아니라 한 scope의 교정들을 종합)
|
|
41
|
+
3. Edit 툴로 DESIGN.md의 해당 섹션 수정:
|
|
42
|
+
- `components.button` → DESIGN.md §8 (Components → Button) 또는 §13 (Components 상세)
|
|
43
|
+
- `color` → §2 (Color Palette)
|
|
44
|
+
- `typography` → §3
|
|
45
|
+
- `spacing` → §4 (Spacing scale)
|
|
46
|
+
- `voice` → §10 (Voice & Tone)
|
|
47
|
+
- `motion` → §15 (Motion & Easing)
|
|
48
|
+
- `visualTheme` → §1 (Visual Theme)
|
|
49
|
+
4. **voice/내러티브 수정 시 DESIGN.md의 기존 문체 preserve** — 교정 내용만 반영, 문장 스타일/길이/톤 유지
|
|
50
|
+
5. **§10-15 (Brand Philosophy 레이어)는 reference voice 보존이 우선** — preference가 §10-15 본문 자체를 다시 쓰라고 하지 않는 한 본문은 건드리지 않고 §1-9의 axes만 수정
|
|
44
51
|
|
|
45
52
|
## Phase 4 — 상태 플립
|
|
46
53
|
|
|
47
|
-
반영한
|
|
54
|
+
반영한 엔트리: 해당 엔트리의 omd-meta 블록을 Edit 툴로:
|
|
55
|
+
- `status: pending` → `status: applied`
|
|
56
|
+
- `applied_at: <ISO timestamp>` 라인 추가
|
|
57
|
+
- (선택) `applied_design_md_hash: <DESIGN.md sha256>` 추가. hash 계산:
|
|
58
|
+
```bash
|
|
59
|
+
node -e "console.log(require('crypto').createHash('sha256').update(require('fs').readFileSync('DESIGN.md')).digest('hex').slice(0,12))"
|
|
60
|
+
```
|
|
48
61
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
62
|
+
거부한 엔트리:
|
|
63
|
+
- `status: pending` → `status: rejected`
|
|
64
|
+
- `rejected_reason: "<짧은 이유>"` 라인 추가
|
|
52
65
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
omd learn --mark-rejected <pref_id> --reason "<짧은 이유>"
|
|
57
|
-
```
|
|
66
|
+
상위 엔트리가 누적된 작은 교정을 통합·대체했으면:
|
|
67
|
+
- 작은 엔트리들은 `status: superseded`
|
|
68
|
+
- `superseded_by: <상위 pref_id>` 추가
|
|
58
69
|
|
|
59
70
|
## Phase 5 — 결과 요약
|
|
60
71
|
|
|
61
|
-
한
|
|
72
|
+
한 문단:
|
|
62
73
|
- 반영된 교정 수 (scope별)
|
|
63
74
|
- 거부된 교정 수 + 이유
|
|
64
|
-
- 사용자에게 `.omd/preferences.md` 직접 확인
|
|
75
|
+
- 사용자에게 `.omd/preferences.md` 직접 확인 안내
|
|
65
76
|
|
|
66
|
-
예시:
|
|
67
77
|
```
|
|
68
|
-
|
|
78
|
+
4 preferences applied to DESIGN.md
|
|
69
79
|
- components.button: CTAs never uppercase, primary brand-500
|
|
70
80
|
- spacing: 8pt grid
|
|
71
|
-
|
|
81
|
+
1 rejected (conflicts with base reference radius)
|
|
72
82
|
|
|
73
83
|
Review .omd/preferences.md for details.
|
|
74
84
|
```
|
|
75
85
|
|
|
86
|
+
## 옵션 패턴
|
|
87
|
+
|
|
88
|
+
사용자가 특정 작업만 요청하는 경우:
|
|
89
|
+
|
|
90
|
+
- **"pending만 보여줘"** → Phase 1만, Phase 2-5 생략
|
|
91
|
+
- **"X scope만 반영"** → 해당 scope만 Phase 3에서 처리
|
|
92
|
+
- **"<pref_id>를 applied로 표시"** → Phase 4의 single-entry 플립만
|
|
93
|
+
- **"<pref_id>를 rejected로 표시 + 이유"** → 동일
|
|
94
|
+
|
|
76
95
|
## 금지
|
|
77
96
|
|
|
78
|
-
- LLM으로 엔트리별 개별 diff를 생성하지 말 것 — scope별 합쳐서 하나의 coherent edit
|
|
79
|
-
- DESIGN.md의 section
|
|
80
|
-
- 교정과 관계없는 부분을 "개선"하지 말
|
|
81
|
-
- pending을 건너뛰지 말 것 — 모든 pending에
|
|
97
|
+
- LLM으로 엔트리별 개별 diff를 생성하지 말 것 — scope별 합쳐서 하나의 coherent edit
|
|
98
|
+
- DESIGN.md의 section heading 계층을 바꾸지 말 것
|
|
99
|
+
- 교정과 관계없는 부분을 "개선"하지 말 것
|
|
100
|
+
- pending을 건너뛰지 말 것 — 모든 pending에 applied/rejected/superseded 중 하나로 플립
|
|
101
|
+
- omd-meta 블록 외부 (body) 수정 금지 — 교정 본문은 영구 기록
|
|
@@ -5,7 +5,7 @@ description: "디자인 선호/교정을 .omd/preferences.md 에 append합니다
|
|
|
5
5
|
|
|
6
6
|
# omd:remember — Preference Logger
|
|
7
7
|
|
|
8
|
-
사용자의 디자인 선호/교정을 `.omd/preferences.md`에 append-only로 기록한다. 나중에 `omd:learn`이 배치로 DESIGN.md에 반영.
|
|
8
|
+
사용자의 디자인 선호/교정을 `.omd/preferences.md`에 append-only로 기록한다. 나중에 `omd:learn`이 배치로 DESIGN.md에 반영. **CLI 호출 없음** — Read/Edit/Write 툴로 직접 처리.
|
|
9
9
|
|
|
10
10
|
## 트리거 발화 패턴
|
|
11
11
|
|
|
@@ -15,30 +15,108 @@ description: "디자인 선호/교정을 .omd/preferences.md 에 append합니다
|
|
|
15
15
|
- "rule of thumb: ..."
|
|
16
16
|
- 사용자가 당신의 디자인 선택을 명시적으로 교정
|
|
17
17
|
|
|
18
|
-
##
|
|
18
|
+
## 파일 포맷 (`.omd/preferences.md`)
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
frontmatter + 엔트리 시퀀스. 엔트리 하나당 `## <heading>` + `omd-meta` 코드블록 + body.
|
|
21
21
|
|
|
22
|
+
```
|
|
23
|
+
---
|
|
24
|
+
schema: omd.preferences/v1
|
|
25
|
+
design_md_hash_at_creation: <hash 또는 빈 문자열>
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
# Preference Log
|
|
29
|
+
|
|
30
|
+
## 2026-04-30T17:48:00.000Z — ctas-never-uppercase
|
|
31
|
+
|
|
32
|
+
```omd-meta
|
|
33
|
+
id: pref_lqxk2_a3f9c1d4
|
|
34
|
+
timestamp: 2026-04-30T17:48:00.000Z
|
|
35
|
+
scope: components.button
|
|
36
|
+
signal: user-statement
|
|
37
|
+
confidence: explicit
|
|
38
|
+
status: pending
|
|
39
|
+
source_agent: claude-code
|
|
40
|
+
source_context: "src/components/Button.tsx"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
CTAs are never uppercase
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## 실행 절차
|
|
47
|
+
|
|
48
|
+
### Step 1 — note 정규화
|
|
49
|
+
사용자 발화를 한 문장 영문으로 요약 (예: "앞으로 CTA 대문자 쓰지마" → `CTAs are never uppercase`).
|
|
50
|
+
|
|
51
|
+
### Step 2 — scope 추론
|
|
52
|
+
note 내용에서 다음 매핑 우선순위 사용:
|
|
53
|
+
|
|
54
|
+
| 매칭 키워드 (정규식, case-i) | scope |
|
|
55
|
+
|---|---|
|
|
56
|
+
| `\b(buttons?\|ctas?\|btns?)\b` | `components.button` |
|
|
57
|
+
| `\b(cards?)\b` | `components.card` |
|
|
58
|
+
| `\b(dialogs?\|modals?)\b` | `components.dialog` |
|
|
59
|
+
| `\b(inputs?\|fields?\|forms?)\b` | `components.input` |
|
|
60
|
+
| `\b(nav\|navigation\|headers?\|menus?)\b` | `components.navigation` |
|
|
61
|
+
| `\b(badges?\|chips?\|pills?\|tags?)\b` | `components.badge` |
|
|
62
|
+
| `\b(tables?\|rows?\|cells?)\b` | `components.table` |
|
|
63
|
+
| `\b(dropdowns?\|selects?\|comboboxes?)\b` | `components.dropdown` |
|
|
64
|
+
| `\b(toasts?\|notifications?\|snackbars?)\b` | `components.toast` |
|
|
65
|
+
| `\b(tabs?)\b` | `components.tabs` |
|
|
66
|
+
| `\b(colors?\|palette\|hex\|hue\|saturation\|shades?\|tints?\|gradients?)\b` | `color` |
|
|
67
|
+
| `\b(font\|typography\|typeface\|weight\|leading\|tracking\|letter-?spacing)\b` | `typography` |
|
|
68
|
+
| `\b(spacing\|gap\|padding\|margin\|grid)\b` | `spacing` |
|
|
69
|
+
| `\b(voice\|tone\|copy\|microcopy\|wording\|language)\b` | `voice` |
|
|
70
|
+
| `\b(motion\|animation\|transition\|easing\|duration)\b` | `motion` |
|
|
71
|
+
| `\b(layout\|structure\|hierarchy)\b` | `layout` |
|
|
72
|
+
| `\b(theme\|aesthetic\|vibe\|mood\|look\|feel)\b` | `visualTheme` |
|
|
73
|
+
| (어느 것도 매칭 X) | `visualTheme` |
|
|
74
|
+
|
|
75
|
+
사용자가 명시적으로 scope를 지정했으면 그대로 사용.
|
|
76
|
+
|
|
77
|
+
### Step 3 — id 생성
|
|
78
|
+
형식: `pref_<base36 timestamp>_<8 hex chars>`. Bash로 한 줄:
|
|
22
79
|
```bash
|
|
23
|
-
|
|
80
|
+
node -e "console.log('pref_' + Date.now().toString(36) + '_' + require('crypto').randomBytes(4).toString('hex'))"
|
|
24
81
|
```
|
|
25
82
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
-
|
|
29
|
-
|
|
83
|
+
### Step 4 — slug + heading
|
|
84
|
+
```bash
|
|
85
|
+
node -e "console.log(process.argv[1].toLowerCase().replace(/[^a-z0-9]+/g,'-').replace(/^-+|-+$/g,'').slice(0,40) || 'entry')" "<note>"
|
|
86
|
+
```
|
|
87
|
+
heading: `<ISO timestamp> — <slug>`
|
|
88
|
+
|
|
89
|
+
### Step 5 — 파일 read/append
|
|
90
|
+
1. `Read .omd/preferences.md` — 없으면 frontmatter+header부터 만든다:
|
|
91
|
+
```
|
|
92
|
+
---
|
|
93
|
+
schema: omd.preferences/v1
|
|
94
|
+
design_md_hash_at_creation:
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
# Preference Log
|
|
30
98
|
|
|
31
|
-
|
|
99
|
+
```
|
|
100
|
+
2. 새 엔트리 블록을 **파일 끝에 append** (Edit 툴, old_string은 마지막 라인, new_string은 마지막 라인 + 빈 줄 + 새 엔트리). 또는 Write로 전체 재작성.
|
|
101
|
+
3. 메타 필드:
|
|
102
|
+
- `id`, `timestamp`, `scope` (필수)
|
|
103
|
+
- `signal: user-statement` (디폴트, 사용자가 직접 발화한 경우) 또는 `user-correction` (사용자가 당신의 선택을 교정한 경우)
|
|
104
|
+
- `confidence: explicit` (디폴트, 사용자가 명시적으로 말한 경우) 또는 `inferred` (당신이 사용자 행동에서 추론한 경우)
|
|
105
|
+
- `status: pending` (모든 새 엔트리)
|
|
106
|
+
- `source_agent`: 환경에서 추론 (`claude-code` / `codex` / `opencode` / `cursor`)
|
|
107
|
+
- `source_context`: 관련 파일 경로 또는 PR번호 (있으면 JSON.stringify로 quote)
|
|
32
108
|
|
|
33
|
-
|
|
109
|
+
### Step 6 — 응답
|
|
110
|
+
간결한 한 줄: `Logged ${id} to .omd/preferences.md (scope: ${scope})`
|
|
34
111
|
|
|
35
112
|
## omd:apply 스킬과의 관계
|
|
36
113
|
|
|
37
|
-
- **자동 감지**: 일반 UI 작업 중
|
|
38
|
-
- **명시적 호출**:
|
|
114
|
+
- **자동 감지**: 일반 UI 작업 중 교정 발생 시 `omd:apply`가 이 스킬 트리거
|
|
115
|
+
- **명시적 호출**: 디자인 원칙 선언만 할 때 직접 트리거
|
|
39
116
|
|
|
40
117
|
## 금지
|
|
41
118
|
|
|
42
|
-
- `.omd/preferences.md` 파일을 직접
|
|
43
|
-
- 같은
|
|
44
|
-
- 사용자에게 "기록할까요?" 묻지 말 것 — 감지 즉시 기록 + 간결
|
|
119
|
+
- `.omd/preferences.md` 파일을 frontmatter 외 직접 손대지 말 것 (id 충돌 방지) — 항상 위 절차로
|
|
120
|
+
- 같은 세션 내 동일 내용 중복 기록 금지
|
|
121
|
+
- 사용자에게 "기록할까요?" 묻지 말 것 — 감지 즉시 기록 + 간결 알림
|
|
122
|
+
- frontmatter의 `design_md_hash_at_creation`은 첫 생성 시에만 채움 (이후 절대 수정 X)
|