oh-my-design-cli 0.1.3 → 1.0.1

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 (77) 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 +75 -0
  5. package/.claude/settings.json +55 -0
  6. package/AGENTS.md +111 -0
  7. package/README.ja.md +1 -1
  8. package/README.ko.md +1 -1
  9. package/README.md +76 -203
  10. package/README.zh-TW.md +1 -1
  11. package/agents/AGENT.md +53 -0
  12. package/agents/omd-3d-blender.md +269 -0
  13. package/agents/omd-a11y-auditor.md +97 -0
  14. package/agents/omd-asset-curator.md +260 -0
  15. package/agents/omd-critic.md +181 -0
  16. package/agents/omd-master.md +548 -0
  17. package/agents/omd-microcopy.md +63 -0
  18. package/agents/omd-persona-tester.md +118 -0
  19. package/agents/omd-ui-junior.md +129 -0
  20. package/agents/omd-ux-engineer.md +265 -0
  21. package/agents/omd-ux-researcher.md +62 -0
  22. package/agents/omd-ux-writer.md +181 -0
  23. package/data/opt-out-corpus.json +141 -0
  24. package/data/reference-fingerprints.json +1495 -0
  25. package/dist/bin/oh-my-design.js +3 -818
  26. package/dist/bin/oh-my-design.js.map +1 -1
  27. package/dist/install-skills-GQPTQF5S.js +420 -0
  28. package/dist/install-skills-GQPTQF5S.js.map +1 -0
  29. package/package.json +22 -21
  30. package/scripts/context.cjs +91 -0
  31. package/scripts/postinstall.cjs +54 -0
  32. package/skills/omd-apply/SKILL.md +64 -53
  33. package/skills/omd-harness/SKILL.md +271 -0
  34. package/skills/omd-init/SKILL.md +1 -1
  35. package/skills/omd-learn/SKILL.md +56 -36
  36. package/skills/omd-remember/SKILL.md +94 -16
  37. package/skills/omd-sync/SKILL.md +141 -17
  38. package/dist/chunk-6YNSV3VY.js +0 -35
  39. package/dist/chunk-6YNSV3VY.js.map +0 -1
  40. package/dist/chunk-MHFYGZSO.js +0 -337
  41. package/dist/chunk-MHFYGZSO.js.map +0 -1
  42. package/dist/chunk-N2JG6N4Q.js +0 -264
  43. package/dist/chunk-N2JG6N4Q.js.map +0 -1
  44. package/dist/chunk-OOQQEUGX.js +0 -46
  45. package/dist/chunk-OOQQEUGX.js.map +0 -1
  46. package/dist/chunk-OR5DHENY.js +0 -250
  47. package/dist/chunk-OR5DHENY.js.map +0 -1
  48. package/dist/customizer-CM76752R.js +0 -8
  49. package/dist/customizer-CM76752R.js.map +0 -1
  50. package/dist/index.d.ts +0 -559
  51. package/dist/index.js +0 -3113
  52. package/dist/index.js.map +0 -1
  53. package/dist/init-UMM4XIV5.js +0 -675
  54. package/dist/init-UMM4XIV5.js.map +0 -1
  55. package/dist/install-skills-CM6VXFZJ.js +0 -152
  56. package/dist/install-skills-CM6VXFZJ.js.map +0 -1
  57. package/dist/learn-33LHKEJA.js +0 -140
  58. package/dist/learn-33LHKEJA.js.map +0 -1
  59. package/dist/reference-YMNAOXJQ.js +0 -47
  60. package/dist/reference-YMNAOXJQ.js.map +0 -1
  61. package/dist/reference-parser-TM3CJPNE.js +0 -10
  62. package/dist/reference-parser-TM3CJPNE.js.map +0 -1
  63. package/dist/remember-UAFA5B2O.js +0 -78
  64. package/dist/remember-UAFA5B2O.js.map +0 -1
  65. package/dist/sync-FDYRKNFE.js +0 -417
  66. package/dist/sync-FDYRKNFE.js.map +0 -1
  67. package/dist/templates/templates/design-md.hbs +0 -44
  68. package/dist/templates/templates/partials/agent-prompt-guide.hbs +0 -28
  69. package/dist/templates/templates/partials/color-palette.hbs +0 -49
  70. package/dist/templates/templates/partials/component-stylings.hbs +0 -28
  71. package/dist/templates/templates/partials/depth-elevation.hbs +0 -31
  72. package/dist/templates/templates/partials/dos-donts.hbs +0 -13
  73. package/dist/templates/templates/partials/layout.hbs +0 -30
  74. package/dist/templates/templates/partials/responsive.hbs +0 -25
  75. package/dist/templates/templates/partials/shadcn-tokens.hbs +0 -64
  76. package/dist/templates/templates/partials/typography.hbs +0 -43
  77. package/dist/templates/templates/partials/visual-theme.hbs +0 -26
