oh-my-design-cli 1.8.6 → 1.9.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 (275) hide show
  1. package/.claude/hooks/lib/preferences-parser.cjs +1 -0
  2. package/.claude/hooks/lib/preferences-writer.cjs +1 -0
  3. package/.claude/hooks/post-edit-watch.cjs +1 -0
  4. package/.claude/hooks/session-end-foldin.cjs +1 -0
  5. package/.claude/hooks/session-state-loader.cjs +1 -0
  6. package/.claude/hooks/skill-activation.cjs +1 -0
  7. package/.claude/settings.json +10 -1
  8. package/.github/assets/logo-bg.png +0 -0
  9. package/AGENTS.md +20 -1
  10. package/CHANGELOG.md +482 -0
  11. package/MIGRATION.md +103 -0
  12. package/README.ja.md +56 -19
  13. package/README.ko.md +47 -30
  14. package/README.md +48 -30
  15. package/README.zh-TW.md +59 -22
  16. package/agents/AGENT.md +8 -7
  17. package/agents/omd-codex-image.md +1 -1
  18. package/agents/omd-critic.md +6 -7
  19. package/agents/omd-designer-review.md +1 -1
  20. package/agents/omd-final-qa.md +1 -1
  21. package/agents/omd-humanizer.md +38 -0
  22. package/agents/omd-kr-writer.md +3 -4
  23. package/agents/omd-locale-adapter.md +12 -16
  24. package/agents/omd-master.md +24 -19
  25. package/agents/omd-microcopy.md +1 -1
  26. package/agents/omd-orchestrator.md +7 -8
  27. package/agents/omd-slop-auditor.md +31 -0
  28. package/agents/omd-ux-researcher.md +1 -1
  29. package/data/reference-fingerprints.json +2761 -85
  30. package/dist/bin/oh-my-design.js +16 -8
  31. package/dist/bin/oh-my-design.js.map +1 -1
  32. package/dist/chunk-UJIHCEYF.js +99 -0
  33. package/dist/chunk-UJIHCEYF.js.map +1 -0
  34. package/dist/doctor-LDWSFM3I.js +763 -0
  35. package/dist/doctor-LDWSFM3I.js.map +1 -0
  36. package/dist/install-skills-ZZFNJ2II.js +1120 -0
  37. package/dist/install-skills-ZZFNJ2II.js.map +1 -0
  38. package/docs/CLI_QUICKSTART.md +157 -0
  39. package/package.json +25 -4
  40. package/scripts/postinstall.cjs +5 -4
  41. package/skills/claude-design/SKILL.md +38 -14
  42. package/skills/claude-design/references/claude-design-flow.md +11 -11
  43. package/skills/claude-design/references/codebase-analysis.md +5 -4
  44. package/skills/omd-final-qa/SKILL.md +1 -1
  45. package/skills/omd-harness/SKILL.md +51 -50
  46. package/skills/omd-humanize/SKILL.md +116 -0
  47. package/skills/omd-humanize/agents/openai.yaml +4 -0
  48. package/skills/omd-humanize/references/locale-playbooks.md +102 -0
  49. package/skills/omd-humanize/references/sources.md +11 -0
  50. package/skills/omd-init/SKILL.md +18 -12
  51. package/skills/omd-locale-adapter/SKILL.md +58 -91
  52. package/skills/omd-orchestrator/SKILL.md +11 -5
  53. package/skills/omd-reference-capture/SKILL.md +80 -26
  54. package/skills/omd-reference-capture/scripts/PLAYWRIGHT-LICENSE.txt +202 -0
  55. package/skills/omd-reference-capture/scripts/PLAYWRIGHT-NOTICE.txt +5 -0
  56. package/skills/omd-reference-capture/scripts/THIRD_PARTY_NOTICES.md +3 -0
  57. package/skills/omd-reference-capture/scripts/capture-reference-evidence.mjs +159906 -0
  58. package/skills/omd-slop-audit/SKILL.md +110 -0
  59. package/skills/omd-slop-audit/agents/openai.yaml +4 -0
  60. package/skills/omd-slop-audit/references/pattern-catalog.md +96 -0
  61. package/skills/omd-slop-audit/references/sources.md +24 -0
  62. package/web/references/104/DESIGN.md +354 -0
  63. package/web/references/11st/DESIGN.md +197 -316
  64. package/web/references/29cm/DESIGN.md +297 -393
  65. package/web/references/42dot/DESIGN.md +390 -0
  66. package/web/references/8percent/DESIGN.md +438 -0
  67. package/web/references/ably/DESIGN.md +267 -572
  68. package/web/references/acer/DESIGN.md +329 -0
  69. package/web/references/airbnb/DESIGN.md +219 -557
  70. package/web/references/airbridge/DESIGN.md +451 -0
  71. package/web/references/alipay/DESIGN.md +179 -420
  72. package/web/references/apple/DESIGN.md +224 -498
  73. package/web/references/asleep/DESIGN.md +402 -0
  74. package/web/references/au/DESIGN.md +334 -0
  75. package/web/references/autopedia/DESIGN.md +382 -0
  76. package/web/references/aws-cloudscape/DESIGN.md +241 -0
  77. package/web/references/baemin/DESIGN.md +332 -491
  78. package/web/references/banksalad/DESIGN.md +269 -180
  79. package/web/references/barogo/DESIGN.md +416 -0
  80. package/web/references/beusable/DESIGN.md +464 -0
  81. package/web/references/bigin/DESIGN.md +454 -0
  82. package/web/references/bilibili/DESIGN.md +206 -374
  83. package/web/references/bmw/DESIGN.md +162 -276
  84. package/web/references/brandi/DESIGN.md +184 -325
  85. package/web/references/buzzvil/DESIGN.md +457 -0
  86. package/web/references/cafe24/DESIGN.md +472 -0
  87. package/web/references/catchtable/DESIGN.md +219 -264
  88. package/web/references/cgv/DESIGN.md +317 -0
  89. package/web/references/channeltalk/DESIGN.md +210 -336
  90. package/web/references/china-airlines/DESIGN.md +291 -0
  91. package/web/references/cjonstyle/DESIGN.md +453 -0
  92. package/web/references/class101/DESIGN.md +168 -387
  93. package/web/references/classum/DESIGN.md +228 -203
  94. package/web/references/claude/DESIGN.md +242 -479
  95. package/web/references/clay/DESIGN.md +179 -377
  96. package/web/references/clickhouse/DESIGN.md +254 -343
  97. package/web/references/codeit/DESIGN.md +470 -0
  98. package/web/references/cohere/DESIGN.md +214 -324
  99. package/web/references/coinone/DESIGN.md +199 -179
  100. package/web/references/corca/DESIGN.md +414 -0
  101. package/web/references/coupang/DESIGN.md +205 -475
  102. package/web/references/cursor/DESIGN.md +240 -381
  103. package/web/references/dabang/DESIGN.md +248 -213
  104. package/web/references/dable/DESIGN.md +431 -0
  105. package/web/references/datarize/DESIGN.md +338 -0
  106. package/web/references/dealicious/DESIGN.md +402 -0
  107. package/web/references/drdiary/DESIGN.md +404 -0
  108. package/web/references/elastic/DESIGN.md +238 -0
  109. package/web/references/elevenlabs/DESIGN.md +183 -321
  110. package/web/references/elice/DESIGN.md +445 -0
  111. package/web/references/eslite/DESIGN.md +257 -0
  112. package/web/references/evaair/DESIGN.md +338 -0
  113. package/web/references/expo/DESIGN.md +238 -331
  114. package/web/references/familymart-tw/DESIGN.md +254 -0
  115. package/web/references/ferrari/DESIGN.md +160 -377
  116. package/web/references/figma/DESIGN.md +197 -451
  117. package/web/references/fitpet/DESIGN.md +445 -0
  118. package/web/references/flex/DESIGN.md +170 -253
  119. package/web/references/framer/DESIGN.md +197 -340
  120. package/web/references/freee/DESIGN.md +211 -533
  121. package/web/references/friendliai/DESIGN.md +431 -0
  122. package/web/references/frip/DESIGN.md +440 -0
  123. package/web/references/furiosaai/DESIGN.md +450 -0
  124. package/web/references/gangnamunni/DESIGN.md +154 -591
  125. package/web/references/gaudiolab/DESIGN.md +414 -0
  126. package/web/references/google/DESIGN.md +290 -398
  127. package/web/references/goorm/DESIGN.md +470 -0
  128. package/web/references/greencar/DESIGN.md +420 -0
  129. package/web/references/hackle/DESIGN.md +472 -0
  130. package/web/references/hashicorp/DESIGN.md +273 -318
  131. package/web/references/heydealer/DESIGN.md +429 -0
  132. package/web/references/humanscape/DESIGN.md +433 -0
  133. package/web/references/hwahae/DESIGN.md +453 -0
  134. package/web/references/hyundai/DESIGN.md +158 -368
  135. package/web/references/hyundaicard/DESIGN.md +171 -121
  136. package/web/references/ibm/DESIGN.md +227 -378
  137. package/web/references/idus/DESIGN.md +450 -0
  138. package/web/references/igaworks/DESIGN.md +437 -0
  139. package/web/references/inflearn/DESIGN.md +221 -366
  140. package/web/references/intuit/DESIGN.md +244 -0
  141. package/web/references/jandi/DESIGN.md +191 -291
  142. package/web/references/jobplanet/DESIGN.md +415 -0
  143. package/web/references/kakao/DESIGN.md +287 -475
  144. package/web/references/kakaobank/DESIGN.md +220 -521
  145. package/web/references/kakaogames/DESIGN.md +220 -0
  146. package/web/references/kakaopay/DESIGN.md +207 -526
  147. package/web/references/karrot/DESIGN.md +227 -251
  148. package/web/references/kb-kookmin/DESIGN.md +308 -0
  149. package/web/references/kbank/DESIGN.md +220 -175
  150. package/web/references/kcd/DESIGN.md +432 -0
  151. package/web/references/kmong/DESIGN.md +281 -356
  152. package/web/references/krds/DESIGN.md +245 -19
  153. package/web/references/kream/DESIGN.md +224 -359
  154. package/web/references/kurly/DESIGN.md +211 -533
  155. package/web/references/kyobobook/DESIGN.md +445 -0
  156. package/web/references/lablup/DESIGN.md +474 -0
  157. package/web/references/lamborghini/DESIGN.md +198 -360
  158. package/web/references/lemonbase/DESIGN.md +452 -0
  159. package/web/references/lguplus/DESIGN.md +259 -0
  160. package/web/references/likelion/DESIGN.md +283 -0
  161. package/web/references/line/DESIGN.md +396 -355
  162. package/web/references/linear.app/DESIGN.md +209 -562
  163. package/web/references/lotteon/DESIGN.md +386 -0
  164. package/web/references/lovable/DESIGN.md +255 -349
  165. package/web/references/makinarocks/DESIGN.md +242 -0
  166. package/web/references/maum-ai/DESIGN.md +423 -0
  167. package/web/references/medibloc/DESIGN.md +425 -0
  168. package/web/references/megabox/DESIGN.md +232 -0
  169. package/web/references/mercari/DESIGN.md +217 -434
  170. package/web/references/microsoft/DESIGN.md +234 -388
  171. package/web/references/mildang/DESIGN.md +411 -0
  172. package/web/references/millie/DESIGN.md +183 -504
  173. package/web/references/minimax/DESIGN.md +264 -322
  174. package/web/references/miricanvas/DESIGN.md +182 -355
  175. package/web/references/miro/DESIGN.md +234 -232
  176. package/web/references/mistral.ai/DESIGN.md +231 -314
  177. package/web/references/mixi/DESIGN.md +234 -0
  178. package/web/references/moin/DESIGN.md +445 -0
  179. package/web/references/mongodb/DESIGN.md +219 -316
  180. package/web/references/moreh/DESIGN.md +437 -0
  181. package/web/references/musinsa/DESIGN.md +184 -510
  182. package/web/references/mynavi/DESIGN.md +242 -0
  183. package/web/references/myrealtrip/DESIGN.md +169 -406
  184. package/web/references/naver/DESIGN.md +328 -499
  185. package/web/references/naverwebtoon/DESIGN.md +172 -395
  186. package/web/references/neosapience/DESIGN.md +441 -0
  187. package/web/references/nhn/DESIGN.md +169 -346
  188. package/web/references/nhncloud/DESIGN.md +185 -123
  189. package/web/references/nota/DESIGN.md +451 -0
  190. package/web/references/notion/DESIGN.md +271 -475
  191. package/web/references/nrise/DESIGN.md +417 -0
  192. package/web/references/ohouse/DESIGN.md +210 -549
  193. package/web/references/oliveyoung/DESIGN.md +217 -326
  194. package/web/references/ollama/DESIGN.md +241 -308
  195. package/web/references/onestore/DESIGN.md +248 -0
  196. package/web/references/palantir/DESIGN.md +256 -0
  197. package/web/references/panasonic/DESIGN.md +276 -0
  198. package/web/references/patternfly/DESIGN.md +259 -0
  199. package/web/references/payhere/DESIGN.md +438 -0
  200. package/web/references/pega/DESIGN.md +262 -0
  201. package/web/references/peoplefund/DESIGN.md +431 -0
  202. package/web/references/petfriends/DESIGN.md +428 -0
  203. package/web/references/pinkfong/DESIGN.md +199 -367
  204. package/web/references/pinterest/DESIGN.md +258 -308
  205. package/web/references/portone/DESIGN.md +446 -0
  206. package/web/references/poya/DESIGN.md +286 -0
  207. package/web/references/pozalabs/DESIGN.md +438 -0
  208. package/web/references/protopie/DESIGN.md +419 -0
  209. package/web/references/qraft/DESIGN.md +448 -0
  210. package/web/references/queenit/DESIGN.md +432 -0
  211. package/web/references/quotabook/DESIGN.md +449 -0
  212. package/web/references/rebellions/DESIGN.md +449 -0
  213. package/web/references/recruit/DESIGN.md +247 -0
  214. package/web/references/remember/DESIGN.md +202 -396
  215. package/web/references/resend/DESIGN.md +274 -348
  216. package/web/references/returnzero/DESIGN.md +460 -0
  217. package/web/references/sakura-internet/DESIGN.md +269 -0
  218. package/web/references/samsung/DESIGN.md +157 -359
  219. package/web/references/sandoll/DESIGN.md +445 -0
  220. package/web/references/sanity/DESIGN.md +241 -375
  221. package/web/references/saramin/DESIGN.md +465 -0
  222. package/web/references/scatterlab/DESIGN.md +412 -0
  223. package/web/references/selectstar/DESIGN.md +439 -0
  224. package/web/references/servicenow/DESIGN.md +289 -0
  225. package/web/references/shiftee/DESIGN.md +468 -0
  226. package/web/references/shinhanbank/DESIGN.md +161 -341
  227. package/web/references/shinhancard/DESIGN.md +208 -319
  228. package/web/references/sionic/DESIGN.md +454 -0
  229. package/web/references/sktelecom/DESIGN.md +310 -0
  230. package/web/references/socar/DESIGN.md +125 -32
  231. package/web/references/softbank/DESIGN.md +231 -0
  232. package/web/references/solapi/DESIGN.md +483 -0
  233. package/web/references/sony/DESIGN.md +234 -0
  234. package/web/references/soop/DESIGN.md +261 -0
  235. package/web/references/spacex/DESIGN.md +184 -323
  236. package/web/references/spotify/DESIGN.md +253 -394
  237. package/web/references/squeezebits/DESIGN.md +441 -0
  238. package/web/references/stayfolio/DESIGN.md +422 -0
  239. package/web/references/stripe/DESIGN.md +212 -491
  240. package/web/references/supabase/DESIGN.md +244 -316
  241. package/web/references/supertone/DESIGN.md +413 -0
  242. package/web/references/tabling/DESIGN.md +415 -0
  243. package/web/references/taishinbank/DESIGN.md +243 -0
  244. package/web/references/teamlab/DESIGN.md +240 -0
  245. package/web/references/teamsparta/DESIGN.md +472 -0
  246. package/web/references/tellingme/DESIGN.md +392 -0
  247. package/web/references/tesla/DESIGN.md +244 -427
  248. package/web/references/thsr/DESIGN.md +284 -0
  249. package/web/references/thumbtack/DESIGN.md +302 -0
  250. package/web/references/together.ai/DESIGN.md +257 -304
  251. package/web/references/toss/DESIGN.md +251 -593
  252. package/web/references/toss-securities/DESIGN.md +255 -178
  253. package/web/references/tossbank/DESIGN.md +195 -491
  254. package/web/references/toyota/DESIGN.md +238 -0
  255. package/web/references/tving/DESIGN.md +197 -245
  256. package/web/references/typed/DESIGN.md +413 -0
  257. package/web/references/uber/DESIGN.md +256 -333
  258. package/web/references/udn/DESIGN.md +250 -0
  259. package/web/references/upbit/DESIGN.md +145 -256
  260. package/web/references/upstage/DESIGN.md +211 -204
  261. package/web/references/uswds/DESIGN.md +287 -0
  262. package/web/references/vercel/DESIGN.md +236 -440
  263. package/web/references/vuno/DESIGN.md +413 -0
  264. package/web/references/wanted/DESIGN.md +215 -487
  265. package/web/references/weverse/DESIGN.md +437 -0
  266. package/web/references/wise/DESIGN.md +223 -231
  267. package/web/references/wisetracker/DESIGN.md +462 -0
  268. package/web/references/wooribank/DESIGN.md +245 -0
  269. package/web/references/yanolja/DESIGN.md +250 -419
  270. package/web/references/yeogiotte/DESIGN.md +235 -418
  271. package/web/references/zapier/DESIGN.md +256 -377
  272. package/web/references/zendesk/DESIGN.md +288 -0
  273. package/web/references/zigzag/DESIGN.md +200 -621
  274. package/dist/install-skills-7UUDOLG2.js +0 -734
  275. package/dist/install-skills-7UUDOLG2.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/install-skills.ts","../src/core/agent-detect.ts"],"sourcesContent":["import * as p from '@clack/prompts';\nimport pc from 'picocolors';\nimport {\n readFileSync,\n readdirSync,\n writeFileSync,\n existsSync,\n mkdirSync,\n cpSync,\n rmSync,\n rmdirSync,\n statSync,\n renameSync,\n} from 'node:fs';\nimport { join, dirname, relative } from 'node:path';\nimport { homedir } from 'node:os';\nimport { createHash } from 'node:crypto';\nimport { fileURLToPath } from 'node:url';\nimport { detectInstalledAgents } from '../core/agent-detect.js';\nimport {\n CLAUDE_HOOK_PATHS,\n isKnownLegacyHook,\n isSelfConsistentManagedHook,\n renderManagedHook,\n} from './hook-contract.js';\nimport { unsafeManagedPath } from './install-path.js';\n\nexport type SkillTarget = 'claude-code' | 'codex' | 'opencode' | 'cursor';\n\nconst VALID_SKILL_TARGETS = ['claude-code', 'codex', 'opencode', 'cursor'] as const;\nconst DEVELOPMENT_ONLY_SKILLS = new Set(['omd-lab-02-design-harness']);\n\nexport function targetsAvailableForScope(\n targets: SkillTarget[],\n scope: 'project' | 'global',\n): SkillTarget[] {\n return scope === 'global' ? targets.filter((target) => target !== 'cursor') : targets;\n}\n\n/** Channels that host SKILL.md trees. Cursor is NOT one — it consumes a\n * `.cursor/rules` shim + the shared `.claude/data` catalog (issue #20). */\ntype SkillChannel = Exclude<SkillTarget, 'cursor'>;\n\nexport interface InstallSkillsOptions {\n dir?: string;\n agents?: SkillTarget[];\n force?: boolean;\n /** Refresh the project-scoped Claude hook bundle without granting permission\n * to overwrite any other unmarked user files. Intended for `omd doctor`\n * repairs when a hook is stale or locally modified. */\n repairHooks?: boolean;\n /** Non-interactive: install all skills + all agents without TUI prompt.\n * Default false → interactive multiselect when TTY available. */\n all?: boolean;\n /** Pre-select specific skill names from CLI flag (`--skills omd-init,omd-apply`).\n * Overrides interactive prompt when set. */\n skillsFilter?: string[];\n /** Pre-select specific agent names. Overrides interactive prompt when set. */\n agentsFilter?: string[];\n /** Minimal install: only the named skill files — skip sub-agents, data files,\n * hooks, and settings.json. Ideal for shipping a single standalone skill. */\n skillsOnly?: boolean;\n /** Install to each channel's user-level directory instead of this project\n * (`~/.claude`, `~/.agents` + `~/.codex`, or `~/.config/opencode`).\n * Writes skills + sub-agents (+ data); never touches global hooks/settings.\n * When unset and interactive, the TUI asks project-vs-global. */\n global?: boolean;\n}\n\ninterface InstallPlan {\n target: SkillChannel;\n destDir: string;\n layout: 'folder' | 'flat';\n}\n\nfunction findPackageRoot(): string | null {\n let cur = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 8; i++) {\n if (existsSync(join(cur, 'skills'))) return cur;\n const parent = dirname(cur);\n if (parent === cur) break;\n cur = parent;\n }\n return null;\n}\n\nfunction listShippedSkills(packageRoot: string): string[] {\n const skillsDir = join(packageRoot, 'skills');\n if (!existsSync(skillsDir)) return [];\n return readdirSync(skillsDir)\n .filter(\n (name) =>\n !DEVELOPMENT_ONLY_SKILLS.has(name) &&\n existsSync(join(skillsDir, name, 'SKILL.md')),\n )\n .sort();\n}\n\n/**\n * Canonical agent definitions live at `agents/<name>.md` (markdown with YAML\n * frontmatter). Channel-specific files (.claude/agents/*.md, .codex/agents/*.toml)\n * are generated artifacts — never the source of truth.\n *\n * The package ships only `agents/` and the generator emits per-channel files\n * into the user's project on `omd install-skills`.\n */\nfunction listCanonicalAgents(packageRoot: string): string[] {\n const dir = join(packageRoot, 'agents');\n if (!existsSync(dir)) return [];\n return readdirSync(dir)\n .filter((name) => name.startsWith('omd-') && name.endsWith('.md'))\n .sort();\n}\n\ninterface ParsedAgent {\n name: string;\n description: string;\n tools: string[];\n model: string;\n body: string;\n}\n\n/** Parse `agents/<name>.md` YAML frontmatter + body into structured form. */\nfunction parseCanonicalAgent(packageRoot: string, filename: string): ParsedAgent {\n const src = readFileSync(join(packageRoot, 'agents', filename), 'utf8');\n const match = /^---\\n([\\s\\S]*?)\\n---\\n([\\s\\S]*)$/.exec(src);\n if (!match) {\n throw new Error(`agents/${filename}: missing YAML frontmatter`);\n }\n const fm = match[1];\n const body = match[2];\n const grab = (key: string): string => {\n const re = new RegExp(`^${key}:\\\\s*(.+)$`, 'm');\n const m = re.exec(fm);\n if (!m) return '';\n const value = m[1].trim();\n if (value.startsWith('\"') && value.endsWith('\"')) {\n try {\n const decoded: unknown = JSON.parse(value);\n if (typeof decoded === 'string') return decoded;\n } catch {\n // Fall through to the conservative scalar handling below. The generated\n // channel file will still be valid even if a hand-edited description has\n // malformed escaping in the canonical YAML.\n }\n }\n if (value.startsWith(\"'\") && value.endsWith(\"'\")) {\n return value.slice(1, -1).replace(/''/g, \"'\");\n }\n return value;\n };\n return {\n name: grab('name') || filename.replace(/\\.md$/, ''),\n description: grab('description'),\n tools: grab('tools')\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean),\n model: grab('model') || 'sonnet',\n body,\n };\n}\n\n/** Render a canonical agent as a Claude Code subagent file.\n * IMPORTANT: Claude Code's subagent parser requires YAML frontmatter (`---`)\n * as the FIRST line of the file. Any preceding content (HTML comments, blank\n * lines) breaks discovery. So we encode the managed-by-omd marker as a\n * custom frontmatter field (`omd_managed: true`) instead of an HTML comment.\n */\nfunction renderClaudeAgent(a: ParsedAgent): string {\n const fm = [\n '---',\n `name: ${JSON.stringify(a.name)}`,\n `description: ${JSON.stringify(a.description)}`,\n `tools: ${JSON.stringify(a.tools)}`,\n `model: ${JSON.stringify(a.model)}`,\n `omd_managed: true`,\n '---',\n '',\n ].join('\\n');\n return fm + a.body;\n}\n\ntype NativeAgentChannel = 'codex' | 'opencode';\n\n/**\n * Canonical roles are authored once and historically used Claude Code paths and\n * tool names. A generated non-Claude role must be executable using only the\n * channel it was installed for: otherwise a perfectly healthy Codex/OpenCode\n * install tells the agent to read a sibling `.claude/skills` tree that does not\n * exist in a single-channel project.\n */\nfunction nativeAgentBody(\n body: string,\n channel: NativeAgentChannel,\n scope: 'project' | 'global',\n): string {\n const skillRoot = channel === 'codex'\n ? scope === 'global' ? '~/.agents/skills' : '.agents/skills'\n : scope === 'global' ? '~/.config/opencode/skills' : '.opencode/skills';\n\n const nativeBody = body\n .replace(/(?:~\\/)?\\.claude\\/skills\\//g, `${skillRoot}/`)\n .replace(/You are spawned as a Claude Code subagent/g, 'You run as a host-managed subagent')\n .replace(/Claude Code subagent/g, 'host-managed subagent')\n .replace(/the Agent tool/g, \"the host's native sub-agent mechanism\")\n .replace(/Agent tool/g, 'host-native sub-agent mechanism')\n .replace(/AskUserQuestion-compatible/g, 'host question-interface compatible')\n .replace(\n /launcher calls AskUserQuestion\\(questions_file\\)/g,\n 'launcher presents questions from `questions_file` to the user',\n )\n .replace(/launcher calls AskUserQuestion/g, 'launcher presents the questions to the user')\n .replace(/no AskUserQuestion/g, 'without direct user-question tools');\n return channel === 'opencode'\n ? nativeBody.replace(/\\bomd:([a-z0-9-]+)/g, 'omd-$1')\n : nativeBody;\n}\n\nfunction nativeSkillRoot(\n channel: NativeAgentChannel,\n scope: 'project' | 'global',\n): string {\n if (channel === 'codex') {\n return scope === 'global' ? '~/.agents/skills' : '.agents/skills';\n }\n return scope === 'global' ? '~/.config/opencode/skills' : '.opencode/skills';\n}\n\n/** OpenCode uses Markdown agents whose id is the filename. */\nfunction renderOpenCodeAgent(\n a: ParsedAgent,\n scope: 'project' | 'global',\n dataRoot: string,\n): string {\n const skillRoot = nativeSkillRoot('opencode', scope);\n return [\n '---',\n `description: ${JSON.stringify(a.description)}`,\n 'mode: subagent',\n '---',\n '',\n '## Installed role runtime contract',\n '',\n `- Resolve referenced OmD skills from \\`${skillRoot}/<skill>/SKILL.md\\`.`,\n `- Resolve the installed offline catalog from \\`${dataRoot}\\`.`,\n \"- Use OpenCode's native sub-agent mechanism for specialist handoffs.\",\n '',\n nativeAgentBody(a.body, 'opencode', scope).trimEnd(),\n '',\n '<!-- omd:installed-agent — generated from the canonical OmD role. -->',\n '',\n ].join('\\n');\n}\n\n/**\n * Canonical agents predate the skill-only CLI and a few bodies still describe\n * retired shell helpers. Codex receives the full role body, but those obsolete\n * invocations must not be executable instructions in the generated agent.\n */\nfunction codexSafeAgentBody(body: string): string {\n return body\n .replace(/`omd init prepare`/g, '`omd:init` skill flow')\n .replace(/^omd remember .*$/gm, 'Use the `omd:remember` skill directly with the same finding and context.')\n .replace(/`omd remember [^`]+`/g, 'the `omd:remember` skill with the same correction details')\n .replace(/\\bomd remember\\b/g, 'omd:remember skill');\n}\n\n/** Render a canonical agent as a self-contained Codex TOML definition. */\nfunction renderCodexAgent(\n a: ParsedAgent,\n scope: 'project' | 'global',\n dataRoot: string,\n): string {\n const name = a.name.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n const desc = a.description.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n const skillRoot = nativeSkillRoot('codex', scope);\n const instructions = [\n '## Codex runtime contract',\n '',\n '- Execute the OmD behavior directly from these instructions and the installed skills.',\n `- Resolve referenced OmD skills from \\`${skillRoot}/<skill>/SKILL.md\\`.`,\n `- Resolve the installed offline catalog from \\`${dataRoot}\\`.`,\n \"- Use Codex's native sub-agent mechanism for specialist handoffs.\",\n '- The OmD CLI exposes installation and diagnostics only. Do not invent or invoke removed helper subcommands.',\n '',\n codexSafeAgentBody(nativeAgentBody(a.body, 'codex', scope)).trimEnd(),\n ].join('\\n');\n if (instructions.includes(\"'''\")) {\n throw new Error(`agents/${a.name}.md: cannot encode triple single quote in Codex TOML`);\n }\n return [\n `name = \"${name}\"`,\n `description = \"${desc}\"`,\n '',\n `developer_instructions = '''`,\n instructions,\n `'''`,\n '',\n ].join('\\n');\n}\n\nfunction planForTarget(\n projectRoot: string,\n target: SkillChannel,\n scope: 'project' | 'global',\n): InstallPlan {\n switch (target) {\n case 'claude-code':\n return {\n target,\n destDir: join(projectRoot, '.claude', 'skills'),\n layout: 'folder',\n };\n case 'codex':\n // Official Codex skill discovery path is `.agents/skills/<name>/SKILL.md`\n // (developers.openai.com/codex/skills) — NOT `.codex/skills`. Folder layout\n // so multi-file skills (scripts/, references/) install + run.\n return {\n target,\n destDir: join(projectRoot, '.agents', 'skills'),\n layout: 'folder',\n };\n case 'opencode':\n // OpenCode loads project skills from `.opencode/skills/<name>/SKILL.md`\n // and global skills from `~/.config/opencode/skills/<name>/SKILL.md`.\n return {\n target,\n destDir: scope === 'global'\n ? join(projectRoot, '.config', 'opencode', 'skills')\n : join(projectRoot, '.opencode', 'skills'),\n layout: 'folder',\n };\n }\n}\n\n/**\n * Channel → shared data dir (`<dir>/data/…`) for read-only data assets\n * (catalog JSONs, reference DESIGN.md catalog, ctx-prime helper scripts).\n * Single lookup table replacing the repeated if-else/ternary chains (issue #28).\n * `null` = the channel hosts no data dir of its own:\n * - cursor reads the SHARED `.claude/data` path (issue #20) — resolved by\n * `dataDirFor()` below, which also applies the claude-code dedup guard.\n * (Helper scripts intentionally stay skill-channel-only — a cursor-only\n * install gets the catalog + JSONs but no ctx-prime, matching the shim's scope.)\n */\ntype DataChannelDir = '.claude' | '.codex' | '.opencode' | '.config/opencode';\n\nconst CHANNEL_DATA_DIRS: Record<SkillTarget, DataChannelDir | null> = {\n 'claude-code': '.claude',\n codex: '.codex',\n opencode: '.opencode',\n cursor: null,\n};\n\n/**\n * Where a target's data assets (data JSONs + reference catalog) land. Cursor\n * reuses `.claude/data` so the catalog location stays single (issue #20) —\n * but ONLY when claude-code isn't also selected: the claude-code pass already\n * writes there, and a second pass would double-copy the catalog.\n */\nexport function dataDirFor(\n target: SkillTarget,\n targets: SkillTarget[]\n): DataChannelDir | null {\n if (target === 'cursor') {\n return targets.includes('claude-code') ? null : '.claude';\n }\n return CHANNEL_DATA_DIRS[target];\n}\n\nfunction dataDirForScope(\n target: SkillTarget,\n targets: SkillTarget[],\n scope: 'project' | 'global',\n): DataChannelDir | null {\n if (scope === 'global' && target === 'opencode') return '.config/opencode';\n return dataDirFor(target, targets);\n}\n\nconst MANAGED_HEADER =\n '<!-- omd:installed-skill — managed by `omd install-skills`. Do not edit; rerun the command to refresh. -->';\n\n// Substring shared by old (line 1) and new (after-frontmatter) marker formats.\n// Used for detection so upgrades from pre-v1.7.2 installs still refresh.\nconst MANAGED_MARKER_SUBSTR = 'omd:installed-skill';\n\n/**\n * Write the managed marker AFTER the YAML frontmatter block so the very first\n * line of the installed file is `---`. Claude Code's skill loader reads the\n * frontmatter `name`/`description` only when `---` is line 1 — a leading HTML\n * comment makes it register the comment as the description (issue #17).\n *\n * If the source has no frontmatter (shouldn't happen for SKILL.md, but be\n * defensive), fall back to prepending the marker.\n */\nfunction withManagedMarker(src: string): string {\n // \\r?\\n: a CRLF checkout (Windows core.autocrlf) must not miss the\n // frontmatter and fall back to a line-1 marker — that reintroduces #17.\n const fm = /^(---\\r?\\n[\\s\\S]*?\\r?\\n---\\r?\\n)([\\s\\S]*)$/.exec(src);\n if (!fm) {\n return MANAGED_HEADER + '\\n\\n' + src;\n }\n return fm[1] + MANAGED_HEADER + '\\n\\n' + fm[2];\n}\n\n/**\n * Detect an omd-managed installed-skill file. Matches both the new format\n * (marker after frontmatter) and the legacy format (marker on line 1) by\n * scanning the first ~30 lines for the marker substring. This keeps upgrades\n * working: a pre-v1.7.2 file with the marker at line 1 is still recognized as\n * managed and gets refreshed rather than skipped as user-edited drift.\n */\nfunction isManagedSkillFile(content: string): boolean {\n if (!content) return false;\n const head = content.split('\\n', 30).join('\\n');\n return head.includes(MANAGED_MARKER_SUBSTR);\n}\n\nfunction withGlobalDataHint(src: string, globalDataRoot: string | null): string {\n if (!globalDataRoot) return src;\n const hint = [\n `> **Installed global data root (highest priority):** \\`${globalDataRoot}\\`.`,\n '> Resolve catalog JSON, references, and helper scripts there before project-relative fallbacks.',\n '',\n ].join('\\n');\n const fm = /^(---\\r?\\n[\\s\\S]*?\\r?\\n---\\r?\\n)([\\s\\S]*)$/.exec(src);\n return fm ? fm[1] + hint + fm[2] : hint + src;\n}\n\nfunction renderSkillForChannel(\n src: string,\n folderName: string,\n target: SkillChannel,\n): string {\n // Canonical skill sources may use either the repository's historical\n // namespaced form (`omd:apply`) or portable Agent Skills hyphen-case\n // (`omd-apply`). Always derive the installed name from the folder so every\n // channel gets the contract it actually accepts.\n const installedName = target === 'opencode'\n ? folderName\n : folderName.replace(/^omd-/, 'omd:');\n const rendered = src.replace(\n /^name:\\s*[^\\r\\n]+$/m,\n `name: ${installedName}`,\n );\n return target === 'opencode'\n ? rendered.replace(/\\bomd:([a-z0-9][a-z0-9-]*)\\b/g, 'omd-$1')\n : rendered;\n}\n\ninterface InstallResult {\n target: SkillTarget;\n skill: string;\n destPath: string;\n status: 'created' | 'updated' | 'removed' | 'unchanged' | 'skipped-drift' | 'skipped-incompat';\n reason?: 'unsafe-path';\n}\n\n/**\n * Codex used `.codex/skills` before the official cross-agent skill path settled\n * on `.agents/skills`. Current Codex still scans the legacy tree, so an old\n * marker-first SKILL.md can produce loader errors even after the new copy is\n * healthy. Remove only OmD-owned legacy entrypoints; preserve user files and\n * every sidecar in the directory.\n */\nfunction removeManagedLegacyCodexSkills(root: string): InstallResult[] {\n const legacyRoot = join(root, '.codex', 'skills');\n if (!existsSync(legacyRoot)) return [];\n const results: InstallResult[] = [];\n for (const entry of readdirSync(legacyRoot, { withFileTypes: true })) {\n if (!entry.isDirectory()) continue;\n const skillPath = join(legacyRoot, entry.name, 'SKILL.md');\n if (!existsSync(skillPath)) continue;\n const content = readFileSync(skillPath, 'utf8');\n if (!isManagedSkillFile(content)) continue;\n rmSync(skillPath, { force: true });\n const dir = dirname(skillPath);\n if (readdirSync(dir).length === 0) rmdirSync(dir);\n results.push({\n target: 'codex',\n skill: `legacy-skill:${entry.name}`,\n destPath: skillPath,\n status: 'removed',\n });\n }\n return results;\n}\n\n// Skill-tree entries that must never be installed (runtime state, caches, OS cruft).\nconst IGNORED_SKILL_ENTRIES = new Set(['.runtime', '__pycache__', '.DS_Store']);\n\nfunction isIgnoredSkillTreeEntry(name: string): boolean {\n return IGNORED_SKILL_ENTRIES.has(name) || name.endsWith('.pyc');\n}\n\n/** Compare only package-owned entries. Destination-only sidecars are user\n * content and deliberately do not make an otherwise current install dirty. */\nfunction shippedSkillTreeMatches(sourceDir: string, destinationDir: string): boolean {\n if (!existsSync(destinationDir)) return false;\n for (const entry of readdirSync(sourceDir, { withFileTypes: true })) {\n if (entry.name === 'SKILL.md' || isIgnoredSkillTreeEntry(entry.name)) continue;\n const source = join(sourceDir, entry.name);\n const destination = join(destinationDir, entry.name);\n if (!existsSync(destination)) return false;\n try {\n const destinationStat = statSync(destination);\n if (entry.isDirectory()) {\n if (!destinationStat.isDirectory() || !shippedSkillTreeMatches(source, destination)) {\n return false;\n }\n } else if (\n !destinationStat.isFile() ||\n !readFileSync(source).equals(readFileSync(destination))\n ) {\n return false;\n }\n } catch {\n return false;\n }\n }\n return true;\n}\n\n/**\n * A skill may restrict itself to specific agent channels via a frontmatter line\n * `x-omd-channels: claude-code` (comma/space separated). Returns the allowed\n * channels, or null when channel-agnostic (installs anywhere). Used by skills that\n * depend on a particular agent runtime — e.g. claude-design needs Claude Code's\n * claude-in-chrome MCP + Bash/python/node and is therefore claude-code only.\n */\nfunction parseSkillChannels(skillMd: string): SkillChannel[] | null {\n const fm = /^---\\n([\\s\\S]*?)\\n---/.exec(skillMd);\n if (!fm) return null;\n const m = /^x-omd-channels:\\s*(.+)$/m.exec(fm[1]);\n if (!m) return null;\n const valid: SkillChannel[] = ['claude-code', 'codex', 'opencode'];\n const list = m[1]\n .split(/[,\\s]+/)\n .map((s) => s.trim())\n .filter((s): s is SkillChannel => (valid as string[]).includes(s));\n return list.length > 0 ? list : null;\n}\n\n/**\n * The agent channels a skill can install into: its declared `x-omd-channels`\n * (if any), else all channels. All three channels now use folder layout\n * (.claude/skills, .agents/skills, .opencode/skills) so multi-file skills with\n * scripts/references install everywhere — the only restriction is what the skill\n * itself declares (e.g. claude-design needs a browser-driving runtime).\n */\nfunction skillSupportedChannels(packageRoot: string, skill: string): SkillChannel[] {\n return (\n parseSkillChannels(readFileSync(join(packageRoot, 'skills', skill, 'SKILL.md'), 'utf8')) ??\n (['claude-code', 'codex', 'opencode'] as SkillChannel[])\n );\n}\n\nfunction installOne(\n packageRoot: string,\n installRoot: string,\n plan: InstallPlan,\n skill: string,\n force: boolean,\n globalDataRoot: string | null,\n): InstallResult {\n const skillDir = join(packageRoot, 'skills', skill);\n const src = readFileSync(join(skillDir, 'SKILL.md'), 'utf8');\n // Project installs keep the canonical cross-channel fallback order intact.\n // Global installs need one absolute, machine-local root because their skills\n // execute from arbitrary project working directories.\n const channelSrc = withGlobalDataHint(\n renderSkillForChannel(src, skill, plan.target),\n globalDataRoot,\n );\n // Marker goes AFTER frontmatter so `---` stays line 1 (issue #17).\n const managed = withManagedMarker(channelSrc);\n\n // Respect a skill's declared channel restriction (frontmatter `x-omd-channels:`).\n const channels = parseSkillChannels(src);\n if (channels && !channels.includes(plan.target)) {\n return {\n target: plan.target,\n skill,\n destPath: join(plan.destDir, skill + '.md'),\n status: 'skipped-incompat',\n };\n }\n\n // A skill is \"multi-file\" when it ships more than SKILL.md (scripts/, references/, …).\n const extras = readdirSync(skillDir).filter(\n (n) => n !== 'SKILL.md' && !IGNORED_SKILL_ENTRIES.has(n)\n );\n const isMultiFile = extras.length > 0;\n\n // Retained for compatibility with any future flat-layout channel. All current\n // skill channels use folder layout and therefore keep scripts/references.\n if (plan.layout !== 'folder' && isMultiFile) {\n return {\n target: plan.target,\n skill,\n destPath: join(plan.destDir, skill + '.md'),\n status: 'skipped-incompat',\n };\n }\n\n const destPath =\n plan.layout === 'folder'\n ? join(plan.destDir, skill, 'SKILL.md')\n : join(plan.destDir, skill + '.md');\n\n if (unsafeManagedPath(installRoot, destPath)) {\n return { target: plan.target, skill, destPath, status: 'skipped-drift', reason: 'unsafe-path' };\n }\n\n const exists = existsSync(destPath);\n const existing = exists ? readFileSync(destPath, 'utf8') : '';\n\n // Drift protection guards the user-editable SKILL.md. Multi-file skills also\n // compare every shipped sidecar, while ignoring destination-only user files.\n // This keeps a second install genuinely idempotent without hiding stale tools.\n if (exists && existing === managed) {\n const extrasCurrent = !isMultiFile || shippedSkillTreeMatches(\n skillDir,\n join(plan.destDir, skill),\n );\n if (extrasCurrent) {\n return { target: plan.target, skill, destPath, status: 'unchanged' };\n }\n }\n // Drift = a file we didn't write. Detect the marker anywhere in the head\n // (new after-frontmatter position OR legacy line-1 position) so pre-v1.7.2\n // installs are recognized as managed and refreshed, not skipped.\n if (exists && !isManagedSkillFile(existing) && !force) {\n return { target: plan.target, skill, destPath, status: 'skipped-drift' };\n }\n\n mkdirSync(dirname(destPath), { recursive: true });\n writeFileSync(destPath, managed, 'utf8');\n\n // Copy the rest of the skill tree (scripts/, references/, …) for folder layout.\n if (plan.layout === 'folder' && isMultiFile) {\n const destSkillDir = join(plan.destDir, skill);\n for (const entry of extras) {\n cpSync(join(skillDir, entry), join(destSkillDir, entry), {\n recursive: true,\n filter: (s) => !/(\\/__pycache__|\\/\\.runtime|\\.pyc$|\\.DS_Store$)/.test(s),\n });\n }\n }\n\n return {\n target: plan.target,\n skill,\n destPath,\n status: exists ? 'updated' : 'created',\n };\n}\n\n/** Install a hook script from package's .claude/hooks/ to project. */\nfunction installHookFile(\n packageRoot: string,\n projectRoot: string,\n filename: (typeof CLAUDE_HOOK_PATHS)[number],\n force: boolean\n): InstallResult {\n const target: SkillTarget = 'claude-code';\n const skillLabel = `hook:${filename}`;\n const srcPath = join(packageRoot, '.claude', 'hooks', filename);\n const destPath = join(projectRoot, '.claude', 'hooks', filename);\n\n if (!existsSync(srcPath)) {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n if (unsafeManagedPath(projectRoot, destPath)) {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift', reason: 'unsafe-path' };\n }\n const src = readFileSync(srcPath, 'utf8');\n const managed = renderManagedHook(src);\n const exists = existsSync(destPath);\n const existing = exists ? readFileSync(destPath, 'utf8') : '';\n if (exists && existing === managed) {\n return { target, skill: skillLabel, destPath, status: 'unchanged' };\n }\n if (\n exists &&\n !force &&\n !isSelfConsistentManagedHook(existing) &&\n !isKnownLegacyHook(filename, existing)\n ) {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n mkdirSync(dirname(destPath), { recursive: true });\n const tempPath = `${destPath}.omd-${process.pid}-${Date.now()}.tmp`;\n try {\n writeFileSync(tempPath, managed, { encoding: 'utf8', flag: 'wx' });\n renameSync(tempPath, destPath);\n } catch (error) {\n rmSync(tempPath, { force: true });\n throw error;\n }\n return { target, skill: skillLabel, destPath, status: exists ? 'updated' : 'created' };\n}\n\ntype JsonObject = Record<string, unknown>;\n\nfunction isJsonObject(value: unknown): value is JsonObject {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction parseJsonObject(value: string): JsonObject | null {\n try {\n const parsed: unknown = JSON.parse(value);\n return isJsonObject(parsed) ? parsed : null;\n } catch {\n return null;\n }\n}\n\nfunction isOmdHook(value: unknown): boolean {\n if (!isJsonObject(value) || typeof value.command !== 'string') return false;\n const command = value.command;\n const ownedHookPaths = [\n '${CLAUDE_PROJECT_DIR}/.claude/hooks/skill-activation.cjs',\n '${CLAUDE_PROJECT_DIR}/.claude/hooks/session-state-loader.cjs',\n '${CLAUDE_PROJECT_DIR}/.claude/hooks/post-edit-watch.cjs',\n '${CLAUDE_PROJECT_DIR}/.claude/hooks/session-end-foldin.cjs',\n // Pre-v1.8 installs briefly registered this repository-only helper.\n '${CLAUDE_PROJECT_DIR}/scripts/context_restore.sh',\n ];\n return ownedHookPaths.some((path) => command.includes(path));\n}\n\nconst REPOSITORY_ONLY_CONTEXT_RESTORE_COMMAND =\n 'bash ${CLAUDE_PROJECT_DIR}/scripts/context_restore.sh';\n\n/**\n * This hook belongs to the oh-my-design repository continuity protocol. The\n * npm package does not ship scripts/context_restore.sh, so it must never be\n * copied into a consumer project from the repository's source settings.\n */\nfunction isRepositoryOnlySourceHook(value: unknown): boolean {\n return isJsonObject(value) &&\n typeof value.command === 'string' &&\n value.command === REPOSITORY_ONLY_CONTEXT_RESTORE_COMMAND;\n}\n\nfunction stripHookEntries(\n groups: unknown[],\n shouldStrip: (hook: unknown) => boolean,\n): unknown[] {\n const retained: unknown[] = [];\n for (const group of groups) {\n if (!isJsonObject(group) || !Array.isArray(group.hooks)) {\n retained.push(group);\n continue;\n }\n const hooks = group.hooks.filter((hook) => !shouldStrip(hook));\n if (hooks.length > 0) retained.push({ ...group, hooks });\n }\n return retained;\n}\n\n/** Remove only OmD command entries while preserving user hooks in the same group. */\nfunction stripInstalledOmdHooks(groups: unknown[]): unknown[] {\n return stripHookEntries(groups, isOmdHook);\n}\n\n/** Remove repository-only hooks from the package settings template. */\nfunction stripRepositoryOnlySourceHooks(source: JsonObject): JsonObject {\n if (!isJsonObject(source.hooks)) return source;\n const hooks: JsonObject = {};\n for (const [event, groups] of Object.entries(source.hooks)) {\n hooks[event] = Array.isArray(groups)\n ? stripHookEntries(groups, isRepositoryOnlySourceHook)\n : groups;\n }\n return { ...source, hooks };\n}\n\n/** Merge OmD hook groups into a user's settings without replacing other keys/hooks. */\nfunction mergeClaudeSettings(existing: JsonObject, source: JsonObject): JsonObject {\n const installableSource = stripRepositoryOnlySourceHooks(source);\n const existingHooks = isJsonObject(existing.hooks) ? existing.hooks : {};\n const sourceHooks = isJsonObject(installableSource.hooks) ? installableSource.hooks : {};\n const mergedHooks: JsonObject = { ...existingHooks };\n\n for (const event of new Set([...Object.keys(existingHooks), ...Object.keys(sourceHooks)])) {\n const userGroups = Array.isArray(existingHooks[event]) ? existingHooks[event] : [];\n const omdGroups = Array.isArray(sourceHooks[event]) ? sourceHooks[event] : [];\n mergedHooks[event] = [...stripInstalledOmdHooks(userGroups), ...omdGroups];\n }\n\n // Existing user settings win for every non-hook key. The source supplies\n // schema/metadata only when the user did not already define them.\n return { ...installableSource, ...existing, hooks: mergedHooks };\n}\n\n/** Install .claude/settings.json via a structural, user-preserving hook merge. */\nfunction installSettingsJson(\n packageRoot: string,\n projectRoot: string,\n force: boolean\n): InstallResult {\n const target: SkillTarget = 'claude-code';\n const skillLabel = 'settings:.claude/settings.json';\n const srcPath = join(packageRoot, '.claude', 'settings.json');\n const destPath = join(projectRoot, '.claude', 'settings.json');\n if (!existsSync(srcPath)) {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n if (unsafeManagedPath(projectRoot, destPath)) {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift', reason: 'unsafe-path' };\n }\n const src = readFileSync(srcPath, 'utf8');\n const sourceSettings = parseJsonObject(src);\n if (!sourceSettings) {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n const exists = existsSync(destPath);\n const existing = exists ? readFileSync(destPath, 'utf8') : '';\n const existingSettings = exists ? parseJsonObject(existing) : {};\n if (exists && !existingSettings && !force) {\n // Invalid JSON cannot be merged safely. --force remains the explicit escape hatch.\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n const merged = existingSettings\n ? mergeClaudeSettings(existingSettings, sourceSettings)\n : stripRepositoryOnlySourceHooks(sourceSettings);\n if (exists && existingSettings && JSON.stringify(existingSettings) === JSON.stringify(merged)) {\n return { target, skill: skillLabel, destPath, status: 'unchanged' };\n }\n mkdirSync(dirname(destPath), { recursive: true });\n writeFileSync(destPath, JSON.stringify(merged, null, 2) + '\\n', 'utf8');\n return { target, skill: skillLabel, destPath, status: exists ? 'updated' : 'created' };\n}\n\n/**\n * Copy a read-only data asset (reference-fingerprints.json, vocabulary.json, …)\n * from the package's `data/` into the project's `.claude/data/` or `.codex/data/`.\n * The skill reads these at runtime — they replace the deprecated `omd init recommend` CLI.\n */\nfunction installDataFile(\n packageRoot: string,\n projectRoot: string,\n channelDir: string,\n dataFilename: string,\n _force: boolean,\n // Cursor reuses the `.claude` data dir (single catalog path) — callers pass\n // an explicit target so the results table reports the real channel.\n target: SkillTarget\n): InstallResult {\n const skillLabel = `data:${dataFilename}`;\n\n const srcPath = join(packageRoot, 'data', dataFilename);\n const destPath = join(projectRoot, channelDir, 'data', dataFilename);\n\n if (!existsSync(srcPath)) {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n if (unsafeManagedPath(projectRoot, destPath)) {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift', reason: 'unsafe-path' };\n }\n\n const src = readFileSync(srcPath, 'utf8');\n const exists = existsSync(destPath);\n const existing = exists ? readFileSync(destPath, 'utf8') : '';\n\n // Data files are pure copies — no managed header (would corrupt JSON).\n if (exists && existing === src) {\n return { target, skill: skillLabel, destPath, status: 'unchanged' };\n }\n // This directory is a package-managed read-only cache, not a customization\n // surface. Refresh stale catalog metadata on every upgrade; otherwise the\n // skill and copied reference set can silently disagree until --force is used.\n mkdirSync(dirname(destPath), { recursive: true });\n writeFileSync(destPath, src, 'utf8');\n return {\n target,\n skill: skillLabel,\n destPath,\n status: exists ? 'updated' : 'created',\n };\n}\n\n/**\n * Generate a per-channel agent file from the canonical `agents/<name>.md`.\n *\n * Channel = 'claude' → emits `.claude/agents/<name>.md` (markdown w/ frontmatter)\n * Channel = 'codex' → emits `.codex/agents/<name>.toml` (TOML pointer)\n */\nfunction installAgentFile(\n packageRoot: string,\n projectRoot: string,\n channel: 'claude' | 'codex' | 'opencode',\n filename: string,\n force: boolean,\n scope: 'project' | 'global',\n): InstallResult {\n const target: SkillTarget = channel === 'claude'\n ? 'claude-code'\n : channel === 'codex'\n ? 'codex'\n : 'opencode';\n const skillLabel = `agent:${filename}`;\n\n const parsed = parseCanonicalAgent(packageRoot, filename);\n const nativeDataRoot = scope === 'global'\n ? channel === 'codex'\n ? join(projectRoot, '.codex', 'data')\n : join(projectRoot, '.config', 'opencode', 'data')\n : channel === 'codex'\n ? '.codex/data'\n : '.opencode/data';\n const rendered = channel === 'claude'\n ? renderClaudeAgent(parsed)\n : channel === 'codex'\n ? renderCodexAgent(parsed, scope, nativeDataRoot)\n : renderOpenCodeAgent(parsed, scope, nativeDataRoot);\n\n const destFilename = channel === 'codex'\n ? filename.replace(/\\.md$/, '.toml')\n : filename;\n const channelRoot = channel === 'claude'\n ? '.claude'\n : channel === 'codex'\n ? '.codex'\n : scope === 'global'\n ? join('.config', 'opencode')\n : '.opencode';\n const destPath = join(\n projectRoot,\n channelRoot,\n 'agents',\n destFilename\n );\n\n // For Claude Code: managed marker is encoded as `omd_managed: true` INSIDE the\n // frontmatter (rendered above) — no HTML comment can precede `---` or the\n // subagent loader rejects the file.\n // For Codex: TOML allows leading comments, so `# omd:installed-agent ...` is fine.\n const managed = channel === 'claude' || channel === 'opencode'\n ? rendered\n : '# omd:installed-agent — generated from agents/' +\n filename +\n ' by `omd install-skills`. Do not edit; rerun the command to refresh.\\n\\n' +\n rendered;\n\n if (unsafeManagedPath(projectRoot, destPath)) {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift', reason: 'unsafe-path' };\n }\n\n const exists = existsSync(destPath);\n const existing = exists ? readFileSync(destPath, 'utf8') : '';\n\n if (exists && existing === managed) {\n return { target, skill: skillLabel, destPath, status: 'unchanged' };\n }\n\n // Drift detection sentinels:\n // Claude — look for `omd_managed: true` line inside frontmatter\n // Codex — look for `# omd:installed-agent` comment\n const isManaged = channel === 'claude'\n ? /\\nomd_managed:\\s*true\\b/.test(existing)\n : channel === 'codex'\n ? existing.startsWith('# omd:installed-agent')\n : existing.includes('<!-- omd:installed-agent');\n\n if (exists && !isManaged && !force) {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n\n mkdirSync(dirname(destPath), { recursive: true });\n writeFileSync(destPath, managed, 'utf8');\n return {\n target,\n skill: skillLabel,\n destPath,\n status: exists ? 'updated' : 'created',\n };\n}\n\n/**\n * Copy the reference catalog (`web/references/<id>/DESIGN.md`) into the project's\n * `.claude/data/references/<id>/DESIGN.md` so it's reachable on clean npx installs\n * — where there is no `node_modules` and no dev `web/references` (issue #16).\n *\n * Only DESIGN.md per id is copied (not _promo.json/_research.md/screenshots) to\n * keep the install lean. Idempotent: skips ids whose DESIGN.md already matches.\n * Returns the number of catalog entries available at the destination after\n * the sync (including already-current files), so an idempotent upgrade never\n * reports the misleading \"0 catalog refs installed\".\n */\nfunction installReferenceCatalog(\n packageRoot: string,\n installRoot: string,\n channelDir: string,\n force: boolean\n): number {\n const srcRoot = join(packageRoot, 'web', 'references');\n if (!existsSync(srcRoot)) return 0;\n const destRoot = join(installRoot, channelDir, 'data', 'references');\n if (unsafeManagedPath(installRoot, destRoot)) return 0;\n const manifestPath = join(destRoot, '.omd-managed.json');\n\n let previouslyManaged = new Set<string>();\n let previousHashes = new Map<string, string>();\n if (existsSync(manifestPath)) {\n try {\n const parsed: unknown = JSON.parse(readFileSync(manifestPath, 'utf8'));\n if (\n isJsonObject(parsed) &&\n Array.isArray(parsed.managedIds) &&\n parsed.managedIds.every((id) => typeof id === 'string')\n ) {\n previouslyManaged = new Set(parsed.managedIds as string[]);\n if (isJsonObject(parsed.managedDesignHashes)) {\n previousHashes = new Map(\n Object.entries(parsed.managedDesignHashes)\n .filter((entry): entry is [string, string] => typeof entry[1] === 'string'),\n );\n }\n }\n } catch {\n // A corrupt/missing manifest must fail safe: refresh known canonical IDs,\n // but never infer ownership of unknown destination directories.\n }\n }\n\n let available = 0;\n const sourceIds = new Set<string>();\n const managedDesignHashes: Record<string, string> = {};\n for (const id of readdirSync(srcRoot)) {\n const srcDesign = join(srcRoot, id, 'DESIGN.md');\n if (!existsSync(srcDesign)) continue;\n sourceIds.add(id);\n available++;\n const destDesign = join(destRoot, id, 'DESIGN.md');\n const src = readFileSync(srcDesign, 'utf8');\n const srcHash = createHash('sha256').update(src).digest('hex');\n if (existsSync(destDesign)) {\n const existing = readFileSync(destDesign, 'utf8');\n if (existing === src) {\n managedDesignHashes[id] = srcHash;\n continue;\n }\n const previousHash = previousHashes.get(id);\n const unchangedSinceLastInstall = previousHash !== undefined &&\n createHash('sha256').update(existing).digest('hex') === previousHash;\n if (!force && !unchangedSinceLastInstall) {\n // Same-id files can be user-curated references. Without a matching\n // installer hash, preserve them and stop claiming ownership.\n continue;\n }\n }\n mkdirSync(dirname(destDesign), { recursive: true });\n writeFileSync(destDesign, src, 'utf8');\n managedDesignHashes[id] = srcHash;\n }\n // Prune only IDs that a prior OmD manifest explicitly claimed. Unknown IDs\n // can be local/private references and must survive an installer upgrade.\n if (existsSync(destRoot)) {\n for (const id of readdirSync(destRoot)) {\n if (previouslyManaged.has(id) && !sourceIds.has(id)) {\n const retiredRoot = join(destRoot, id);\n const retiredDesign = join(retiredRoot, 'DESIGN.md');\n const previousHash = previousHashes.get(id);\n const safeToRemove = existsSync(retiredDesign) && (\n force || (\n previousHash !== undefined &&\n createHash('sha256').update(readFileSync(retiredDesign, 'utf8')).digest('hex') === previousHash\n )\n );\n if (safeToRemove) rmSync(retiredDesign, { force: true });\n if (existsSync(retiredRoot) && readdirSync(retiredRoot).length === 0) {\n rmdirSync(retiredRoot);\n }\n }\n }\n }\n mkdirSync(destRoot, { recursive: true });\n writeFileSync(\n manifestPath,\n JSON.stringify({\n schemaVersion: 2,\n managedIds: Object.keys(managedDesignHashes).sort(),\n managedDesignHashes,\n }, null, 2) + '\\n',\n 'utf8',\n );\n return available;\n}\n\n/**\n * Cursor channel shim — Cursor has no skill/agent surface; it consumes a\n * project rule at `.cursor/rules/omd-design.mdc`. Frontmatter, body, and the\n * body-hash marker below mirror the omd:sync skill's cursor template EXACTLY\n * (skills/omd-sync/SKILL.md, \"whole\" mode: hash = sha256 of the body text,\n * 12-char hex prefix), so a later omd:sync run reads the installer-written\n * file as `clean` rather than drifted (issue #20).\n */\nconst CURSOR_RULE_BODY = [\n 'The authoritative design spec lives at `@DESIGN.md` (repo root). Open and read before generating/modifying UI.',\n '',\n 'Pending preference corrections: `@.omd/preferences.md`.',\n '',\n 'Precedence: DESIGN.md > preferences.md > framework defaults.',\n '',\n 'If DESIGN.md is missing and the user asks to establish a design system:',\n '1. Inspect the existing product, routes, and constraints.',\n '2. Read `.claude/data/reference-fingerprints.json` and only recommend ids present there.',\n '3. Load the chosen `.claude/data/references/<id>/DESIGN.md`, explain the project-specific delta, and ask before writing root DESIGN.md.',\n '4. Unknown reference fields stay absent; never substitute a system font, generic component, or guessed token as a brand fact.',\n '',\n 'When applying DESIGN.md, preserve existing behavior and user copy unless asked, then verify the actual product route and accessibility before reporting completion.',\n 'Cursor receives this rule and the catalog, not OmD named skills or sub-agents; execute the contract directly from natural-language requests.',\n].join('\\n');\n\nfunction renderCursorRule(): string {\n const hash = createHash('sha256').update(CURSOR_RULE_BODY).digest('hex').slice(0, 12);\n return [\n '---',\n 'description: Authoritative brand & UI design system. Read DESIGN.md before UI work.',\n 'globs:',\n ' - \"**/*.tsx\"',\n ' - \"**/*.jsx\"',\n ' - \"**/*.vue\"',\n ' - \"**/*.svelte\"',\n ' - \"**/*.css\"',\n ' - \"**/*.scss\"',\n ' - \"**/tailwind.config.*\"',\n ' - \"**/components/**\"',\n ' - \"**/app/**/page.*\"',\n 'alwaysApply: false',\n '---',\n '',\n `<!-- omd:start v=1 hash=${hash} -->`,\n CURSOR_RULE_BODY,\n '<!-- omd:end -->',\n '',\n ].join('\\n');\n}\n\nfunction installCursorRule(installRoot: string, force: boolean): InstallResult {\n const target: SkillTarget = 'cursor';\n const skillLabel = 'rule:omd-design.mdc';\n const destPath = join(installRoot, '.cursor', 'rules', 'omd-design.mdc');\n const rendered = renderCursorRule();\n\n if (unsafeManagedPath(installRoot, destPath)) {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift', reason: 'unsafe-path' };\n }\n\n const exists = existsSync(destPath);\n const existing = exists ? readFileSync(destPath, 'utf8') : '';\n if (exists && existing === rendered) {\n return { target, skill: skillLabel, destPath, status: 'unchanged' };\n }\n // The omd marker block doubles as the managed sentinel (matching omd:sync's\n // whole-mode rules): a file without it is user content → drift unless --force.\n if (exists && !existing.includes('<!-- omd:start') && !force) {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n mkdirSync(dirname(destPath), { recursive: true });\n writeFileSync(destPath, rendered, 'utf8');\n return { target, skill: skillLabel, destPath, status: exists ? 'updated' : 'created' };\n}\n\nconst STATUS_LABEL: Record<InstallResult['status'], string> = {\n created: pc.green('created'),\n updated: pc.cyan('updated'),\n removed: pc.magenta('removed'),\n unchanged: pc.dim('unchanged'),\n 'skipped-drift': pc.yellow('skipped'),\n 'skipped-incompat': pc.yellow('skipped (claude-code only)'),\n};\n\nfunction autoDetectTargets(projectRoot: string): SkillTarget[] {\n const presence = detectInstalledAgents(projectRoot);\n const targets: SkillTarget[] = [];\n if (presence.claudeCode) targets.push('claude-code');\n if (presence.codex) targets.push('codex');\n if (presence.opencode) targets.push('opencode');\n // Cursor hosts no skills — its channel writes the .cursor/rules shim + the\n // shared .claude/data catalog (issue #20). Only when .cursor is detected;\n // the no-signal fallback below stays skill-channel-only so we never drop a\n // .cursor dir into projects that don't use Cursor.\n if (presence.cursor) targets.push('cursor');\n if (targets.length === 0) {\n // Fallback: install for all three skill channels so user gets coverage\n // even without explicit signal. Idempotent so cost is low.\n return ['claude-code', 'codex', 'opencode'];\n }\n\n return targets;\n}\n\nexport async function runInstallSkills(\n opts: InstallSkillsOptions = {}\n): Promise<number> {\n const projectRoot = opts.dir ?? process.cwd();\n const packageRoot = findPackageRoot();\n if (!packageRoot) {\n console.error(pc.red('omd install-skills: package data not found'));\n return 1;\n }\n\n const allSkills = listShippedSkills(packageRoot);\n if (allSkills.length === 0) {\n console.error(pc.red('omd install-skills: no skills found in package'));\n return 1;\n }\n const allAgents = listCanonicalAgents(packageRoot);\n\n if (opts.agents) {\n const invalidTargets = (opts.agents as string[]).filter(\n (target) => !(VALID_SKILL_TARGETS as readonly string[]).includes(target),\n );\n if (invalidTargets.length > 0) {\n console.error(\n pc.red(\n `omd install-skills: invalid agent channel(s): ${invalidTargets.join(', ')}. ` +\n `Choose from ${VALID_SKILL_TARGETS.join(', ')}.`,\n ),\n );\n return 1;\n }\n if (opts.agents.length === 0) {\n console.error(pc.red('omd install-skills: no agent channel selected'));\n return 1;\n }\n }\n\n if (opts.skillsFilter) {\n const unknownSkills = opts.skillsFilter.filter((skill) => !allSkills.includes(skill));\n if (unknownSkills.length > 0) {\n console.error(\n pc.red(`omd install-skills: unknown skill(s): ${unknownSkills.join(', ')}`),\n );\n return 1;\n }\n if (opts.skillsFilter.length === 0) {\n console.error(pc.red('omd install-skills: no skill selected'));\n return 1;\n }\n }\n\n if (opts.agentsFilter) {\n const availableAgentIds = new Set(allAgents.map((name) => name.replace(/\\.md$/, '')));\n const unknownAgents = opts.agentsFilter.filter((agent) => !availableAgentIds.has(agent));\n if (unknownAgents.length > 0) {\n console.error(\n pc.red(`omd install-skills: unknown sub-agent(s): ${unknownAgents.join(', ')}`),\n );\n return 1;\n }\n }\n\n const force = opts.force ?? false;\n const minimal = opts.skillsOnly === true;\n // Install scope: 'project' (channel-local dirs under cwd) or 'global'\n // (channel-specific user dirs). --global forces it; otherwise the interactive\n // TUI asks. Global writes skills + sub-agents\n // (+ data) to the user-level dir but never touches global hooks/settings.json.\n let scope: 'project' | 'global' = opts.global ? 'global' : 'project';\n\n p.intro(\n pc.bold('omd install-skills') +\n pc.dim(` (${relative(process.cwd(), projectRoot) || '.'})`)\n );\n\n // Each dimension (scope / skills / sub-agents / channels) is resolved\n // independently: a CLI flag pins it; otherwise we prompt — but only when stdin\n // is a TTY and --all wasn't passed. This is the key fix: `--skills X` or\n // `--skills-only` no longer suppress the *channel* (where to install) prompt —\n // they only pin the dimension they name.\n const isTTY = Boolean(process.stdin.isTTY && process.stdout.isTTY);\n const interactive = isTTY && !opts.all;\n\n const detected = autoDetectTargets(projectRoot);\n // Real presence (not the all-3 fallback) — used for hint labels + prompt defaults.\n const presence = detectInstalledAgents(projectRoot);\n const actuallyDetected: SkillTarget[] = [\n presence.claudeCode ? 'claude-code' : null,\n presence.codex ? 'codex' : null,\n presence.opencode ? 'opencode' : null,\n presence.cursor ? 'cursor' : null,\n ].filter((x): x is SkillTarget => x !== null);\n\n // --- Scope (project vs global) — --global pins it, else ask / default project.\n if (!opts.global && interactive) {\n const scopeResult = await p.select({\n message: 'Install scope · 어디에 설치할까요?',\n options: [\n { value: 'project', label: 'Project', hint: `${relative(process.cwd(), projectRoot) || '.'} · 이 프로젝트만` },\n { value: 'global', label: 'Global', hint: '~/.claude · ~/.agents · ~/.config/opencode (hooks/settings 제외)' },\n ],\n initialValue: 'project',\n });\n if (p.isCancel(scopeResult)) { p.cancel('Install cancelled.'); return 130; }\n scope = scopeResult as 'project' | 'global';\n }\n\n // --- Skills — --skills pins it, else ask / default ALL.\n let skills: string[];\n if (opts.skillsFilter) {\n skills = allSkills.filter((s) => opts.skillsFilter!.includes(s));\n } else if (interactive) {\n const skillResult = await p.multiselect({\n message: 'Skills · space = 토글 · a = 전체 · enter = 확인 (default ALL)',\n options: allSkills.map((s) => ({ value: s, label: s, hint: 'omd skill' })),\n initialValues: allSkills,\n required: true,\n });\n if (p.isCancel(skillResult)) { p.cancel('Install cancelled.'); return 130; }\n skills = skillResult as string[];\n } else {\n skills = allSkills;\n }\n\n // --- Sub-agents — dropped by --skills-only, else --agents pins, else ask / ALL.\n let canonicalAgents: string[];\n if (minimal) {\n canonicalAgents = [];\n } else if (opts.agentsFilter) {\n canonicalAgents = allAgents.filter((a) => opts.agentsFilter!.includes(a.replace(/\\.md$/, '')));\n } else if (interactive && allAgents.length > 0) {\n const agentResult = await p.multiselect({\n message: 'Sub-agents · space = 토글 · a = 전체 · enter = 확인 (default ALL)',\n options: allAgents.map((a) => ({ value: a, label: a.replace(/\\.md$/, ''), hint: 'subagent' })),\n initialValues: allAgents,\n required: false,\n });\n if (p.isCancel(agentResult)) { p.cancel('Install cancelled.'); return 130; }\n canonicalAgents = agentResult as string[];\n } else {\n canonicalAgents = allAgents;\n }\n\n // --- Channels / targets — the \"where do I install\" choice.\n // --agent pins it. Otherwise, in a TTY we ASK — limited to the channels the\n // selected skills actually support (claude-design is claude-code only, so its\n // picker shows just Claude Code). Non-TTY / --all falls back to auto-resolution.\n const supportedTargets = ((): SkillTarget[] => {\n const set = new Set<SkillTarget>(skills.flatMap((s) => skillSupportedChannels(packageRoot, s)));\n // Cursor consumes no skills — its channel install (.cursor/rules shim +\n // shared .claude/data catalog) is skill-independent, so always offer it.\n set.add('cursor');\n return targetsAvailableForScope(\n (['claude-code', 'codex', 'opencode', 'cursor'] as SkillTarget[]).filter((t) => set.has(t)),\n scope,\n );\n })();\n const channelLabel: Record<SkillTarget, string> = {\n 'claude-code': 'Claude Code',\n codex: 'Codex',\n opencode: 'OpenCode',\n cursor: 'Cursor',\n };\n const channelDir: Record<SkillTarget, string> = {\n 'claude-code': '.claude',\n codex: '.codex',\n opencode: '.opencode',\n cursor: '.cursor',\n };\n let targets: SkillTarget[];\n if (opts.agents) {\n targets = opts.agents;\n } else if (interactive) {\n const defaults = actuallyDetected.filter((t) => supportedTargets.includes(t));\n const targetResult = await p.multiselect({\n message: 'Agent channels · 어디에 설치할까요? · space = 토글 · enter = 확인',\n options: supportedTargets.map((t) => ({\n value: t,\n label: channelLabel[t],\n hint: actuallyDetected.includes(t) ? `${channelDir[t]}/ detected` : '',\n })) as { value: SkillTarget; label: string; hint?: string }[],\n initialValues: defaults.length > 0 ? defaults : supportedTargets,\n required: true,\n });\n if (p.isCancel(targetResult)) { p.cancel('Install cancelled.'); return 130; }\n targets = targetResult as SkillTarget[];\n } else {\n // Non-interactive (CI / piped / --all): resolve from flags + detection,\n // narrowed to channels the selected skills support.\n targets = opts.all\n ? (['claude-code', 'codex', 'opencode'] as SkillTarget[])\n : minimal\n ? (actuallyDetected.length > 0 ? actuallyDetected : (['claude-code'] as SkillTarget[]))\n : detected;\n const narrowed = targets.filter((t) => supportedTargets.includes(t));\n if (narrowed.length > 0) targets = narrowed;\n }\n\n if (targets.length === 0) {\n console.error(pc.red('omd install-skills: no compatible agent channel selected'));\n return 1;\n }\n\n if (scope === 'global' && targets.includes('cursor')) {\n console.error(\n pc.red('omd install-skills: Cursor rules are project-scoped. Run the Cursor install from the project root without --global.'),\n );\n return 1;\n }\n if (\n opts.repairHooks &&\n (scope !== 'project' || minimal || !targets.includes('claude-code'))\n ) {\n console.error(\n pc.red('omd install-skills: --repair-hooks requires a project-scoped Claude Code target and cannot be combined with --skills-only.'),\n );\n return 1;\n }\n\n // Global scope roots everything at the home dir, so channel plans resolve to\n // ~/.claude, ~/.agents + ~/.codex, or ~/.config/opencode. Project scope uses\n // cwd (or --dir).\n const installRoot = scope === 'global' ? homedir() : projectRoot;\n // Cursor hosts no SKILL.md tree — it's excluded from skill plans and handled\n // below via the .cursor/rules shim + shared data copies (issue #20).\n const skillChannelTargets = targets.filter(\n (t): t is SkillChannel => t !== 'cursor'\n );\n const plans = skillChannelTargets.map((t) => planForTarget(installRoot, t, scope));\n\n p.log.message(\n pc.bold('Scope: ') +\n pc.cyan(scope) +\n pc.dim(scope === 'global' ? ' (channel user directories)' : ` (${relative(process.cwd(), projectRoot) || '.'})`)\n );\n p.log.message(\n pc.bold(`Skills (${skills.length}): `) +\n skills.map((s) => pc.cyan(s)).join(', ')\n );\n if (minimal) {\n // --skills-only: sub-agents are intentionally skipped (minimal single-skill\n // install). Clear BEFORE the summary so we never print agents we won't write.\n canonicalAgents = [];\n p.log.message(pc.bold('Agents: ') + pc.dim('skipped (--skills-only)'));\n } else if (canonicalAgents.length > 0) {\n p.log.message(\n pc.bold(`Agents (${canonicalAgents.length}): `) +\n canonicalAgents.map((a) => pc.cyan(a.replace(/\\.md$/, ''))).join(', ')\n );\n }\n p.log.message(\n pc.bold('Targets: ') + targets.map((t) => pc.cyan(t)).join(', ')\n );\n\n const results: InstallResult[] = [];\n // Count of reference-catalog DESIGN.md files copied (issue #16) — surfaced in\n // the install summary. Declared here so the outro (outside `if (!minimal)`) sees it.\n let catalogCount = 0;\n const catalogDestinations = new Set<string>();\n for (const plan of plans) {\n const dataDir = dataDirForScope(plan.target, targets, scope);\n const globalDataRoot = scope === 'global' && dataDir\n ? join(installRoot, dataDir, 'data')\n : null;\n for (const skill of skills) {\n results.push(installOne(packageRoot, installRoot, plan, skill, force, globalDataRoot));\n }\n }\n\n if (targets.includes('codex')) {\n results.push(...removeManagedLegacyCodexSkills(installRoot));\n }\n\n // Generate per-channel sub-agent definitions from the canonical `agents/`.\n // This is the v2 portable source-of-truth pattern (oh-my-agent style).\n // `canonicalAgents` is already resolved above by the TUI / --agents filter.\n for (const target of targets) {\n if (target === 'claude-code') {\n for (const filename of canonicalAgents) {\n results.push(installAgentFile(packageRoot, installRoot, 'claude', filename, force, scope));\n }\n } else if (target === 'codex') {\n for (const filename of canonicalAgents) {\n results.push(installAgentFile(packageRoot, installRoot, 'codex', filename, force, scope));\n }\n } else if (target === 'opencode') {\n for (const filename of canonicalAgents) {\n results.push(installAgentFile(packageRoot, installRoot, 'opencode', filename, force, scope));\n }\n }\n }\n\n if (!minimal) {\n // Cursor channel: write the `.cursor/rules` shim (the exact content omd:sync\n // renders for .cursor/rules/omd-design.mdc) so Cursor reads DESIGN.md before\n // UI work. No skills/agents/hooks — the shim plus the shared .claude/data\n // copies below are the whole Cursor install (issue #20).\n if (targets.includes('cursor')) {\n results.push(installCursorRule(installRoot, force));\n }\n\n // Ship the read-only data assets (reference fingerprints, controlled vocab,\n // human-readable tag matrix, opt-out corpus) so skills + hooks can run entirely\n // on the host CLI's own model — no external API keys.\n const dataFiles = [\n 'reference-fingerprints.json',\n 'reference-tags.md',\n 'vocabulary.json',\n 'synonyms.json',\n 'opt-out-corpus.json',\n ];\n // Channel→dir resolution (incl. the cursor shared-`.claude/data` dedup guard,\n // issue #20) lives in dataDirFor — single source for all three copy loops.\n for (const target of targets) {\n const dataDir = dataDirForScope(target, targets, scope);\n if (!dataDir) continue;\n for (const dataFile of dataFiles) {\n results.push(installDataFile(packageRoot, installRoot, dataDir, dataFile, force, target));\n }\n }\n\n // Ship the reference catalog (DESIGN.md per id) into .claude/data/references\n // so omd:init can resolve a reference on clean npx installs — no node_modules,\n // no dev web/references (issue #16). Skipped under --skills-only (handled by the\n // enclosing `if (!minimal)`). Codex gets the same copy under .codex/data.\n // Same dataDirFor single-path rule as the data JSONs above — Cursor reads\n // .claude/data/references, never a second catalog location.\n for (const target of targets) {\n const dataDir = dataDirForScope(target, targets, scope);\n if (dataDir) {\n const catalogRoot = join(installRoot, dataDir, 'data', 'references');\n if (unsafeManagedPath(installRoot, catalogRoot)) {\n results.push({\n target,\n skill: 'data:references',\n destPath: catalogRoot,\n status: 'skipped-drift',\n reason: 'unsafe-path',\n });\n continue;\n }\n const count = installReferenceCatalog(packageRoot, installRoot, dataDir, force);\n catalogCount = Math.max(catalogCount, count);\n if (count > 0) {\n catalogDestinations.add(`${dataDir}/data/references/<id>/DESIGN.md`);\n }\n }\n }\n\n // Copy ctx-prime.cjs (+ its companion context.cjs) into each selected skill\n // channel's scoped data tree so /omd-harness works after either a project or\n // global install. Cursor has no skill runtime and intentionally gets no helper.\n for (const target of targets) {\n if (target === 'cursor') continue;\n const cd = dataDirForScope(target, targets, scope);\n if (!cd) continue;\n for (const helper of ['ctx-prime.cjs', 'context.cjs']) {\n const srcHelper = join(packageRoot, 'scripts', helper);\n if (!existsSync(srcHelper)) continue;\n const destHelper = join(installRoot, cd, 'data', 'scripts', helper);\n if (unsafeManagedPath(installRoot, destHelper)) {\n results.push({\n target,\n skill: `data-script:${helper}`,\n destPath: destHelper,\n status: 'skipped-drift',\n reason: 'unsafe-path',\n });\n continue;\n }\n const srcTxt = readFileSync(srcHelper, 'utf8');\n if (existsSync(destHelper) && readFileSync(destHelper, 'utf8') === srcTxt) continue;\n mkdirSync(dirname(destHelper), { recursive: true });\n writeFileSync(destHelper, srcTxt, 'utf8');\n }\n }\n\n // Hooks + settings.json are PROJECT-SCOPED only — a global install must not\n // mutate the user's global Claude config / make hooks fire in every project.\n if (scope === 'project' && targets.includes('claude-code')) {\n for (const hookFile of CLAUDE_HOOK_PATHS) {\n results.push(\n installHookFile(\n packageRoot,\n installRoot,\n hookFile,\n force || opts.repairHooks === true,\n ),\n );\n }\n // settings.json (with merge, never clobber user)\n results.push(installSettingsJson(packageRoot, installRoot, force));\n }\n } // !minimal — skills-only skips data files, hooks, and settings.json\n\n p.log.message(pc.bold('\\nResults:'));\n for (const r of results) {\n const rel = relative(installRoot, r.destPath);\n p.log.message(\n ` ${STATUS_LABEL[r.status]} ${pc.dim(r.target.padEnd(12))} ${rel}`\n );\n }\n\n const driftCount = results.filter((r) => r.status === 'skipped-drift').length;\n const changedCount = results.filter(\n (r) => r.status === 'created' || r.status === 'updated' || r.status === 'removed'\n ).length;\n const currentCount = results.filter((r) => r.status === 'unchanged').length;\n\n if (driftCount > 0) {\n const unsafeCount = results.filter(\n (result) => result.status === 'skipped-drift' && result.reason === 'unsafe-path',\n ).length;\n const hookDrift = results.some(\n (result) =>\n result.status === 'skipped-drift' && result.skill.startsWith('hook:'),\n );\n const repairHint = unsafeCount > 0\n ? ` ${unsafeCount} unsafe symlinked managed path(s) were refused; replace them with project-local files and run doctor. --force will not bypass this safety check.`\n : hookDrift\n ? ' Use --repair-hooks for Claude hooks only, or --force to overwrite all unmarked files.'\n : ' Rerun with --force to overwrite.';\n p.outro(\n pc.yellow(\n `${changedCount} changed, ${currentCount} already current, ${driftCount} skipped (existing files lack a valid omd ownership marker).${repairHint}`\n )\n );\n return 2;\n }\n\n // Minimal single-skill install (--skills-only): no omd onboarding, no agents/hooks.\n // Ideal for shipping a standalone skill (e.g. claude-design) to people who don't\n // want the rest of the omd toolchain.\n if (minimal) {\n for (const r of results.filter((x) => x.status === 'skipped-incompat')) {\n p.log.warn(\n `${pc.bold(r.skill)} ${pc.dim('skipped for ')}${pc.cyan(r.target)}${pc.dim(' — declares x-omd-channels (channel not supported).')}`\n );\n }\n const installed = results.filter(\n (r) => r.status === 'created' || r.status === 'updated' || r.status === 'unchanged'\n );\n if (installed.length === 0) {\n p.outro(pc.yellow('Nothing installed — no compatible skill/channel match.'));\n return 0;\n }\n p.outro(\n pc.green(\n `Done. Installed ${skills.map((s) => pc.bold(s)).join(', ')} ${scope === 'global' ? `globally for ${targets.join(', ')}` : `for ${targets.join(', ')}`}.`\n ) +\n pc.dim(' → restart your agent, then use the skill (e.g. ') +\n pc.cyan('/claude-design') +\n pc.dim(').')\n );\n return 0;\n }\n\n // Friendly next-step nudge after successful install.\n // The first prompt is kept identical to the README's \"Your first 60 seconds\"\n // block so the README, the terminal, and the postinstall message all teach\n // the same activation moment. Bilingual (EN + KR) so an English reader is not\n // handed a Korean-only outro.\n const cursorOnly = targets.length === 1 && targets[0] === 'cursor';\n const nextSteps = cursorOnly\n ? [\n `${pc.bold('Restart Cursor, then establish or apply DESIGN.md:')}`,\n '',\n ` ${pc.cyan('EN')} ${pc.dim('Set up our design system — Toss-style, for a family meal-tracking app. Ask before writing DESIGN.md.')}`,\n ` ${pc.cyan('KR')} ${pc.dim('토스 스타일로 가족 식단 공유 앱 디자인 시스템을 제안하고 DESIGN.md 작성 전에 확인해줘')}`,\n '',\n `${pc.dim('Cursor uses the installed rule + local catalog directly. It does not receive OmD named skills or sub-agents.')}`,\n `${pc.dim('You can also choose a reference in the Builder and download DESIGN.md before asking Cursor to build.')}`,\n '',\n `${pc.yellow('⚠ Already-running session?')} ${pc.dim('Restart Cursor so it reloads the project rule.')}`,\n ].join('\\n')\n : [\n `${pc.bold('Restart your agent, then type your first prompt:')}`,\n '',\n ` ${pc.cyan('EN')} ${pc.dim('Set up our design system — Toss-style, for a family meal-tracking app.')}`,\n ` ${pc.cyan('KR')} ${pc.dim('토스 스타일로 가족 식단 공유 앱 디자인 시스템 잡아줘')}`,\n '',\n `${pc.dim('Your agent routes through omd:init and writes DESIGN.md after confirmation. Then build against it:')}`,\n ` ${pc.cyan('EN')} ${pc.dim('Design the home screen.')} ${pc.cyan('KR')} ${pc.dim('홈 화면 디자인해줘')}`,\n '',\n `${pc.dim('Full walkthrough → \"Your first 60 seconds\" in the README. Routing is automatic — no slash command needed.')}`,\n `${pc.dim('Power user: ')}${pc.cyan('/omd-harness <task>')}${pc.dim(' — jump straight into the pipeline.')}`,\n '',\n `${pc.yellow('⚠ Already-running session?')} ${pc.dim('Restart the coding agent after install. Codex must also trust the project before it loads project-local .codex/agents roles.')}`,\n ].join('\\n');\n p.note(nextSteps, 'Next');\n\n // Counts derived from what was actually resolved/installed — never hardcoded,\n // so the outro can't drift from the real skill/agent/hook set (or the README).\n const hookCount = scope === 'project' && targets.includes('claude-code') ? 4 : 0;\n if (catalogCount > 0) {\n p.log.message(\n pc.bold('Reference catalog: ') +\n pc.cyan(`${catalogCount}`) +\n pc.dim(` DESIGN.md synced → ${[...catalogDestinations].join(' + ')}`),\n );\n }\n const reportedSkillCount = skillChannelTargets.length > 0 ? skills.length : 0;\n const reportedAgentCount = skillChannelTargets.length > 0 ? canonicalAgents.length : 0;\n p.outro(\n pc.green(\n `Done. ${reportedSkillCount} skills · ${reportedAgentCount} sub-agents · ${hookCount} hooks · ${catalogCount} catalog refs ready (${changedCount} changed · ${currentCount} already current)${scope === 'global' ? ' globally (channel user directories)' : ''}.`,\n ),\n );\n return 0;\n}\n","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":"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","names":["existsSync","join","existsSync","join"]}
@@ -0,0 +1,157 @@
1
+ # oh-my-design CLI quickstart
2
+
3
+ oh-my-design gives your existing AI coding environment two things it can reuse across sessions:
4
+
5
+ - a local catalog of 440+ real-company `DESIGN.md` references; and
6
+ - skills and specialist roles for creating, applying, reviewing, and maintaining your own `DESIGN.md`.
7
+
8
+ The CLI is the installer and health check. It does not generate a UI by itself and it does not call a separate AI service. Claude Code, Codex, and OpenCode receive OmD skills and specialist roles. Cursor is intentionally narrower: it receives a project rule and catalog, not OmD skills or sub-agents.
9
+
10
+ ## Your first 60 seconds — Claude Code, Codex, or OpenCode
11
+
12
+ Run this from the root of the project you want to design:
13
+
14
+ ```bash
15
+ npx oh-my-design-cli@latest
16
+ ```
17
+
18
+ Choose the detected coding-agent channels and keep the default bundle selection. The installer writes only the capabilities each selected channel supports. When installation finishes:
19
+
20
+ 1. Restart your coding agent so it discovers the new skills and roles. In Codex, trust the project when prompted; untrusted projects intentionally ignore project-local `.codex/agents` roles.
21
+ 2. Confirm that the channel files and local reference catalog are complete:
22
+
23
+ ```bash
24
+ npx oh-my-design-cli@latest doctor
25
+ ```
26
+
27
+ 3. Give the skill-enabled agent this prompt:
28
+
29
+ ```text
30
+ Set up our design system — Toss-style, for a family meal-tracking app.
31
+ ```
32
+
33
+ The agent uses `omd:init` to recommend a reference, asks for confirmation, and writes `DESIGN.md` at the project root.
34
+
35
+ 4. Build with the system:
36
+
37
+ ```text
38
+ Design the home screen using our DESIGN.md, then audit it with omd:feel.
39
+ ```
40
+
41
+ Activation is complete when `DESIGN.md` exists and `doctor` reports the installed channel as ready.
42
+
43
+ ## Your first 60 seconds — Cursor
44
+
45
+ Install the Cursor project channel explicitly:
46
+
47
+ ```bash
48
+ npx oh-my-design-cli@latest install-skills --agent cursor --all
49
+ ```
50
+
51
+ Restart Cursor, then verify the project rule and 440-reference catalog:
52
+
53
+ ```bash
54
+ npx oh-my-design-cli@latest doctor
55
+ ```
56
+
57
+ Before a root `DESIGN.md` exists, `doctor` correctly reports that the bundle is installed but the project still needs activation.
58
+
59
+ Cursor does **not** receive `omd:init`, `omd:feel`, `/omd-harness`, or OmD sub-agents. Create the root `DESIGN.md` by either supported path:
60
+
61
+ 1. Open the [Builder](https://oh-my-design.kr/builder), choose and customize a reference, then download `DESIGN.md` into the project root; or
62
+ 2. use the installed catalog with an explicit prompt:
63
+
64
+ ```text
65
+ Read .claude/data/references/toss/DESIGN.md and create a root DESIGN.md
66
+ for this product using confirmed values only. Keep unknown facts absent.
67
+ ```
68
+
69
+ Then build with a normal Cursor prompt:
70
+
71
+ ```text
72
+ Read @DESIGN.md and redesign the home screen without changing behavior.
73
+ ```
74
+
75
+ The rule's minimal contract is deliberate: `DESIGN.md` has first priority, pending `.omd/preferences.md` corrections come next, and framework defaults come last. Do not substitute a plausible brand fact for an unresolved one.
76
+
77
+ ## What is installed
78
+
79
+ | Coding agent | Project-local files | Runtime behavior |
80
+ |---|---|---|
81
+ | Claude Code | `.claude/skills/`, `.claude/agents/`, `.claude/data/`, and managed hooks | Skills, specialist roles, local references, and natural-language activation |
82
+ | Codex | `.agents/skills/`, `.codex/agents/`, and `.codex/data/` | Skills, embedded specialist-role definitions, and local references |
83
+ | OpenCode | `.opencode/skills/`, `.opencode/agents/`, and `.opencode/data/` | Skills, native sub-agents, and an offline-capable local reference catalog |
84
+ | Cursor | `.cursor/rules/omd-design.mdc` and shared references in `.claude/data/` | A project rule that makes Cursor read `DESIGN.md`; no OmD skill, sub-agent, or hook channel is installed |
85
+
86
+ The interactive installer detects available channels. To select one explicitly:
87
+
88
+ ```bash
89
+ npx oh-my-design-cli@latest install-skills --agent codex
90
+ ```
91
+
92
+ For a user-level installation available across projects:
93
+
94
+ ```bash
95
+ npx oh-my-design-cli@latest install-skills --global
96
+ ```
97
+
98
+ Global paths stay native to each channel: Claude Code uses `~/.claude/`; Codex uses `~/.agents/skills/` with roles and data under `~/.codex/`; OpenCode uses `~/.config/opencode/{skills,agents,data}/`. Cursor rules are project-scoped and deliberately reject `--global`. Verify a global installation separately:
99
+
100
+ ```bash
101
+ npx oh-my-design-cli@latest doctor --global
102
+ ```
103
+
104
+ Global installation does not modify global hooks or settings. Each project still needs its own `DESIGN.md`.
105
+
106
+ ## Work inside your agent
107
+
108
+ Once installed, prompts—not repeated CLI commands—are the main interface. Named OmD skills in the table below require Claude Code, Codex, or OpenCode.
109
+
110
+ | Goal | Available in | Example prompt |
111
+ |---|---|---|
112
+ | Create the project system | Claude Code / Codex / OpenCode | `Set up our design system — Linear-style, for a B2B operations dashboard.` |
113
+ | Create the project system | Cursor | Download from the Builder, or use the explicit local-catalog prompt above |
114
+ | Apply it to existing UI | All channels, once root `DESIGN.md` exists | `Redesign the billing page using our DESIGN.md. Preserve all behavior.` |
115
+ | Run the full design pipeline | Claude Code / Codex / OpenCode | `/omd-harness Checkout completion screen — success, failure, and partial-success states` |
116
+ | Review interface quality | Claude Code / Codex / OpenCode | `Audit this screen with omd:feel and fix the high-confidence issues.` |
117
+ | Keep a correction | Claude Code / Codex / OpenCode | `Remember this preference: cards should use borders, not decorative shadows.` |
118
+ | Capture a new reference | Claude Code / Codex / OpenCode | `Capture a design reference from this first-party product URL and keep unresolved facts absent.` |
119
+
120
+ The full harness includes explicit user checkpoints. It does not silently approve information architecture, design-system changes, or final validation.
121
+
122
+ ## Diagnose and recover
123
+
124
+ Run the doctor whenever a skill, role, or reference does not appear:
125
+
126
+ ```bash
127
+ npx oh-my-design-cli@latest doctor
128
+ ```
129
+
130
+ It checks the real files for each installed channel, the reference count and fingerprint, required activation files, and the project `DESIGN.md`. For automation, use:
131
+
132
+ ```bash
133
+ npx oh-my-design-cli@latest doctor --json
134
+ ```
135
+
136
+ Common recovery paths:
137
+
138
+ - **No installation found:** run `npx oh-my-design-cli@latest` from the intended project root.
139
+ - **Installation is incomplete:** run the scoped repair command printed by `doctor`, then run `doctor` again.
140
+ - **The files exist but the agent cannot see them:** fully restart the agent. Claude Code, Codex, and OpenCode discover skills/roles at session start; Cursor must reload the project rule. In Codex, also confirm that the project is trusted.
141
+ - **The bundle is healthy but no `DESIGN.md` exists:** in Claude Code, Codex, or OpenCode, ask the agent to set up the design system. In Cursor, use the Builder download or explicit local-catalog prompt above. This is an activation step, not an installation failure.
142
+ - **A managed upgrade skipped a file:** oh-my-design preserves files without its managed marker. Review the local edits before deciding whether to rerun with `--force`.
143
+ - **A managed Claude hook is stale:** use the `--repair-hooks` command printed by `doctor`; it refreshes the managed hook bundle without overwriting other unmarked files.
144
+ - **You checked the wrong folder:** pass `--dir <project-root>` to both `install-skills` and `doctor`.
145
+
146
+ ## Next steps
147
+
148
+ - [English documentation](https://oh-my-design.kr/docs/en)
149
+ - [한국어 문서](https://oh-my-design.kr/docs/ko)
150
+ - [日本語ドキュメント](https://oh-my-design.kr/docs/ja)
151
+ - [简体中文文档](https://oh-my-design.kr/docs/zh-cn)
152
+ - [繁體中文(台灣)文件](https://oh-my-design.kr/docs/zh-tw)
153
+ - [Reference catalog](https://oh-my-design.kr/design-systems)
154
+ - [Migration guide for 0.1.x](../MIGRATION.md)
155
+ - [GitHub repository](https://github.com/kwakseongjae/oh-my-design)
156
+
157
+ For command flags, run `npx oh-my-design-cli@latest --help` or `npx oh-my-design-cli@latest doctor --help`.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "oh-my-design-cli",
3
- "version": "1.8.6",
4
- "description": "Bootstrap oh-my-design skills + agents into your project. After install, talk to your AI coding agent in natural language — no other CLI commands.",
3
+ "version": "1.9.0",
4
+ "description": "Give your AI coding agent a project-owned DESIGN.md workflow with a guided installer, doctor, and 440+ quality-graded references.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "oh-my-design": "dist/bin/oh-my-design.js",
@@ -22,9 +22,11 @@
22
22
  "skills/omd-orchestrator",
23
23
  "skills/omd-kr-writer",
24
24
  "skills/omd-locale-adapter",
25
+ "skills/omd-humanize",
25
26
  "skills/omd-designer-review",
26
27
  "skills/omd-final-qa",
27
28
  "skills/omd-feel",
29
+ "skills/omd-slop-audit",
28
30
  "skills/omd-codex-image",
29
31
  "skills/claude-design",
30
32
  "agents",
@@ -38,6 +40,10 @@
38
40
  ".claude/hooks/lib/*.cjs",
39
41
  ".claude/settings.json",
40
42
  "AGENTS.md",
43
+ "CHANGELOG.md",
44
+ "MIGRATION.md",
45
+ "docs/CLI_QUICKSTART.md",
46
+ ".github/assets/logo-bg.png",
41
47
  "scripts/postinstall.cjs",
42
48
  "scripts/context.cjs",
43
49
  "scripts/ctx-prime.cjs"
@@ -53,6 +59,7 @@
53
59
  "prepublishOnly": "npm run build && node scripts/gen-llms-full.cjs && bash scripts/check-release-hygiene.sh pack",
54
60
  "gen:llms-full": "node scripts/gen-llms-full.cjs",
55
61
  "check-counts": "node scripts/check-counts.mjs",
62
+ "check:catalog": "npm --prefix web run check:reference-pipeline",
56
63
  "postinstall": "node scripts/postinstall.cjs"
57
64
  },
58
65
  "keywords": [
@@ -61,14 +68,17 @@
61
68
  "ai-agent",
62
69
  "claude-code",
63
70
  "codex",
71
+ "opencode",
72
+ "cursor",
64
73
  "skills",
74
+ "design-skills",
65
75
  "google-stitch",
66
76
  "vibe-coding"
67
77
  ],
68
78
  "license": "MIT",
69
79
  "repository": {
70
80
  "type": "git",
71
- "url": "https://github.com/kwakseongjae/oh-my-design"
81
+ "url": "git+https://github.com/kwakseongjae/oh-my-design.git"
72
82
  },
73
83
  "engines": {
74
84
  "node": ">=18"
@@ -80,8 +90,19 @@
80
90
  },
81
91
  "devDependencies": {
82
92
  "@types/node": "^22.13.10",
93
+ "playwright-core": "^1.61.1",
83
94
  "tsup": "^8.4.0",
84
95
  "typescript": "^5.8.2",
85
96
  "vitest": "^3.1.1"
86
- }
97
+ },
98
+ "main": "index.js",
99
+ "directories": {
100
+ "doc": "docs",
101
+ "test": "test"
102
+ },
103
+ "author": "",
104
+ "bugs": {
105
+ "url": "https://github.com/kwakseongjae/oh-my-design/issues"
106
+ },
107
+ "homepage": "https://oh-my-design.kr/docs/en"
87
108
  }
@@ -29,26 +29,27 @@ const c = {
29
29
 
30
30
  const lines = [
31
31
  '',
32
- `${c.green('✓')} ${c.bold('oh-my-design installed.')} ${c.dim('(commands: omd, oh-my-design)')}`,
32
+ `${c.green('✓')} ${c.bold('oh-my-design package installed.')}`,
33
33
  '',
34
34
  ` ${c.bold('Next')} in any project:`,
35
35
  '',
36
36
  ` ${c.cyan('cd')} ${c.dim('<your project>')}`,
37
- ` ${c.cyan('omd install-skills')} ${c.dim('# one time per project — installs the design harness')}`,
37
+ ` ${c.cyan('npx oh-my-design-cli@latest')} ${c.dim('# one time per project — opens the guided installer')}`,
38
38
  '',
39
39
  ` ${c.bold('Then restart your agent and type your first prompt:')}`,
40
40
  '',
41
41
  ` ${c.cyan('EN')} ${c.dim('Set up our design system — Toss-style, for a family meal-tracking app.')}`,
42
42
  ` ${c.cyan('KR')} ${c.dim('토스 스타일로 가족 식단 공유 앱 디자인 시스템 잡아줘')}`,
43
43
  '',
44
- ` ${c.dim('Your agent runs omd:init and writes DESIGN.md. Then: "Design the home screen."')}`,
44
+ ` ${c.dim('Claude Code, Codex, and OpenCode route through omd:init; Cursor uses its installed rule + catalog contract.')}`,
45
+ ` ${c.dim('After DESIGN.md is confirmed: "Design the home screen."')}`,
45
46
  ` ${c.dim('Full walkthrough → "Your first 60 seconds" in the README. Routing is automatic.')}`,
46
47
  '',
47
48
  ` ${c.dim('Power user shortcut: ')} ${c.cyan('/omd-harness <task>')} ${c.dim('— jump straight into the pipeline.')}`,
49
+ ` ${c.dim('Something not loading? ')} ${c.cyan('npx oh-my-design-cli@latest doctor')} ${c.dim('— checks the install and prints the exact next action.')}`,
48
50
  '',
49
51
  ` ${c.bold('⚠ Already running Claude Code?')} ${c.dim('Quit (Cmd+Q on macOS) and relaunch — subagents only load at session start.')}`,
50
52
  '',
51
53
  ];
52
54
 
53
55
  console.log(lines.join('\n'));
54
-