$curl -o .claude/agents/omd-codex-image.md https://raw.githubusercontent.com/kwakseongjae/oh-my-design/HEAD/.claude/agents/omd-codex-image.mdChannel-aware image materializer. Reads `` spec blocks in HTML/MD/JSX and materializes them through Codex's native image generation, omd-asset-curator fallback, or user-queue (OpenCode). One spec format, three downstream paths.
| 1 | <!-- omd:installed-skill — managed by `omd install-skills`. Do not edit; rerun the command to refresh. --> |
| 2 | |
| 3 | |
| 4 | # omd:codex-image |
| 5 | |
| 6 | 채널-aware 이미지 materialization 스킬. **하나의 spec format** + **세 가지 downstream 처리**. |
| 7 | |
| 8 | 문제: oh-my-design이 emit하는 HTML/MD에는 늘 illustration 자리가 비어있다. 채널별 capability가 다르다 — Codex CLI는 내장 image generation을 갖고, Claude Code는 안 가졌고, OpenCode는 user에게 위임한다. 이 차이를 skill 1개로 통합한다. |
| 9 | |
| 10 | ## 0. 단일 spec format |
| 11 | |
| 12 | artifact (HTML, Markdown, JSX, MDX) 안에 다음 주석 블록을 둔다. |
| 13 | |
| 14 | ```html |
| 15 | <!-- omd:gen-image |
| 16 | filename: assets/karrot-hero.png |
| 17 | prompt: "Karrot mobile home feed screenshot — white canvas, warm near-black headings, single Karrot Orange #FF6600 floating CTA. Mobile portrait, no chrome." |
| 18 | aspect: "16:9" |
| 19 | style: "product screenshot, minimal, mobile" |
| 20 | references: |
| 21 | - https://www.daangn.com/ |
| 22 | - https://seed-design.io/ |
| 23 | notes: "Single saturated element per viewport. Use brand orange only on the CTA." |
| 24 | --> |
| 25 | <img src="assets/karrot-hero.png" alt="당근 홈 피드의 단일 오렌지 CTA" /> |
| 26 | ``` |
| 27 | |
| 28 | 규칙: |
| 29 | |
| 30 | - `filename` — 결과물 저장 경로. 같은 디렉토리 기준 상대경로 권장. |
| 31 | - `prompt` — 한 문단, 구체적, 시각 디테일 + 톤 + 컬러 hex 포함. |
| 32 | - `aspect` — `16:9` `4:3` `1:1` `9:16` 중 하나. |
| 33 | - `style` — 1~3 단어 (`product screenshot` / `inline svg` / `editorial photo` / `dieline diagram`). |
| 34 | - `references` — 실제 브랜드 URL (선택, IP-safe 컨텍스트용). |
| 35 | - `notes` — generator나 검수자가 알아야 할 제약. |
| 36 | |
| 37 | 이 spec은 channel과 무관하다. 채널별 처리는 §1. |
| 38 | |
| 39 | ## 1. 채널 분기 |
| 40 | |
| 41 | 호출 envelope의 `channel` (또는 환경 detect — `process.env.OMD_CHANNEL`이나 host agent 식별)에 따라: |
| 42 | |
| 43 | ### 1.1 channel = `codex` |
| 44 | |
| 45 | Codex CLI는 native image generation을 갖고 있다. 다음 순서: |
| 46 | |
| 47 | 1. artifact를 Read로 전체 로드 |
| 48 | 2. `<!-- omd:gen-image ... -->` 블록을 정규식으로 추출 (multiline) |
| 49 | 3. 각 블록마다 Codex의 image generation primitive 호출 — 도구명은 host에 따라 다르다 (`generate_image`, `image.create`, `dall_e_image_generation`, `gpt_image_1` 등). Codex agent가 자기 환경에서 사용 가능한 것을 선택한다. |
| 50 | 4. 출력 파일을 `filename` 경로에 저장 (mkdir -p) |
| 51 | 5. **주석 블록은 그대로 두고** `<img src>`나 `![]()` 경로가 매칭되는지 확인. 안 맞으면 alt 텍스트 보존하면서 src만 교체. |
| 52 | 6. 각 처리 항목에 `<!-- omd:gen-image:done at=<ISO timestamp> by=codex -->` 주석을 spec 블록 아래에 1줄 추가. |
| 53 | |
| 54 | generation 실패 시: spec 블록은 그대로, `<!-- omd:gen-image:error reason="..." -->` 추가. 다음 호출이 재시도 가능하도록. |
| 55 | |
| 56 | prompt 보강 (Codex가 좋은 결과를 내려면): |
| 57 | |
| 58 | - `prompt` 원문 + 다음 prefix 자동 prepend: `"Render as <aspect> <style>. "` |
| 59 | - `notes`에 색 hex 있으면 prompt 끝에 `"Use only the following colors: <hex list>."` 추가 |
| 60 | - `references` 있으면 `"Stylistic reference inspiration only — do not copy verbatim: <urls>"` 추가 |
| 61 | |
| 62 | ### 1.2 channel = `claude-code` |
| 63 | |
| 64 | Claude Code는 native image generation이 없다. 대신 `omd-asset-curator` 스킬로 라우팅: |
| 65 | |
| 66 | 1. spec 블록을 추출 |
| 67 | 2. 각 블록을 omd-asset-curator의 spec 형식으로 변환: |
| 68 | - `prompt` → asset-curator의 `description` |
| 69 | - `style` → asset-curator의 medium 힌트 (`product screenshot` → Picsum/Loremflickr, `inline svg` → 직접 SVG 생성, `icon` → Lucide) |
| 70 | 3. asset-curator가 free-license 자원에서 매칭 (DiceBear avatars, Lucide icons, Picsum CC0 photos, unDraw SVG 등) 또는 인라인 SVG를 생성해 `filename`에 저장 |
| 71 | 4. spec 블록 그대로 두고 `<!-- omd:gen-image:done at=<ISO> by=asset-curator source=<url> -->` 추가 |
| 72 | 5. **사용자에게 1줄 알림**: "Claude Code에서는 free-license fallback을 적용했어요. 더 정확한 이미지는 Codex 채널에서 generate하면 돼요." |
| 73 | |
| 74 | ### 1.3 channel = `opencode` |
| 75 | |
| 76 | OpenCode는 image generation도, asset-curator도 자동으로 못 돌린다. user-in-the-loop: |
| 77 | |
| 78 | 1. spec 블록을 추출 |
| 79 | 2. terminal에 `## Image generation queue` 섹션을 emit: |
| 80 | ``` |
| 81 | 3개 이미지를 수동으로 생성/소싱해서 다음 경로에 두세요: |
| 82 | |
| 83 | 1. assets/karrot-hero.png (16:9) |
| 84 | prompt: "Karrot mobile home feed — single orange CTA..." |
| 85 | refs: https://www.daangn.com/ |
| 86 | |
| 87 | 2. ... |
| 88 | ``` |
| 89 | 3. spec 블록은 그대로 두고 done 주석은 user 확인 후에만 추가 |
| 90 | |
| 91 | ## 2. fallback 그래프 |
| 92 | |
| 93 | artifact를 처리할 때 항상 우선순위: |
| 94 | |
| 95 | ``` |
| 96 | Codex native gen ──┐ |
| 97 | ├─► success → done 주석 추가 |
| 98 | asset-curator ──┘ |
| 99 | │ |
| 100 | └─► fail → user-queue (OpenCode 식 prompt) |
| 101 | ``` |
| 102 | |
| 103 | 각 단계 실패는 다음 단계로 escalate. 마지막 단계도 실패하면 error 주석 + 사용자에게 명시. |
| 104 | |
| 105 | ## 3. idempotency |
| 106 | |
| 107 | 이 스킬은 같은 artifact에 여러 번 돌려도 안전해야 한다. |
| 108 | |
| 109 | - 이미 `<!-- omd:gen-image:done -->` 주석이 붙은 spec 블록은 스킵 |
| 110 | - `filename` 경로의 파일이 이미 존재하면 (size > 0) 스킵 |
| 111 | - `--force` 명시되면 둘 다 무시하고 재생성 |
| 112 | |
| 113 | ## 4. IP safe rails |
| 114 | |
| 115 | - `references` URL은 inspiration만. 절대 verbatim 카피 X. |
| 116 | - 브랜드 로고는 generation이 아니라 reference에서 **다운로드**해서 가져오기. Codex/asset-curator 둘 다 로고는 generate 금지 (왜곡된 가짜 로고는 IP risk). |
| 117 | - 사람 얼굴이 들어가는 generation은 디폴트 거절 (DiceBear avatars 같은 캐리커처는 OK). |
| 118 | |
| 119 | ## 5. 출력 보고 |
| 120 | |
| 121 | 처리 끝에 한 줄 요약: |
| 122 | |
| 123 | ``` |
| 124 | ✓ 4 images materialized (codex) · 0 skipped · 0 errors |
| 125 | → experiments/2026-05-19/karrot/assets/{hero,one-color,grid,detail}.png |
| 126 | ``` |
| 127 | |
| 128 | 또는 fallback이 섞였을 때: |
| 129 | |
| 130 | ``` |
| 131 | ✓ 2 codex · 2 asset-curator fallback · 1 user-queue |
| 132 | ``` |
| 133 | |
| 134 | ## 6. 호출 envelope |
| 135 | |
| 136 | ```yaml |
| 137 | agent: omd-codex-image |
| 138 | inputs: |
| 139 | artifact_path: experiments/2026-05-19/karrot/landing.html |
| 140 | channel: codex # 또는 claude-code | opencode | auto |
| 141 | force: false # done 주석 무시 |