@@ -1,23 +1,22 @@
1
1
  ---
2
2
  name: omd:learn
3
- description: "누적된 preference 교정사항을 DESIGN.md에 반영합니다. '프리퍼런스 정리해줘', 'DESIGN.md 업데이트', 'preference 반영', 'fold preferences', 'learn from corrections' 같은 요청에 트리거됩니다."
3
+ description: ".omd/preferences.md의 status:pending 항목을 DESIGN.md에 정식 merge하고 status를 applied로 플립. '프리퍼런스 정리해줘', 'fold preferences', 'apply all corrections', 「好みをDESIGN.mdに反映」, 「套用偏好」류의 요청에 트리거. 단발성 교정 기록은 omd:remember."
4
4
  ---
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) 수정 금지 — 교정 본문은 영구 기록
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: omd:remember
3
- description: "디자인 선호/교정을 .omd/preferences.md append합니다. 사용자가 '기억해', '앞으로는 ~해', '우리는 ~하지 않아', 'remember that' 같은 발화를 하거나 디자인 원칙을 명시할트리거됩니다."
3
+ description: "사용자의 디자인 선호·교정을 .omd/preferences.md에 기록. '이거 기억해줘', '앞으로는 이렇게', 'remember this', 'going forward never X', 「覚えておいて」, 「記住這個」류의 발화 또는 omd:apply가 교정을 감지했을 트리거. 기록된 내용은 omd:learn으로 DESIGN.md에 정식 반영."
4
4
  ---
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)
@@ -1,38 +1,162 @@
1
1
  ---
2
2
  name: omd:sync
3
- description: "DESIGN.md shim 파일들(CLAUDE.md / AGENTS.md / .cursor/rules/omd-design.mdc)을 관리합니다. 'shim 업데이트', 'AGENTS.md 동기화', 'CLAUDE.md drift 확인' 같은 요청에 트리거됩니다."
3
+ description: "DESIGN.md 변경분을 CLAUDE.md / AGENTS.md / .cursor/rules/omd-design.mdc shim 3종에 전파. 'shim 갱신', 'drift 확인', 'ship', 'publish', 「shimを更新」, 「同步 CLAUDE.md」류의 요청에 트리거. DESIGN.md가 수정됐는데 shim이 오래됐을 때 자동 감지해 제안하기도 함."
4
4
  ---
5
5
 
6
6
  # omd:sync — Shim Maintenance
7
7
 
8
- DESIGN.md가 모든 주요 AI 코딩 에이전트 (Claude Code, Codex, OpenCode, Cursor)에게 보이도록 shim 파일 3종을 관리한다.
8
+ DESIGN.md가 모든 주요 AI 코딩 에이전트(Claude Code, Codex, OpenCode, Cursor)에게 보이도록 shim 파일 3종을 관리한다. **CLI 호출 없음** — Read/Write/Edit 툴로 직접 처리.
9
9
 
10
- ## 관리 대상
10
+ ## 관리 대상 (3 파일)
11
11
 
12
- - `CLAUDE.md` Claude Code용 `@./DESIGN.md` import
13
- - `AGENTS.md` — Codex CLI + OpenCode 공용 pointer
14
- - `.cursor/rules/omd-design.mdc` Cursor auto-attach rule
12
+ | ID | 경로 | 모드 |
13
+ |---|---|---|
14
+ | `claude` | `CLAUDE.md` | block (managed marker만) |
15
+ | `agents` | `AGENTS.md` | block (managed marker만) |
16
+ | `cursor` | `.cursor/rules/omd-design.mdc` | whole (전체 파일이 omd 전용) |
15
17
 
16
- 파일은 `<!-- omd:start v=1 hash=... -->` ~ `<!-- omd:end -->` marker block으로 관리된다. Cursor mdc는 frontmatter 포함 전체 파일이 관리 단위.
18
+ block 모드는 `<!-- omd:start v=1 hash=<sha256:12> -->` ~ `<!-- omd:end -->` 마커 안만 관리, 나머지 사용자 콘텐츠 보존. whole 모드는 frontmatter 포함 전체 파일이 관리 단위.
17
19
 
