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.
Files changed (74) hide show
  1. package/.claude/hooks/post-edit-watch.cjs +99 -0
  2. package/.claude/hooks/session-end-foldin.cjs +96 -0
  3. package/.claude/hooks/session-state-loader.cjs +64 -0
  4. package/.claude/hooks/skill-activation.cjs +73 -0
  5. package/.claude/settings.json +55 -0
  6. package/.claude/skills/skill-rules.json +87 -0
  7. package/AGENTS.md +111 -0
  8. package/README.md +75 -202
  9. package/agents/AGENT.md +53 -0
  10. package/agents/omd-3d-blender.md +269 -0
  11. package/agents/omd-a11y-auditor.md +97 -0
  12. package/agents/omd-asset-curator.md +260 -0
  13. package/agents/omd-critic.md +181 -0
  14. package/agents/omd-master.md +548 -0
  15. package/agents/omd-microcopy.md +63 -0
  16. package/agents/omd-persona-tester.md +118 -0
  17. package/agents/omd-ui-junior.md +129 -0
  18. package/agents/omd-ux-engineer.md +265 -0
  19. package/agents/omd-ux-researcher.md +62 -0
  20. package/agents/omd-ux-writer.md +181 -0
  21. package/data/opt-out-corpus.json +141 -0
  22. package/data/reference-fingerprints.json +1495 -0
  23. package/dist/bin/oh-my-design.js +3 -818
  24. package/dist/bin/oh-my-design.js.map +1 -1
  25. package/dist/install-skills-SVIYKXOE.js +442 -0
  26. package/dist/install-skills-SVIYKXOE.js.map +1 -0
  27. package/package.json +23 -23
  28. package/scripts/context.cjs +91 -0
  29. package/scripts/postinstall.cjs +54 -0
  30. package/skills/omd-apply/SKILL.md +64 -53
  31. package/skills/omd-harness/SKILL.md +271 -0
  32. package/skills/omd-learn/SKILL.md +55 -35
  33. package/skills/omd-remember/SKILL.md +93 -15
  34. package/skills/omd-sync/SKILL.md +140 -16
  35. package/dist/chunk-6YNSV3VY.js +0 -35
  36. package/dist/chunk-6YNSV3VY.js.map +0 -1
  37. package/dist/chunk-MHFYGZSO.js +0 -337
  38. package/dist/chunk-MHFYGZSO.js.map +0 -1
  39. package/dist/chunk-N2JG6N4Q.js +0 -264
  40. package/dist/chunk-N2JG6N4Q.js.map +0 -1
  41. package/dist/chunk-OOQQEUGX.js +0 -46
  42. package/dist/chunk-OOQQEUGX.js.map +0 -1
  43. package/dist/chunk-OR5DHENY.js +0 -250
  44. package/dist/chunk-OR5DHENY.js.map +0 -1
  45. package/dist/customizer-CM76752R.js +0 -8
  46. package/dist/customizer-CM76752R.js.map +0 -1
  47. package/dist/index.d.ts +0 -559
  48. package/dist/index.js +0 -3113
  49. package/dist/index.js.map +0 -1
  50. package/dist/init-UMM4XIV5.js +0 -675
  51. package/dist/init-UMM4XIV5.js.map +0 -1
  52. package/dist/install-skills-CM6VXFZJ.js +0 -152
  53. package/dist/install-skills-CM6VXFZJ.js.map +0 -1
  54. package/dist/learn-33LHKEJA.js +0 -140
  55. package/dist/learn-33LHKEJA.js.map +0 -1
  56. package/dist/reference-YMNAOXJQ.js +0 -47
  57. package/dist/reference-YMNAOXJQ.js.map +0 -1
  58. package/dist/reference-parser-TM3CJPNE.js +0 -10
  59. package/dist/reference-parser-TM3CJPNE.js.map +0 -1
  60. package/dist/remember-UAFA5B2O.js +0 -78
  61. package/dist/remember-UAFA5B2O.js.map +0 -1
  62. package/dist/sync-FDYRKNFE.js +0 -417
  63. package/dist/sync-FDYRKNFE.js.map +0 -1
  64. package/dist/templates/templates/design-md.hbs +0 -44
  65. package/dist/templates/templates/partials/agent-prompt-guide.hbs +0 -28
  66. package/dist/templates/templates/partials/color-palette.hbs +0 -49
  67. package/dist/templates/templates/partials/component-stylings.hbs +0 -28
  68. package/dist/templates/templates/partials/depth-elevation.hbs +0 -31
  69. package/dist/templates/templates/partials/dos-donts.hbs +0 -13
  70. package/dist/templates/templates/partials/layout.hbs +0 -30
  71. package/dist/templates/templates/partials/responsive.hbs +0 -25
  72. package/dist/templates/templates/partials/shadcn-tokens.hbs +0 -64
  73. package/dist/templates/templates/partials/typography.hbs +0 -43
  74. 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
