$npx -y skills add kwakseongjae/oh-my-design --skill omd-harness화면 전체나 신규 surface를 처음부터 디자인할 때의 진입점 — Discovery→Wireframe→Components→Microcopy→Validation 파이프라인을 omd-master 오케스트레이터로 실행. 트리거: '랜딩페이지', '랜딩 페이지', '랜딩 만들어줘', '홈 화면', '첫 화면', '프로토타입', '그럴싸한', '구색 갖춰', 'first screen', 'first impression', 'landing page', 'landing', 'prototype', 'MVP U
| 1 | <!-- omd:installed-skill — managed by `omd install-skills`. Do not edit; rerun the command to refresh. --> |
| 2 | |
| 3 | |
| 4 | # omd:harness — Design Harness Entry |
| 5 | |
| 6 | 이 스킬은 **omd-master 오케스트레이터**를 호출하는 단일 진입점이다. 본 스킬은 launcher + 사전체크 + run 디렉토리 부트스트랩 책임만 가지고, phase 로직은 `agents/omd-master.md`에 있다. |
| 7 | |
| 8 | CLI 의존 없음. 모든 부트스트랩은 Bash + Write 툴로 직접 실행한다. |
| 9 | |
| 10 | ## 트리거 |
| 11 | |
| 12 | - `/omd-harness <task>` 명시 호출 |
| 13 | - 사용자가 자연어로 "디자인 하네스 / 시니어 디자이너처럼 / 알아서 디자인" 요청 |
| 14 | |
| 15 | ## Step 0 — task 추출 |
| 16 | |
| 17 | 슬래시에 task 같이 적었으면 (`/omd-harness 물 음용 유도 메인 화면`) 그 자연어 부분이 task. 빈 슬래시면 한 번 묻기: |
| 18 | |
| 19 | ``` |
| 20 | 어떤 디자인 작업을 진행할까요? |
| 21 | shape: "[도메인] + [톤/스타일] + [핵심 화면]" — 예: "토스 스타일 가족용 식단 앱 메인 화면" |
| 22 | ``` |
| 23 | |
| 24 | ## Step 1 — Channel-safe role activation + inline recovery (v1.9.0+) |
| 25 | |
| 26 | 먼저 Agent 도구의 사용 가능 role 목록에서 `omd-master`를 확인한다. 있으면 Step 2로 간다. 없을 때는 Markdown을 임의 폴더에 복사하지 않는다. Claude와 Codex의 role schema가 다르고, `.agents/`는 Codex에서 skill 경로이지 role 경로가 아니다. |
| 27 | |
| 28 | ### 1.1 — 현재 채널과 설치물 검증 |
| 29 | |
| 30 | - 이 스킬이 `.agents/skills/omd-harness/`에서 로드됐거나 `.codex/agents/omd-master.toml`이 있으면 **Codex**다. role 파일은 `.codex/agents/omd-master.toml`이어야 하며 top-level `name`, `description`, `developer_instructions`가 모두 있어야 한다. |
| 31 | - 이 스킬이 `.claude/skills/omd-harness/`에서 로드됐거나 `.claude/agents/omd-master.md`가 있으면 **Claude Code**다. role 파일은 첫 줄이 `---`인 `.claude/agents/omd-master.md`여야 한다. |
| 32 | - 이 스킬이 `.opencode/skills/omd-harness/`에서 로드됐거나 `.opencode/agents/omd-master.md`가 있으면 **OpenCode**다. project role은 `.opencode/agents/omd-master.md`, global role은 `~/.config/opencode/agents/omd-master.md`이며 `mode: subagent`여야 한다. |
| 33 | - 폴더가 존재한다는 이유만으로 채널을 추측하지 않는다. 둘 다 없거나 모호하면 사용자에게 현재 coding-agent 채널 하나만 확인한다. |
| 34 | |
| 35 | role 파일이 없거나 schema가 틀렸으면 해당 채널만 최신 설치기로 복구한다: |
| 36 | |
| 37 | ```bash |
| 38 | # <channel> = claude-code, codex 또는 opencode |
| 39 | npx oh-my-design-cli@latest install-skills --agent <channel> --all |
| 40 | npx oh-my-design-cli@latest doctor |
| 41 | ``` |
| 42 | |
| 43 | Codex의 legacy `.codex/skills` OmD entrypoint는 최신 설치기가 안전하게 제거하고 공식 `.agents/skills` 경로로 옮긴다. 사용자 소유 파일은 삭제하지 않는다. |
| 44 | |
| 45 | ### 1.2 — 현재 세션에서 role 목록이 stale인 경우 |
| 46 | |
| 47 | 설치 파일이 유효해도 이미 실행 중인 세션의 Agent 목록은 즉시 갱신되지 않을 수 있다. 이 경우 작업을 중단하거나 잘못된 형식으로 복사하지 말고: |
| 48 | |
| 49 | 1. 유효한 role 파일을 Read한다. Codex는 `developer_instructions`, Claude Code는 frontmatter 뒤 body를 읽는다. |
| 50 | 2. 현재 main agent가 그 지침을 **inline omd-master persona**로 채택해 이번 run을 계속한다. |
| 51 | 3. 사용자에게는 한 줄만 알린다: `역할 파일은 복구됐고, 이번 작업은 inline으로 계속해요. 다음 세션부터 전용 role이 자동 로드됩니다.` |
| 52 | 4. 다음 새 세션을 위해 Codex는 project trust가 필요하고, 두 채널 모두 설치/업그레이드 후 재시작이 필요하다는 점을 마지막 handoff에 남긴다. |
| 53 | |
| 54 | Step 4에서 전용 role spawn이 여전히 불가능하면 inline persona가 같은 phase/state/checkpoint 계약을 직접 실행한다. 산출물이나 사용자 checkpoint를 생략하지 않는다. |
| 55 | |
| 56 | ## Step 2 — Run 디렉토리 부트스트랩 (인라인 Bash) |
| 57 | |
| 58 | 이전엔 `omd harness "<task>" --internal` CLI를 호출했지만 1.0.0부터는 스킬이 직접 한다. 결정론적 hard verify gate: |
| 59 | |
| 60 | ### 2.1 기존 run 재사용 체크 |
| 61 | |
| 62 | ```bash |
| 63 | ls -t .omd/runs 2>/dev/null | head -1 |
| 64 | ``` |
| 65 | |
| 66 | 출력 있으면 그 디렉토리의 `task.md`를 Read해서 사용자 task와 의미적으로 일치하는지 확인. 일치하면 그 run 재사용 — Step 3으로 점프. |
| 67 | |
| 68 | ### 2.2 신규 run 부트스트랩 |
| 69 | |
| 70 | 다음을 **반드시 정확히 이 순서로** Bash 툴로 실행: |
| 71 | |
| 72 | ```bash |
| 73 | # 2.2.1 — timestamp + slug 결정 (한국어 보존) |
| 74 | TS=$(node -e "console.log(new Date().toISOString().replace(/[:.]/g,'-'))") |
| 75 | SLUG=$(node -e " |
| 76 | const s = process.argv[1].toLowerCase().trim() |
| 77 | .replace(/[^a-z0-9가-힣\s-]+/g,'') |
| 78 | .replace(/\s+/g,'-') |
| 79 | .replace(/-+/g,'-') |
| 80 | .replace(/^-|-$/g,''); |
| 81 | console.log(s.slice(0,40) || 'untitled'); |
| 82 | " "<EXTRACTED_TASK>") |
| 83 | RUN_ID="run-${TS}-${SLUG}" |
| 84 | RUN_DIR=".omd/runs/${RUN_ID}" |
| 85 | |
| 86 | # 2.2.2 — 표준 서브폴더 생성 |
| 87 | mkdir -p "${RUN_DIR}"/{wireframes,components,assets/briefs,assets/fallback,assets/pinterest-refs,eval/screenshots,persona-feedback,handoff,checkpoints} |
| 88 | |
| 89 | # 2.2.3 — task.md |
| 90 | cat > "${RUN_DIR}/task.md" <<EOF |
| 91 | # Harness Task |
| 92 | |
| 93 | <EXTRACTED_TASK> |
| 94 | |
| 95 | --- |
| 96 | |
| 97 | - run_id: \`${RUN_ID}\` |
| 98 | - started_at: $(date -u +%Y-%m-%dT%H:%M:%SZ) |
| 99 | - cwd: \`$(pwd)\` |
| 100 | EOF |
| 101 | |
| 102 | # 2.2.4 — run.log |
| 103 | echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] run initialized" > "${RUN_DIR}/run.log" |
| 104 | |
| 105 | # 2.2.5 — .omd/.gitignore (idempotent) |
| 106 | mkdir -p .omd |
| 107 | [ -f .omd/.gitignore ] || printf "runs/\ncache/\n" > .omd/.gitignore |
| 108 | |
| 109 | # 2.2.6 — INDEX.md (idempotent header + append) |
| 110 | INDEX=".omd/runs/INDEX.md" |
| 111 | [ -f "${INDEX}" ] || cat > "${INDEX}" <<EOF |
| 112 | # Harness Runs Index |
| 113 | |
| 114 | One line per run. Append-only. |
| 115 | |
| 116 | EOF |
| 117 | TASK_ONELINE=$(echo "<EXTRACTED_TASK>" | tr '\n' ' ' | cut -c1-120) |
| 118 | echo "- $(date -u +%Y-%m-%dT%H:%M:%SZ) \`${RUN_ID}\` — ${TASK_ONELINE}" >> "${INDEX}" |
| 119 | |
| 120 | # 2.2.7 — 결과 출력 (이 스킬이 파싱) |
| 121 | echo "RUN_DIR=${RUN_DIR}" |
| 122 | echo "RUN_ID=${RUN_ID}" |
| 123 | ``` |
| 124 | |
| 125 | ### 2.3 Hard verify gate (master spawn 차단 조건) |
| 126 | |
| 127 | 부트스트랩 다음, master spawn 전에 반드시: |
| 128 | |
| 129 | ```bash |
| 130 | test -d "${RUN_DIR}" && test -f "${RUN_DIR}/task.md" && echo "OK" || echo "FAIL" |
| 131 | ``` |
| 132 | |
| 133 | `OK`가 출력되지 않으면 master는 절대 spawn하지 않는다. 사용자에게: |
| 134 | |
| 135 | ``` |
| 136 | 하네스 부트스트랩이 실패했어요 (run dir |