18
- ## 사용
20
+ ## 템플릿 (정확히 이 본문 — 절대 paraphrase 금지)
19
21
 
20
- 사용자 요청에 따라 다음 중 하나를 Bash 툴로 실행:
22
+ ### CLAUDE.md body
23
+
24
+ ```markdown
25
+ # Design System (oh-my-design)
26
+
27
+ The authoritative brand & UI spec is **@./DESIGN.md**.
28
+ Read before any UI/styling/microcopy/motion work.
29
+
30
+ Preference log (pending corrections): @./.omd/preferences.md
31
+
32
+ Precedence: DESIGN.md > preferences.md > your defaults.
33
+ ```
34
+
35
+ ### AGENTS.md body
36
+
37
+ ```markdown
38
+ ## Design System (oh-my-design)
39
+
40
+ **Before any UI, styling, copy, or motion change, open and read `./DESIGN.md` in full.** It is the authoritative brand/design spec. Treat its tokens, voice, and component rules as binding unless the user overrides in chat.
41
+
42
+ If present, read `./.omd/preferences.md` — pending corrections not yet folded into DESIGN.md. Apply them; flag conflicts.
43
+ ```
44
+
45
+ ### .cursor/rules/omd-design.mdc (whole, frontmatter 포함)
46
+
47
+ ```mdc
48
+ ---
49
+ description: Authoritative brand & UI design system. Read DESIGN.md before UI work.
50
+ globs:
51
+ - "**/*.tsx"
52
+ - "**/*.jsx"
53
+ - "**/*.vue"
54
+ - "**/*.svelte"
55
+ - "**/*.css"
56
+ - "**/*.scss"
57
+ - "**/tailwind.config.*"
58
+ - "**/components/**"
59
+ - "**/app/**/page.*"
60
+ alwaysApply: false
61
+ ---
62
+
63
+ <!-- omd:start v=1 hash=<HASH> -->
64
+ The authoritative design spec lives at `@DESIGN.md` (repo root). Open and read before generating/modifying UI.
65
+
66
+ Pending preference corrections: `@.omd/preferences.md`.
67
+
68
+ Precedence: DESIGN.md > preferences.md > framework defaults.
69
+ <!-- omd:end -->
70
+ ```
71
+
72
+ ## 해시 계산
73
+
74
+ `<HASH>` = sha256 of the body content (마커 제외, body 텍스트만), 12자 hex prefix:
21
75
 