- Bash 툴로:
12
+ `Read .omd/preferences.md` → frontmatter + 엔트리들 파싱:
13
13
 
14
- ```bash
15
- omd learn
16
- ```
14
+ - 엔트리 분리: `## ` heading 기준 split
15
+ - 각 엔트리의 `omd-meta` 코드블록에서 `id`, `scope`, `status` 추출
16
+ - `status: pending`만 필터
17
17
 
18
- 실행하면 pending 엔트리를 scope별로 그룹화해서 보여준다. 출력을 읽고 사용자에게 **그룹별 요약**을 제시한다 (각 엔트리를 나열하지 말고, scope당 2-3줄로 의도 정리).
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에 반영할까요?" 묻는다. 사용자가 동의하면 Phase 3.
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. **DESIGN.md Read 툴로 로드**한다.
37
- 2. scope별로 묶어서 **하나의 coherent edit**을 생성한다 (엔트리당 하나의 edit이 아니라, 한 scope의 교정들을 종합).
38
- 3. Edit 툴로 DESIGN.md의 해당 섹션을 수정.
39
- 4. **voice 섹션이나 내러티브를 수정할 때는 DESIGN.md 기존 문체를 preserve**한다 교정 내용만 반영하되 문장 스타일, 길이, 톤은 유지.
40
- 5. 수정 후 `DESIGN.md`의 hash를 계산. Bash:
41
- ```bash
42
- omd sync --force # shim들의 hash 갱신 (DESIGN.md 해시는 pointer라 바뀌지 않지만 lock만 업데이트)
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
- ```bash
50
- omd learn --mark-applied <pref_id>
51
- ```
62
+ 거부한 엔트리:
63
+ - `status: pending` → `status: rejected`
64
+ - `rejected_reason: "<짧은 이유>"` 라인 추가
52
65
 
53
- 반영 엔트리(충돌, 거부)는:
54
-
55
- ```bash
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
- 4 preferences applied to DESIGN.md
78
+ 4 preferences applied to DESIGN.md
69
79
  - components.button: CTAs never uppercase, primary brand-500
70
80
  - spacing: 8pt grid
71
- 1 rejected (conflicts with base reference radius)
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 구조(heading 계층)를 바꾸지 말 것.
80
- - 교정과 관계없는 부분을 "개선"하지 말 것.
81
- - pending을 건너뛰지 말 것 — 모든 pending에 대해 applied or rejected 플립해야 한다.
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
- Bash 툴로 실행:
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
- omd remember "<사용자 발화를 문장 영문으로 요약>"
80
+ node -e "console.log('pref_' + Date.now().toString(36) + '_' + require('crypto').randomBytes(4).toString('hex'))"
24
81
  ```
25
82
 
26
- 옵션:
27
- - `--scope <value>` — 명시적으로 지정할 때. 기본값은 note 내용에서 자동 추론. 허용: `visualTheme | color | typography | spacing | voice | motion | layout | components.<name>`
28
- - `--context <text>` — 관련 파일 경로나 PR 번호 (e.g. `src/components/Button.tsx`, `#1234`)
29
- - `--agent <name>` — 보통 생략 (환경변수 자동 감지). override 필요 시: `claude-code | codex | opencode | cursor`
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
- 커맨드 출력(`Logged pref_xxx ... → ./.omd/preferences.md`)을 그대로 보여주면 된다. 사용자에게 장황한 확인 불필요.
109
+ ### Step 6 응답
110
+ 간결한 한 줄: `Logged ${id} to .omd/preferences.md (scope: ${scope})`
34
111
 
35
112
  ## omd:apply 스킬과의 관계
36
113
 
37
- - **자동 감지**: 일반 UI 작업 중 교정이 발생하면 `omd:apply` 스킬이 자동으로 커맨드를 호출한다.
38
- - **명시적 호출**: 사용자가 UI 작업 없이 디자인 원칙을 선언하는 경우, 스킬이 직접 트리거된다.
114
+ - **자동 감지**: 일반 UI 작업 중 교정 발생 `omd:apply`가스킬 트리거
115
+ - **명시적 호출**: 디자인 원칙 선언만 직접 트리거
39
116
 
40
117
  ## 금지
41
118
 
42
- - `.omd/preferences.md` 파일을 직접 편집하지 말 것 — 항상 `omd remember` 경유.
43
- - 같은 내용을 중복 기록하지 (같은 세션 내에서).
44
- - 사용자에게 "기록할까요?" 묻지 말 것 — 감지 즉시 기록 + 간결 알림.
119
+ - `.omd/preferences.md` 파일을 frontmatter 외 직접 손대지 말 것 (id 충돌 방지) — 항상 절차로
120
+ - 같은 세션 동일 내용 중복 기록 금지
121
+ - 사용자에게 "기록할까요?" 묻지 말 것 — 감지 즉시 기록 + 간결 알림
122
+ - frontmatter의 `design_md_hash_at_creation`은 첫 생성 시에만 채움 (이후 절대 수정 X)