$npx -y skills add kwakseongjae/oh-my-design --skill omd-syncDESIGN.md 변경분을 CLAUDE.md / AGENTS.md / .cursor/rules/omd-design.mdc shim 3종에 전파. 'shim 갱신', 'drift 확인', 'ship', 'publish', 「shimを更新」, 「同步 CLAUDE.md」류의 요청에 트리거. DESIGN.md가 수정됐는데 shim이 오래됐을 때 자동 감지해 제안하기도 함.
| 1 | <!-- omd:installed-skill — managed by `omd install-skills`. Do not edit; rerun the command to refresh. --> |
| 2 | |
| 3 | |
| 4 | # omd:sync — Shim Maintenance |
| 5 | |
| 6 | DESIGN.md가 모든 주요 AI 코딩 에이전트(Claude Code, Codex, OpenCode, Cursor)에게 보이도록 shim 파일 3종을 관리한다. **CLI 호출 없음** — Read/Write/Edit 툴로 직접 처리. |
| 7 | |
| 8 | ## 관리 대상 (3 파일) |
| 9 | |
| 10 | | ID | 경로 | 모드 | |
| 11 | |---|---|---| |
| 12 | | `claude` | `CLAUDE.md` | block (managed marker만) | |
| 13 | | `agents` | `AGENTS.md` | block (managed marker만) | |
| 14 | | `cursor` | `.cursor/rules/omd-design.mdc` | whole (전체 파일이 omd 전용) | |
| 15 | |
| 16 | block 모드는 `<!-- omd:start v=1 hash=<sha256:12> -->` ~ `<!-- omd:end -->` 마커 안만 관리, 나머지 사용자 콘텐츠 보존. whole 모드는 frontmatter 포함 전체 파일이 관리 단위. |
| 17 | |
| 18 | ## 템플릿 (정확히 이 본문 — 절대 paraphrase 금지) |
| 19 | |
| 20 | ### CLAUDE.md body |
| 21 | |
| 22 | ```markdown |
| 23 | # Design System (oh-my-design) |
| 24 | |
| 25 | The authoritative brand & UI spec is **@./DESIGN.md**. |
| 26 | Read before any UI/styling/microcopy/motion work. |
| 27 | |
| 28 | Preference log (pending corrections): @./.omd/preferences.md |
| 29 | |
| 30 | Precedence: DESIGN.md > preferences.md > your defaults. |
| 31 | ``` |
| 32 | |
| 33 | ### AGENTS.md body |
| 34 | |
| 35 | ```markdown |
| 36 | ## Design System (oh-my-design) |
| 37 | |
| 38 | **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. |
| 39 | |
| 40 | If present, read `./.omd/preferences.md` — pending corrections not yet folded into DESIGN.md. Apply them; flag conflicts. |
| 41 | ``` |
| 42 | |
| 43 | ### .cursor/rules/omd-design.mdc (whole, frontmatter 포함) |
| 44 | |
| 45 | ```mdc |
| 46 | --- |
| 47 | description: Authoritative brand & UI design system. Read DESIGN.md before UI work. |
| 48 | globs: |
| 49 | - "**/*.tsx" |
| 50 | - "**/*.jsx" |
| 51 | - "**/*.vue" |
| 52 | - "**/*.svelte" |
| 53 | - "**/*.css" |
| 54 | - "**/*.scss" |
| 55 | - "**/tailwind.config.*" |
| 56 | - "**/components/**" |
| 57 | - "**/app/**/page.*" |
| 58 | alwaysApply: false |
| 59 | --- |
| 60 | |
| 61 | <!-- omd:start v=1 hash=<HASH> --> |
| 62 | The authoritative design spec lives at `@DESIGN.md` (repo root). Open and read before generating/modifying UI. |
| 63 | |
| 64 | Pending preference corrections: `@.omd/preferences.md`. |
| 65 | |
| 66 | Precedence: DESIGN.md > preferences.md > framework defaults. |
| 67 | <!-- omd:end --> |
| 68 | ``` |
| 69 | |
| 70 | ## 해시 계산 |
| 71 | |
| 72 | `<HASH>` = sha256 of the body content (마커 제외, body 텍스트만), 12자 hex prefix: |
| 73 | |
| 74 | ```bash |
| 75 | node -e "console.log(require('crypto').createHash('sha256').update(process.argv[1]).digest('hex').slice(0,12))" "<body 텍스트>" |
| 76 | ``` |
| 77 | |
| 78 | block 모드 마커 형식 정확히: |
| 79 | ``` |
| 80 | <!-- omd:start v=1 hash=ab12cd34ef56 --> |
| 81 | <body> |
| 82 | <!-- omd:end --> |
| 83 | ``` |
| 84 | |
| 85 | ## 실행 절차 |
| 86 | |
| 87 | 사용자가 어떤 모드를 요청하는지 분기: |
| 88 | - **인터랙티브 (디폴트)** — drift 발견 시 사용자에게 묻기 |
| 89 | - **--force 의도** ("강제 덮어쓰기") — drift 무시하고 덮어씀 |
| 90 | - **--check 의도** ("상태만 검사") — 파일 상태만 출력, write 안 함 |
| 91 | |
| 92 | 각 shim별로: |
| 93 | |
| 94 | ### Step 1 — Read existing |
| 95 | 파일 없으면 → status: `missing`, 새로 write 진행 |
| 96 | 파일 있으면 → 내용 파싱 |
| 97 | |
| 98 | ### Step 2 — block 모드 파싱 |
| 99 | `<!-- omd:start v=N hash=H -->` 라인 찾기: |
| 100 | - 없으면 → status: `missing` (block 부재, 사용자 content 외에 새로 추가 필요) |
| 101 | - 있으면 → marker 사이의 본문 추출. 추출된 본문의 sha256:12를 계산해서 marker의 `hash=H`와 비교 |
| 102 | - 불일치 → status: `drifted` (사용자가 수동 편집함) |
| 103 | - 일치 + 본문 == 템플릿 → status: `clean` |
| 104 | - 일치 + 본문 != 템플릿 → status: `out-of-date` (omd 템플릿이 갱신됨) |
| 105 | |
| 106 | ### Step 3 — whole 모드 파싱 |
| 107 | existing 전체 content와 rendered 템플릿 비교: |
| 108 | - 동일 → `clean` |
| 109 | - 차이 → `drifted` |
| 110 | |
| 111 | ### Step 4 — drift 처리 |
| 112 | - **인터랙티브**: drift된 shim별로 "${path}는 수동 편집됐어요. 덮어쓸까요? (yes/no/show diff)" 묻기 |
| 113 | - **--force**: drift 무시 덮어씀 |
| 114 | - **--check**: drift 있으면 exit 1 동등 — 사용자에게 "drift detected" 보고 후 종료 |
| 115 | |
| 116 | ### Step 5 — Write |
| 117 | - block 모드: existing 안의 marker block만 새 hash + 새 body로 교체. 마커 외부 사용자 content는 보존 |
| 118 | - whole 모드: 파일 전체를 새 rendered content로 교체. 디렉토리 (`.cursor/rules/`) 없으면 mkdir |
| 119 | |
| 120 | ### Step 6 — sync-lock 갱신 |
| 121 | `.omd/sync.lock.json` 기록 (없으면 만든다): |
| 122 | ```json |
| 123 | { |
| 124 | "design_md_hash": "<DESIGN.md sha256:12>", |
| 125 | "targets": { |
| 126 | "CLAUDE.md": "<hash>", |
| 127 | "AGENTS.md": "<hash>", |
| 128 | ".cursor/rules/omd-design.mdc": "<hash>" |
| 129 | }, |
| 130 | "updated_at": "<ISO timestamp>" |
| 131 | } |
| 132 | ``` |
| 133 | |
| 134 | DESIGN.md 해시: |
| 135 | ```bash |
| 136 | [ -f DESIGN.md ] && node -e "console.log(require('crypto').createHash('sha256').update(require('fs').readFileSync('DESIGN.md')).digest('hex').slice(0,12))" |
| 137 | ``` |
| 138 | |
| 139 | ## 결과 보고 |
| 140 | |
| 141 | ``` |
| 142 | CLAUDE.md (block) — updated |
| 143 | AGENTS.md (block) — unchanged |
| 144 | .cursor/rules/omd-design.mdc — created |
| 145 | DESIGN.md hash: ab12cd34ef56 |
| 146 | ``` |
| 147 | |
| 148 | ## 언제 실행하나 |
| 149 | |
| 150 | - DESIGN.md 변경 직후 (shim hash 갱신용) |
| 151 | - 새 프로젝트 첫 도입 (3종 생성) |
| 152 | - `.claude` / `.cursor` 디렉토리 추가 후 |
| 153 | - "drift 확인" 요청 |
| 154 | |
| 155 | ## 금지 |
| 156 | |
| 157 | - 마커 안 본문에 임의 추가/축약 금지 — 위 템플릿 정확히 사용 |
| 158 | - block 모드 파일에서 마커 외부 사용자 content 절대 삭제 금지 |
| 159 | - `.omd/sync.lock.json` 무시 금지 — 항상 갱신 |
| 160 | - DESIGN.md가 없어도 shim은 만들 수 있음 (DESIGN.md 생성 후에 hash만 채움) |