22
76
  ```bash
23
- omd sync # 인터랙티브 drift 시 overwrite/skip/show/quit 프롬프트
24
- omd sync --force # drift 포함 무조건 덮어쓰기
25
- omd sync --check # 상태만 검사 (CI/pre-commit, unclean 시 exit 1)
77
+ node -e "console.log(require('crypto').createHash('sha256').update(process.argv[1]).digest('hex').slice(0,12))" "<body 텍스트>"
78
+ ```
79
+
80
+ block 모드 마커 형식 정확히:
81
+ ```
82
+ <!-- omd:start v=1 hash=ab12cd34ef56 -->
83
+ <body>
84
+ <!-- omd:end -->
85
+ ```
86
+
87
+ ## 실행 절차
88
+
89
+ 사용자가 어떤 모드를 요청하는지 분기:
90
+ - **인터랙티브 (디폴트)** — drift 발견 시 사용자에게 묻기
91
+ - **--force 의도** ("강제 덮어쓰기") — drift 무시하고 덮어씀
92
+ - **--check 의도** ("상태만 검사") — 파일 상태만 출력, write 안 함
93
+
94
+ 각 shim별로:
95
+
96
+ ### Step 1 — Read existing
97
+ 파일 없으면 → status: `missing`, 새로 write 진행
98
+ 파일 있으면 → 내용 파싱
99
+
100
+ ### Step 2 — block 모드 파싱
101
+ `<!-- omd:start v=N hash=H -->` 라인 찾기:
102
+ - 없으면 → status: `missing` (block 부재, 사용자 content 외에 새로 추가 필요)
103
+ - 있으면 → marker 사이의 본문 추출. 추출된 본문의 sha256:12를 계산해서 marker의 `hash=H`와 비교
104
+ - 불일치 → status: `drifted` (사용자가 수동 편집함)
105
+ - 일치 + 본문 == 템플릿 → status: `clean`
106
+ - 일치 + 본문 != 템플릿 → status: `out-of-date` (omd 템플릿이 갱신됨)
107
+
108
+ ### Step 3 — whole 모드 파싱
109
+ existing 전체 content와 rendered 템플릿 비교:
110
+ - 동일 → `clean`
111
+ - 차이 → `drifted`
112
+
113
+ ### Step 4 — drift 처리
114
+ - **인터랙티브**: drift된 shim별로 "${path}는 수동 편집됐어요. 덮어쓸까요? (yes/no/show diff)" 묻기
115
+ - **--force**: drift 무시 덮어씀
116
+ - **--check**: drift 있으면 exit 1 동등 — 사용자에게 "drift detected" 보고 후 종료
117
+
118
+ ### Step 5 — Write
119
+ - block 모드: existing 안의 marker block만 새 hash + 새 body로 교체. 마커 외부 사용자 content는 보존
120
+ - whole 모드: 파일 전체를 새 rendered content로 교체. 디렉토리 (`.cursor/rules/`) 없으면 mkdir
121
+
122
+ ### Step 6 — sync-lock 갱신
123
+ `.omd/sync.lock.json` 기록 (없으면 만든다):
124
+ ```json
125
+ {
126
+ "design_md_hash": "<DESIGN.md sha256:12>",
127
+ "targets": {
128
+ "CLAUDE.md": "<hash>",
129
+ "AGENTS.md": "<hash>",
130
+ ".cursor/rules/omd-design.mdc": "<hash>"
131
+ },
132
+ "updated_at": "<ISO timestamp>"
133
+ }
134
+ ```
135
+
136
+ DESIGN.md 해시:
137
+ ```bash
138
+ [ -f DESIGN.md ] && node -e "console.log(require('crypto').createHash('sha256').update(require('fs').readFileSync('DESIGN.md')).digest('hex').slice(0,12))"
139
+ ```
140
+
141
+ ## 결과 보고
142
+
143
+ ```
144
+ CLAUDE.md (block) — updated
145
+ AGENTS.md (block) — unchanged
146
+ .cursor/rules/omd-design.mdc — created
147
+ DESIGN.md hash: ab12cd34ef56
26
148
  ```
27
149
 
28
150
  ## 언제 실행하나
29
151
 
30
- - `DESIGN.md` 변경 직후 (shim hash 갱신)
31
- - 새 프로젝트에 처음 도입 (shim 3종 생성)
152
+ - DESIGN.md 변경 직후 (shim hash 갱신용)
153
+ - 새 프로젝트 도입 (3종 생성)
32
154
  - `.claude` / `.cursor` 디렉토리 추가 후
33
- - CI에서 상태 확인 (`--check`)
155
+ - "drift 확인" 요청
34
156
 
35
157
  ## 금지
36
158
 
37
- - shim 파일을 직접 편집하지 것. 반드시 `omd sync` 경유.
38
- - marker block 외부 유저 content는 보존된다 (CLAUDE.md / AGENTS.md만). mdc는 omd 전용이라 전체 덮어쓰기.
159
+ - 마커 본문에 임의 추가/축약 금지 템플릿 정확히 사용
160
+ - block 모드 파일에서 마커 외부 사용자 content 절대 삭제 금지
161
+ - `.omd/sync.lock.json` 무시 금지 — 항상 갱신
162
+ - DESIGN.md가 없어도 shim은 만들 수 있음 (DESIGN.md 생성 후에 hash만 채움)
@@ -1,35 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/core/agent-detect.ts
4
- import { existsSync } from "fs";
5
- import { join } from "path";
6
- function detectCallingAgent() {
7
- const env = process.env;
8
- if (env.CLAUDECODE === "1" || env.CLAUDE_CODE === "1" || env.CLAUDE_CODE_TASK_ID) {
9
- return "claude-code";
10
- }
11
- if (env.CODEX_SESSION_ID || env.CODEX || env.OPENAI_CODEX) {
12
- return "codex";
13
- }
14
- if (env.OPENCODE || env.OPENCODE_SESSION) {
15
- return "opencode";
16
- }
17
- if (env.CURSOR_SESSION_ID || env.CURSOR_AGENT) {
18
- return "cursor";
19
- }
20
- return "unknown";
21
- }
22
- function detectInstalledAgents(projectRoot) {
23
- return {
24
- claudeCode: existsSync(join(projectRoot, ".claude")) || existsSync(join(projectRoot, "CLAUDE.md")),
25
- codex: existsSync(join(projectRoot, ".codex")) || existsSync(join(projectRoot, "AGENTS.md")) || existsSync(join(projectRoot, "AGENTS.override.md")),
26
- opencode: existsSync(join(projectRoot, ".opencode")) || existsSync(join(projectRoot, "opencode.json")) || existsSync(join(projectRoot, "opencode.jsonc")),
27
- cursor: existsSync(join(projectRoot, ".cursor")) || existsSync(join(projectRoot, ".cursorrules"))
28
- };
29
- }
30
-
31
- export {
32
- detectCallingAgent,
33
- detectInstalledAgents
34
- };
35
- //# sourceMappingURL=chunk-6YNSV3VY.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core/agent-detect.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport type AgentId = 'claude-code' | 'codex' | 'opencode' | 'cursor' | 'unknown';\n\nexport function detectCallingAgent(): AgentId {\n const env = process.env;\n\n if (env.CLAUDECODE === '1' || env.CLAUDE_CODE === '1' || env.CLAUDE_CODE_TASK_ID) {\n return 'claude-code';\n }\n if (env.CODEX_SESSION_ID || env.CODEX || env.OPENAI_CODEX) {\n return 'codex';\n }\n if (env.OPENCODE || env.OPENCODE_SESSION) {\n return 'opencode';\n }\n if (env.CURSOR_SESSION_ID || env.CURSOR_AGENT) {\n return 'cursor';\n }\n\n return 'unknown';\n}\n\nexport interface AgentPresence {\n claudeCode: boolean;\n codex: boolean;\n opencode: boolean;\n cursor: boolean;\n}\n\nexport function detectInstalledAgents(projectRoot: string): AgentPresence {\n return {\n claudeCode:\n existsSync(join(projectRoot, '.claude')) ||\n existsSync(join(projectRoot, 'CLAUDE.md')),\n codex:\n existsSync(join(projectRoot, '.codex')) ||\n existsSync(join(projectRoot, 'AGENTS.md')) ||\n existsSync(join(projectRoot, 'AGENTS.override.md')),\n opencode:\n existsSync(join(projectRoot, '.opencode')) ||\n existsSync(join(projectRoot, 'opencode.json')) ||\n existsSync(join(projectRoot, 'opencode.jsonc')),\n cursor:\n existsSync(join(projectRoot, '.cursor')) ||\n existsSync(join(projectRoot, '.cursorrules')),\n };\n}\n"],"mappings":";;;AAAA,SAAS,kBAAkB;AAC3B,SAAS,YAAY;AAId,SAAS,qBAA8B;AAC5C,QAAM,MAAM,QAAQ;AAEpB,MAAI,IAAI,eAAe,OAAO,IAAI,gBAAgB,OAAO,IAAI,qBAAqB;AAChF,WAAO;AAAA,EACT;AACA,MAAI,IAAI,oBAAoB,IAAI,SAAS,IAAI,cAAc;AACzD,WAAO;AAAA,EACT;AACA,MAAI,IAAI,YAAY,IAAI,kBAAkB;AACxC,WAAO;AAAA,EACT;AACA,MAAI,IAAI,qBAAqB,IAAI,cAAc;AAC7C,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AASO,SAAS,sBAAsB,aAAoC;AACxE,SAAO;AAAA,IACL,YACE,WAAW,KAAK,aAAa,SAAS,CAAC,KACvC,WAAW,KAAK,aAAa,WAAW,CAAC;AAAA,IAC3C,OACE,WAAW,KAAK,aAAa,QAAQ,CAAC,KACtC,WAAW,KAAK,aAAa,WAAW,CAAC,KACzC,WAAW,KAAK,aAAa,oBAAoB,CAAC;AAAA,IACpD,UACE,WAAW,KAAK,aAAa,WAAW,CAAC,KACzC,WAAW,KAAK,aAAa,eAAe,CAAC,KAC7C,WAAW,KAAK,aAAa,gBAAgB,CAAC;AAAA,IAChD,QACE,WAAW,KAAK,aAAa,SAAS,CAAC,KACvC,WAAW,KAAK,aAAa,cAAc,CAAC;AAAA,EAChD;AACF;","names":[]}