oh-my-design-cli 0.1.2 → 1.0.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 (74) hide show
  1. package/.claude/hooks/post-edit-watch.cjs +99 -0
  2. package/.claude/hooks/session-end-foldin.cjs +96 -0
  3. package/.claude/hooks/session-state-loader.cjs +64 -0
  4. package/.claude/hooks/skill-activation.cjs +73 -0
  5. package/.claude/settings.json +55 -0
  6. package/.claude/skills/skill-rules.json +87 -0
  7. package/AGENTS.md +111 -0
  8. package/README.md +75 -202
  9. package/agents/AGENT.md +53 -0
  10. package/agents/omd-3d-blender.md +269 -0
  11. package/agents/omd-a11y-auditor.md +97 -0
  12. package/agents/omd-asset-curator.md +260 -0
  13. package/agents/omd-critic.md +181 -0
  14. package/agents/omd-master.md +548 -0
  15. package/agents/omd-microcopy.md +63 -0
  16. package/agents/omd-persona-tester.md +118 -0
  17. package/agents/omd-ui-junior.md +129 -0
  18. package/agents/omd-ux-engineer.md +265 -0
  19. package/agents/omd-ux-researcher.md +62 -0
  20. package/agents/omd-ux-writer.md +181 -0
  21. package/data/opt-out-corpus.json +141 -0
  22. package/data/reference-fingerprints.json +1495 -0
  23. package/dist/bin/oh-my-design.js +3 -818
  24. package/dist/bin/oh-my-design.js.map +1 -1
  25. package/dist/install-skills-SVIYKXOE.js +442 -0
  26. package/dist/install-skills-SVIYKXOE.js.map +1 -0
  27. package/package.json +23 -23
  28. package/scripts/context.cjs +91 -0
  29. package/scripts/postinstall.cjs +54 -0
  30. package/skills/omd-apply/SKILL.md +64 -53
  31. package/skills/omd-harness/SKILL.md +271 -0
  32. package/skills/omd-learn/SKILL.md +55 -35
  33. package/skills/omd-remember/SKILL.md +93 -15
  34. package/skills/omd-sync/SKILL.md +140 -16
  35. package/dist/chunk-6YNSV3VY.js +0 -35
  36. package/dist/chunk-6YNSV3VY.js.map +0 -1
  37. package/dist/chunk-MHFYGZSO.js +0 -337
  38. package/dist/chunk-MHFYGZSO.js.map +0 -1
  39. package/dist/chunk-N2JG6N4Q.js +0 -264
  40. package/dist/chunk-N2JG6N4Q.js.map +0 -1
  41. package/dist/chunk-OOQQEUGX.js +0 -46
  42. package/dist/chunk-OOQQEUGX.js.map +0 -1
  43. package/dist/chunk-OR5DHENY.js +0 -250
  44. package/dist/chunk-OR5DHENY.js.map +0 -1
  45. package/dist/customizer-CM76752R.js +0 -8
  46. package/dist/customizer-CM76752R.js.map +0 -1
  47. package/dist/index.d.ts +0 -559
  48. package/dist/index.js +0 -3113
  49. package/dist/index.js.map +0 -1
  50. package/dist/init-UMM4XIV5.js +0 -675
  51. package/dist/init-UMM4XIV5.js.map +0 -1
  52. package/dist/install-skills-CM6VXFZJ.js +0 -152
  53. package/dist/install-skills-CM6VXFZJ.js.map +0 -1
  54. package/dist/learn-33LHKEJA.js +0 -140
  55. package/dist/learn-33LHKEJA.js.map +0 -1
  56. package/dist/reference-YMNAOXJQ.js +0 -47
  57. package/dist/reference-YMNAOXJQ.js.map +0 -1
  58. package/dist/reference-parser-TM3CJPNE.js +0 -10
  59. package/dist/reference-parser-TM3CJPNE.js.map +0 -1
  60. package/dist/remember-UAFA5B2O.js +0 -78
  61. package/dist/remember-UAFA5B2O.js.map +0 -1
  62. package/dist/sync-FDYRKNFE.js +0 -417
  63. package/dist/sync-FDYRKNFE.js.map +0 -1
  64. package/dist/templates/templates/design-md.hbs +0 -44
  65. package/dist/templates/templates/partials/agent-prompt-guide.hbs +0 -28
  66. package/dist/templates/templates/partials/color-palette.hbs +0 -49
  67. package/dist/templates/templates/partials/component-stylings.hbs +0 -28
  68. package/dist/templates/templates/partials/depth-elevation.hbs +0 -31
  69. package/dist/templates/templates/partials/dos-donts.hbs +0 -13
  70. package/dist/templates/templates/partials/layout.hbs +0 -30
  71. package/dist/templates/templates/partials/responsive.hbs +0 -25
  72. package/dist/templates/templates/partials/shadcn-tokens.hbs +0 -64
  73. package/dist/templates/templates/partials/typography.hbs +0 -43
  74. package/dist/templates/templates/partials/visual-theme.hbs +0 -26
@@ -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} from 'node:fs';\nimport { join, dirname, relative } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { detectInstalledAgents } from '../core/agent-detect.js';\n\nexport type SkillTarget = 'claude-code' | 'codex' | 'opencode';\n\nexport interface InstallSkillsOptions {\n dir?: string;\n agents?: SkillTarget[];\n force?: boolean;\n}\n\ninterface InstallPlan {\n target: SkillTarget;\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((name) => existsSync(join(skillsDir, name, 'SKILL.md')))\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 return m ? m[1].trim().replace(/^[\"']|[\"']$/g, '') : '';\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/** Map Claude tool names to Codex tool names (best-effort). */\nfunction claudeToolsToCodex(tools: string[]): string[] {\n const m: Record<string, string> = {\n Read: 'read_file',\n Write: 'write_file',\n Edit: 'edit_file',\n Bash: 'shell',\n Glob: 'search',\n Grep: 'search',\n WebFetch: 'web_fetch',\n WebSearch: 'search',\n Agent: 'spawn_agent',\n TaskCreate: 'task',\n TaskUpdate: 'task',\n TaskList: 'task',\n };\n const out = new Set<string>();\n for (const t of tools) out.add(m[t] ?? t.toLowerCase());\n return [...out].sort();\n}\n\n/** Map Claude model alias to Codex/OpenAI model id (best-effort). */\nfunction claudeModelToCodex(model: string): string {\n const m: Record<string, string> = {\n haiku: 'gpt-4.1-mini',\n sonnet: 'gpt-4.1',\n opus: 'gpt-4.1',\n };\n return m[model.toLowerCase()] ?? 'gpt-4.1';\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: ${a.name}`,\n `description: ${a.description}`,\n `tools: ${a.tools.join(', ')}`,\n `model: ${a.model}`,\n `omd_managed: true`,\n '---',\n '',\n ].join('\\n');\n return fm + a.body;\n}\n\n/** Render a canonical agent as a Codex TOML file (declarative pointer). */\nfunction renderCodexAgent(a: ParsedAgent): string {\n const tools = claudeToolsToCodex(a.tools);\n const model = claudeModelToCodex(a.model);\n const desc = a.description.replace(/\"/g, '\\\\\"');\n return [\n `[agent]`,\n `name = \"${a.name}\"`,\n `description = \"${desc}\"`,\n `model = \"${model}\"`,\n `max_threads = 1`,\n `allowed_tools = [${tools.map((t) => `\"${t}\"`).join(', ')}]`,\n '',\n `instructions = \"\"\"`,\n `Source of truth: agents/${a.name}.md (canonical). The full role spec is`,\n `mirrored to .claude/agents/${a.name}.md when installed for Claude Code.`,\n `Follow that spec verbatim regardless of channel.`,\n '',\n `Codex notes:`,\n `- Spawn sub-agents via spawn_agent with names matching .codex/agents/<name>.toml`,\n `- Use shell to invoke CLI helpers (omd init prepare, omd remember, git apply, npx axe-core, npx lighthouse)`,\n `- All artifacts go inside .omd/runs/run-<latest>/ (or skills/omd-lab-02-design-harness/runs/<lab-version>-...)`,\n `\"\"\"`,\n '',\n ].join('\\n');\n}\n\nfunction planForTarget(projectRoot: string, target: SkillTarget): 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 return {\n target,\n destDir: join(projectRoot, '.codex', 'skills'),\n layout: 'folder',\n };\n case 'opencode':\n return {\n target,\n destDir: join(projectRoot, '.opencode', 'agents'),\n layout: 'flat',\n };\n }\n}\n\nconst MANAGED_HEADER =\n '<!-- omd:installed-skill — managed by `omd install-skills`. Do not edit; rerun the command to refresh. -->';\n\ninterface InstallResult {\n target: SkillTarget;\n skill: string;\n destPath: string;\n status: 'created' | 'updated' | 'unchanged' | 'skipped-drift';\n}\n\nfunction installOne(\n packageRoot: string,\n plan: InstallPlan,\n skill: string,\n force: boolean\n): InstallResult {\n const src = readFileSync(\n join(packageRoot, 'skills', skill, 'SKILL.md'),\n 'utf8'\n );\n const managed = MANAGED_HEADER + '\\n\\n' + src;\n const destPath =\n plan.layout === 'folder'\n ? join(plan.destDir, skill, 'SKILL.md')\n : join(plan.destDir, skill + '.md');\n\n const exists = existsSync(destPath);\n const existing = exists ? readFileSync(destPath, 'utf8') : '';\n\n if (exists && existing === managed) {\n return { target: plan.target, skill, destPath, status: 'unchanged' };\n }\n\n if (exists && !existing.startsWith(MANAGED_HEADER) && !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 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: string,\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 const src = readFileSync(srcPath, 'utf8');\n const exists = existsSync(destPath);\n const existing = exists ? readFileSync(destPath, 'utf8') : '';\n if (exists && existing === src) {\n return { target, skill: skillLabel, destPath, status: 'unchanged' };\n }\n if (exists && !force) {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n mkdirSync(dirname(destPath), { recursive: true });\n writeFileSync(destPath, src);\n return { target, skill: skillLabel, destPath, status: exists ? 'updated' : 'created' };\n}\n\n/** Install skill-rules.json from package's .claude/skills/. */\nfunction installSkillRules(\n packageRoot: string,\n projectRoot: string,\n force: boolean\n): InstallResult {\n const target: SkillTarget = 'claude-code';\n const skillLabel = 'rules:skill-rules.json';\n const srcPath = join(packageRoot, '.claude', 'skills', 'skill-rules.json');\n const destPath = join(projectRoot, '.claude', 'skills', 'skill-rules.json');\n if (!existsSync(srcPath)) {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n const src = readFileSync(srcPath, 'utf8');\n const exists = existsSync(destPath);\n const existing = exists ? readFileSync(destPath, 'utf8') : '';\n if (exists && existing === src) {\n return { target, skill: skillLabel, destPath, status: 'unchanged' };\n }\n if (exists && !force) {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n mkdirSync(dirname(destPath), { recursive: true });\n writeFileSync(destPath, src);\n return { target, skill: skillLabel, destPath, status: exists ? 'updated' : 'created' };\n}\n\n/**\n * Install / merge .claude/settings.json. We MERGE hooks (don't clobber user\n * customizations) by checking if the omd-managed `_doc` field is present.\n * Without --force, if a user-edited settings.json exists (no _doc field),\n * we skip with `skipped-drift`.\n */\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 const src = readFileSync(srcPath, 'utf8');\n const exists = existsSync(destPath);\n const existing = exists ? readFileSync(destPath, 'utf8') : '';\n if (exists && existing === src) {\n return { target, skill: skillLabel, destPath, status: 'unchanged' };\n }\n if (exists && !force) {\n // Check if it's the omd-managed version\n try {\n const parsed = JSON.parse(existing);\n if (typeof parsed._doc === 'string' && parsed._doc.includes('OmD')) {\n // managed — overwrite\n } else {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n } catch {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n }\n mkdirSync(dirname(destPath), { recursive: true });\n writeFileSync(destPath, src);\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): InstallResult {\n const target: SkillTarget = channelDir === '.claude' ? 'claude-code' : 'codex';\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\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 if (exists && !force) {\n // Honor user customization unless --force\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n\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',\n filename: string,\n force: boolean\n): InstallResult {\n const target: SkillTarget = channel === 'claude' ? 'claude-code' : 'codex';\n const skillLabel = `agent:${filename}`;\n\n const parsed = parseCanonicalAgent(packageRoot, filename);\n const rendered =\n channel === 'claude' ? renderClaudeAgent(parsed) : renderCodexAgent(parsed);\n\n const destFilename =\n channel === 'claude' ? filename : filename.replace(/\\.md$/, '.toml');\n const destPath = join(\n projectRoot,\n channel === 'claude' ? '.claude' : '.codex',\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 =\n channel === 'claude'\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 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 =\n channel === 'claude'\n ? /\\nomd_managed:\\s*true\\b/.test(existing)\n : existing.startsWith('# 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\nconst STATUS_LABEL: Record<InstallResult['status'], string> = {\n created: pc.green('created'),\n updated: pc.cyan('updated'),\n unchanged: pc.dim('unchanged'),\n 'skipped-drift': pc.yellow('skipped'),\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 uses .mdc rules, not skills — installed via `omd sync`.\n if (targets.length === 0) {\n // Fallback: install for all three so user gets coverage even without\n // explicit signal. Idempotent so cost is low.\n return ['claude-code', 'codex', 'opencode'];\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 skills = listShippedSkills(packageRoot);\n if (skills.length === 0) {\n console.error(pc.red('omd install-skills: no skills found in package'));\n return 1;\n }\n\n const targets = opts.agents ?? autoDetectTargets(projectRoot);\n const plans = targets.map((t) => planForTarget(projectRoot, t));\n const force = opts.force ?? false;\n\n p.intro(\n pc.bold('omd install-skills') +\n pc.dim(` (${relative(process.cwd(), projectRoot) || '.'})`)\n );\n\n p.log.message(\n pc.bold('Targets: ') +\n targets.map((t) => pc.cyan(t)).join(', ')\n );\n p.log.message(\n pc.bold('Skills: ') + skills.map((s) => pc.cyan(s)).join(', ')\n );\n\n const results: InstallResult[] = [];\n for (const plan of plans) {\n for (const skill of skills) {\n results.push(installOne(packageRoot, plan, skill, force));\n }\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 const canonicalAgents = listCanonicalAgents(packageRoot);\n for (const target of targets) {\n if (target === 'claude-code') {\n for (const filename of canonicalAgents) {\n results.push(installAgentFile(packageRoot, projectRoot, 'claude', filename, force));\n }\n } else if (target === 'codex') {\n for (const filename of canonicalAgents) {\n results.push(installAgentFile(packageRoot, projectRoot, 'codex', filename, force));\n }\n }\n // OpenCode currently has no agent-definition channel — skills only.\n }\n\n // Ship the read-only data assets (reference fingerprints, controlled vocab,\n // human-readable tag matrix, opt-out corpus) into the project so skills + hooks\n // can run entirely 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 for (const target of targets) {\n if (target === 'claude-code') {\n for (const dataFile of dataFiles) {\n results.push(installDataFile(packageRoot, projectRoot, '.claude', dataFile, force));\n }\n } else if (target === 'codex') {\n for (const dataFile of dataFiles) {\n results.push(installDataFile(packageRoot, projectRoot, '.codex', dataFile, force));\n }\n }\n }\n\n // Install hooks (Claude Code only — Codex / OpenCode have separate hook systems)\n if (targets.includes('claude-code')) {\n for (const hookFile of [\n 'skill-activation.cjs',\n 'session-state-loader.cjs',\n 'post-edit-watch.cjs',\n 'session-end-foldin.cjs',\n ]) {\n results.push(installHookFile(packageRoot, projectRoot, hookFile, force));\n }\n // skill-rules.json\n results.push(installSkillRules(packageRoot, projectRoot, force));\n // settings.json (with merge, never clobber user)\n results.push(installSettingsJson(packageRoot, projectRoot, force));\n }\n\n p.log.message(pc.bold('\\nResults:'));\n for (const r of results) {\n const rel = relative(projectRoot, 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 writtenCount = results.filter(\n (r) => r.status === 'created' || r.status === 'updated'\n ).length;\n\n if (driftCount > 0) {\n p.outro(\n pc.yellow(\n `${writtenCount} written, ${driftCount} skipped (existing files lack the omd marker — rerun with --force to overwrite).`\n )\n );\n return 0;\n }\n\n // Friendly next-step nudge after successful install\n const nextSteps = [\n `${pc.bold('Open Claude Code (or Codex). Just say what you want:')}`,\n '',\n ` ${pc.dim('\"토스 스타일 가족 식단 공유 앱 메인 화면 디자인해줘\"')}`,\n ` ${pc.dim('\"Linear-clone B2B SaaS dashboard 만들고 싶어\"')}`,\n ` ${pc.dim('\"이 카드 좀 더 세련되게\"')} ${pc.dim('# 작업 중 자연어 — 자동 hook 라우팅')}`,\n '',\n `${pc.bold('Hook이 자동으로 라우팅')} ${pc.dim('— 디자인 의도 감지해서 하네스/스킬 호출. 슬래시 명령 안 쳐도 됨.')}`,\n '',\n `${pc.dim('Power user shortcut: ')}${pc.cyan('/omd-harness <task>')} ${pc.dim('— hook 우회, 즉시 진입.')}`,\n '',\n `${pc.yellow('⚠ Already-running Claude Code session?')} ${pc.dim('Run `/agents` inside the session to reload — or quit (Cmd+Q on macOS) and relaunch. Without reload, hooks/agents do not load.')}`,\n ].join('\\n');\n p.note(nextSteps, 'Next');\n\n p.outro(\n pc.green(\n `Done. 6 skills · 11 sub-agents · 4 hooks installed (${writtenCount} files).`,\n ),\n );\n return 0;\n}\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"]}
package/package.json CHANGED
@@ -1,45 +1,49 @@
1
1
  {
2
2
  "name": "oh-my-design-cli",
3
- "version": "0.1.2",
4
- "description": "Interactive CLI to generate DESIGN.md files for AI coding agents",
3
+ "version": "1.0.0",
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.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "oh-my-design": "dist/bin/oh-my-design.js",
8
8
  "omd": "dist/bin/oh-my-design.js"
9
9
  },
10
- "main": "dist/index.js",
11
- "types": "dist/index.d.ts",
12
- "exports": {
13
- ".": {
14
- "import": "./dist/index.js",
15
- "types": "./dist/index.d.ts"
16
- }
17
- },
18
10
  "files": [
19
11
  "dist",
20
- "templates",
21
- "skills",
12
+ "skills/omd-apply",
13
+ "skills/omd-init",
14
+ "skills/omd-harness",
15
+ "skills/omd-learn",
16
+ "skills/omd-remember",
17
+ "skills/omd-sync",
18
+ "agents",
22
19
  "data",
23
- "references/**/DESIGN.md"
20
+ "references/**/DESIGN.md",
21
+ ".claude/hooks/*.cjs",
22
+ ".claude/skills/skill-rules.json",
23
+ ".claude/settings.json",
24
+ "AGENTS.md",
25
+ "scripts/postinstall.cjs",
26
+ "scripts/context.cjs"
24
27
  ],
25
28
  "scripts": {
26
- "build": "tsup && cp -r src/templates dist/templates",
29
+ "build": "tsup",
27
30
  "dev": "tsup --watch",
28
31
  "start": "node dist/bin/oh-my-design.js",
29
32
  "test": "vitest run",
30
33
  "test:watch": "vitest",
31
34
  "lint": "tsc --noEmit",
32
35
  "prepare": "npm run build",
33
- "prepublishOnly": "npm run build"
36
+ "prepublishOnly": "npm run build",
37
+ "postinstall": "node scripts/postinstall.cjs"
34
38
  },
35
39
  "keywords": [
36
40
  "design-system",
37
41
  "design-md",
38
- "shadcn",
39
- "cli",
40
42
  "ai-agent",
43
+ "claude-code",
44
+ "codex",
45
+ "skills",
41
46
  "google-stitch",
42
- "design-tokens",
43
47
  "vibe-coding"
44
48
  ],
45
49
  "license": "MIT",
@@ -53,11 +57,7 @@
53
57
  "dependencies": {
54
58
  "@clack/prompts": "^0.9.1",
55
59
  "commander": "^13.1.0",
56
- "handlebars": "^4.7.8",
57
- "picocolors": "^1.1.1",
58
- "shadcn": "^4.2.0",
59
- "tw-animate-css": "^1.4.0",
60
- "zod": "^3.24.2"
60
+ "picocolors": "^1.1.1"
61
61
  },
62
62
  "devDependencies": {
63
63
  "@types/node": "^22.13.10",
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env node
2
+ // scripts/context.cjs — minimal, deterministic project scan invoked by the
3
+ // omd-harness skill / omd-master agent when they want a fast .omd/context.json
4
+ // without doing a full Glob+Read pass in prose.
5
+ //
6
+ // Output: writes .omd/context.json with { kind, framework, css_files,
7
+ // package_name, deps_summary, ts }, and prints the path to stdout.
8
+ //
9
+ // Pure node — no deps. ~80 lines. Optional helper, not load-bearing: master
10
+ // can also just Glob+Read inline, but this preserves determinism for repeat
11
+ // scans across runs.
12
+
13
+ const fs = require('node:fs');
14
+ const path = require('node:path');
15
+
16
+ const cwd = process.argv[2] || process.cwd();
17
+ const out = path.join(cwd, '.omd', 'context.json');
18
+
19
+ function safeRead(p) {
20
+ try {
21
+ return fs.readFileSync(p, 'utf8');
22
+ } catch {
23
+ return null;
24
+ }
25
+ }
26
+
27
+ function listFiles(dir, exts, max = 50) {
28
+ const found = [];
29
+ function walk(d, depth) {
30
+ if (depth > 4 || found.length >= max) return;
31
+ let entries;
32
+ try {
33
+ entries = fs.readdirSync(d, { withFileTypes: true });
34
+ } catch {
35
+ return;
36
+ }
37
+ for (const e of entries) {
38
+ if (e.name.startsWith('.') || e.name === 'node_modules' || e.name === 'dist') continue;
39
+ const full = path.join(d, e.name);
40
+ if (e.isDirectory()) walk(full, depth + 1);
41
+ else if (exts.some((ext) => e.name.endsWith(ext))) {
42
+ found.push(path.relative(cwd, full));
43
+ if (found.length >= max) return;
44
+ }
45
+ }
46
+ }
47
+ walk(dir, 0);
48
+ return found;
49
+ }
50
+
51
+ const pkgRaw = safeRead(path.join(cwd, 'package.json'));
52
+ const pkg = pkgRaw ? JSON.parse(pkgRaw) : null;
53
+ const deps = pkg ? Object.assign({}, pkg.dependencies, pkg.devDependencies) : {};
54
+
55
+ let framework = 'unknown';
56
+ if (deps['next']) framework = 'next';
57
+ else if (deps['vite']) framework = 'vite';
58
+ else if (deps['react']) framework = 'react';
59
+ else if (deps['vue']) framework = 'vue';
60
+ else if (deps['svelte']) framework = 'svelte';
61
+ else if (deps['solid-js']) framework = 'solid';
62
+
63
+ let kind = 'unknown';
64
+ if (pkg) kind = 'node';
65
+ if (fs.existsSync(path.join(cwd, 'index.html')) && !pkg) kind = 'static-html';
66
+
67
+ const cssFiles = listFiles(cwd, ['.css', '.scss', '.sass'], 20);
68
+ const componentFiles = listFiles(cwd, ['.tsx', '.jsx', '.vue', '.svelte'], 30);
69
+
70
+ const ctx = {
71
+ kind,
72
+ framework,
73
+ package_name: pkg?.name ?? null,
74
+ deps_summary: {
75
+ has_tailwind: !!deps['tailwindcss'],
76
+ has_shadcn: !!deps['@shadcn/ui'] || !!deps['shadcn-ui'],
77
+ has_recharts: !!deps['recharts'],
78
+ has_chartjs: !!deps['chart.js'] || !!deps['chartjs'],
79
+ has_d3: !!deps['d3'],
80
+ has_lucide: !!deps['lucide-react'] || !!deps['lucide-vue'] || !!deps['lucide'],
81
+ has_heroicons: !!deps['@heroicons/react'] || !!deps['@heroicons/vue'],
82
+ has_framer_motion: !!deps['framer-motion'],
83
+ },
84
+ css_files: cssFiles,
85
+ component_files: componentFiles.slice(0, 10),
86
+ ts: new Date().toISOString(),
87
+ };
88
+
89
+ fs.mkdirSync(path.dirname(out), { recursive: true });
90
+ fs.writeFileSync(out, JSON.stringify(ctx, null, 2), 'utf8');
91
+ process.stdout.write(out + '\n');
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env node
2
+ // Friendly post-install nudge — only when the user explicitly installed
3
+ // oh-my-design-cli (global or as a top-level dependency).
4
+ //
5
+ // Suppressed when:
6
+ // - running inside CI
7
+ // - this package is being installed as a sub-dependency
8
+ // - npm is in production mode
9
+ //
10
+ // Plain CommonJS so it runs on any Node version without build deps.
11
+
12
+ 'use strict';
13
+
14
+ if (
15
+ process.env.CI ||
16
+ process.env.NODE_ENV === 'production' ||
17
+ // Sub-dep install: npm sets npm_command but no INIT_CWD pointing here
18
+ process.env.npm_config_global !== 'true' && !process.env.INIT_CWD
19
+ ) {
20
+ process.exit(0);
21
+ }
22
+
23
+ const c = {
24
+ bold: (s) => `\x1b[1m${s}\x1b[0m`,
25
+ cyan: (s) => `\x1b[36m${s}\x1b[0m`,
26
+ green: (s) => `\x1b[32m${s}\x1b[0m`,
27
+ dim: (s) => `\x1b[2m${s}\x1b[0m`,
28
+ };
29
+
30
+ const lines = [
31
+ '',
32
+ `${c.green('✓')} ${c.bold('oh-my-design installed.')} ${c.dim('(commands: omd, oh-my-design)')}`,
33
+ '',
34
+ ` ${c.bold('Next')} in any project:`,
35
+ '',
36
+ ` ${c.cyan('cd')} ${c.dim('<your project>')}`,
37
+ ` ${c.cyan('omd install-skills')} ${c.dim('# one time per project — installs the design harness')}`,
38
+ '',
39
+ ` ${c.bold('Then open Claude Code (or Codex). Just say what you want:')}`,
40
+ '',
41
+ ` ${c.dim('"토스 스타일 가족 식단 공유 앱 메인 화면 디자인해줘"')}`,
42
+ ` ${c.dim('"Linear-clone B2B SaaS dashboard 만들고 싶어"')}`,
43
+ ` ${c.dim('"이 카드 좀 더 세련되게"')}`,
44
+ '',
45
+ ` ${c.bold('하네스가 알아서 호출됨')} ${c.dim('— Claude Code hook이 디자인 요청 감지해서 자동 라우팅. 별도 슬래시 안 쳐도 됩니다.')}`,
46
+ '',
47
+ ` ${c.dim('Power user shortcut: ')} ${c.cyan('/omd-harness <task>')} ${c.dim('— hook 우회하고 즉시 진입.')}`,
48
+ '',
49
+ ` ${c.bold('⚠ Already running Claude Code?')} ${c.dim('Quit (Cmd+Q on macOS) and relaunch — subagents only load at session start.')}`,
50
+ '',
51
+ ];
52
+
53
+ console.log(lines.join('\n'));
54
+
@@ -1,85 +1,96 @@
1
1
  ---
2
2
  name: omd:apply
3
- description: "프로젝트 루트의 DESIGN.md를 UI/스타일링/마이크로카피/모션 작업의 authoritative brand context로 적용합니다. 컴포넌트 만들기, CSS 수정, 카피 작성, 애니메이션 튜닝, 디자인 시스템 질문에 트리거됩니다. 사용자 교정이 발생하면 자동으로 omd remember 호출해 preference log에 기록합니다."
3
+ description: "프로젝트의 DESIGN.md를 UI/스타일링/마이크로카피 작업의 authoritative brand context로 적용. 작은 변경(컴포넌트 1개 수정, 단일 텍스트 교체 등)은 인라인 처리하고, 화면 단위 신규 디자인·에셋 생성·접근성 감사·페르소나 검증 등 복합 작업은 적합한 서브에이전트로 즉시 dispatch. 사용자 교정 발생 omd:remember 스킬을 트리거합니다."
4
4
  ---
5
5
 
6
- # omd:apply — Brand Context Injection
6
+ # omd:apply — Brand Context Injection + Dispatch Router
7
7
 
8
- 프로젝트 루트의 `DESIGN.md`를 읽고, UI 관련 모든 작업의 authoritative brand context로 사용한다. 작업 도중 사용자가 디자인 선택을 교정하면 `.omd/preferences.md`에 자동 기록한다.
8
+ DESIGN.md 모든 UI/디자인 작업의 권위 있는 컨텍스트로 사용한다. 단일 책임은 가지:
9
+
10
+ 1. **인라인 처리** — 작은 단일 변경 (1 component, 1 token, 1 카피 라인)은 직접 Edit 툴로 처리
11
+ 2. **Dispatch** — 복합 작업은 적합한 서브에이전트로 즉시 라우팅 (master 거치지 않음)
12
+
13
+ 복합 작업을 인라인으로 처리하면 안 된다. 이 스킬의 가장 중요한 책임은 *언제 dispatch할지 정확히 판단하는 것*.
9
14
 
10
15
  ## 트리거 조건
11
16
 
12
17
  다음 중 하나가 감지되면 SKILL 전체를 로드한다.
13
- - 새 컴포넌트 생성 (button, card, dialog, nav, form 등)
14
- - 기존 컴포넌트 스타일 변경 (Tailwind 클래스, CSS, 토큰 값)
15
- - 마이크로카피 작성/수정 (버튼 라벨, empty state, 에러 메시지, tooltip)
16
- - 모션/트랜지션 추가
17
- - 색상·타이포그래피·스페이싱 조정
18
- - 디자인 시스템 관련 질문
19
-
20
- ## Phase 1 — DESIGN.md 로드
21
18
 
22
- 작업 시작 전:
19
+ - 컴포넌트 생성 / 수정 (button, card, dialog, nav, form 등)
20
+ - 스타일 변경 (Tailwind 클래스, CSS, 토큰 값)
21
+ - 마이크로카피 작성 / 수정 (버튼 라벨, empty state, 에러, tooltip)
22
+ - 모션 / 트랜지션 추가
23
+ - 색상 · 타이포그래피 · 스페이싱 조정
24
+ - 에셋 (아이콘, 차트, 일러스트, 3D 렌더) 요청
25
+ - 디자인 시스템 관련 질문
23
26
 
24
- 1. 프로젝트 루트의 `DESIGN.md`를 **전체 읽는다**. 요약하지 말고 실제 파일 내용을 Read 툴로 로드.
25
- 2. `.omd/preferences.md`가 있으면 함께 읽는다. `status: pending` 엔트리들은 아직 DESIGN.md에 반영 안 된 교정사항 — **이들을 DESIGN.md보다 우선** 적용한다.
26
- 3. Precedence 순서:
27
- ```
28
- .omd/preferences.md (pending) > DESIGN.md > framework defaults
29
- ```
27
+ ## Phase 0 Dispatch decision tree (가장 먼저)
30
28
 
31
- DESIGN.md가 없으면 사용자에게 알리고 `omd init` 제안. 임의 생성하지 것.
29
+ 작업 시작 전에 어떤 처리 경로인지 결정한다. 다음 표를 위에서부터 순차 매칭, 첫 번째 매칭 행으로 진행:
32
30
 
33
- ## Phase 2 Brand Context 적용
31
+ | 사용자 요청 패턴 | 처리 경로 | 이유 |
32
+ |---|---|---|
33
+ | "에셋 / 아이콘 / 일러스트 / 차트 / 사진 / 로고 / 그래프 / SVG 만들어" | dispatch `omd-asset-curator` | 매체 선택 + 스택 매칭이 전문 영역 |
34
+ | "3D / 렌더 / 블렌더 / 목업" 명시 | dispatch `omd-3d-blender` | Blender MCP 필요 |
35
+ | "메인 화면 / landing / 전체 디자인 / 처음부터 / 와이어프레임" | 사용자에게 `/omd-harness` 추천 | 10-phase 파이프라인이 적합 |
36
+ | "접근성 / a11y / 색약 / 키보드 네비" 감사 | dispatch `omd-a11y-auditor` | 전문 감사 |
37
+ | "마이크로카피만 다듬어 / 카피 톤 정리 / empty state 문구 전부" 복수 | dispatch `omd-microcopy` | voice 일관성 |
38
+ | "사용자 시나리오 / 페르소나 walk through / 4명 입장에서 검토" | dispatch `omd-persona-tester` | adversarial 4-페르소나 |
39
+ | "이 카피 좋은지 / hero 카피 약점 / 섹션별 카피 전문가 의견 / A/B 후보" | dispatch `omd-ux-writer` | UX writing 분석 + 대안 + 근거 |
40
+ | "이 인터랙션 / 모션 / 포커스 / 모바일 / 지각 성능 / 섹션별 UX 약점" | dispatch `omd-ux-engineer` | 코드 레벨 인터랙션 감사 + fix |
41
+ | "랜딩 / 메인 화면 / 페이지 *전체*를 전문가 의견으로 개선" | dispatch `omd-ux-writer` + `omd-ux-engineer` (병렬) | 두 트랙 동시 — writing + engineering |
42
+ | "이게 왜 안 좋은지 critique / postmortem / root cause" | dispatch `omd-critic` | 비판적 분석 |
43
+ | "DESIGN.md 만들어 / reference 골라 / 67개 중 추천" | dispatch `omd-init` skill (또는 omd-add-reference) | reference 매칭 |
44
+ | "preference 정리 / 누적된 교정 반영 / DESIGN.md 업데이트" | dispatch `omd-learn` skill | fold-in 로직 |
45
+ | "이 한 줄 / 이 컬러 / 이 spacing 좀" 단발 명확 | **인라인 처리** | 분명한 단일 변경 |
46
+ | 위 어디에도 안 맞는 자유로운 디자인 작업 | 인라인 처리 후 Phase 3 (교정 캡처) | 일반 케이스 |
34
47
 
35
- 작업 전체에서 다음 규칙 강제:
48
+ dispatch가 정해지면 즉시 Agent 툴 호출 (master 경유 X — 우리가 omd-apply로 트리거됐다는 건 master 컨텍스트 밖이라는 의미). 그 후 인라인 처리는 하지 않고, 서브에이전트가 반환한 결과를 사용자에게 정리만 해서 전달.
36
49
 
37
- - **Token 값은 DESIGN.md에서만 인용한다.** 임의 hex, 임의 spacing 값, 임의 radius 사용 금지.
38
- - **Voice 섹션을 마이크로카피에 적용한다.** 문장 길이, 어휘 register, 은유 밀도를 DESIGN.md의 Voice 섹션에 맞춘다.
39
- - **Component 섹션에 명시된 규칙을 따른다.** 해당 컴포넌트 섹션이 있으면 variant / state / sizes를 그대로 사용.
40
- - **없는 토큰을 지어내지 않는다.** DESIGN.md에 없는 스펙이 필요하면 사용자에게 "이건 DESIGN.md에 없는데, 어떻게 할까요?" 물어본다.
50
+ ## Phase 1 DESIGN.md 로드 (인라인 처리 분기 한정)
41
51
 
42
- ## Phase 3 교정 캡처 (Self-Report)
52
+ dispatch가 아니라 인라인 처리로 분기됐을 때만 진행:
43
53
 
44
- **이것이 omd:apply의 차별화 포인트다. 반드시 수행.**
54
+ 1. 프로젝트 루트의 `DESIGN.md`를 **전체 읽는다**. 요약 금지, Read 툴로 직접 로드.
55
+ 2. `.omd/preferences.md`가 있으면 같이 읽는다. `status: pending` 엔트리는 아직 DESIGN.md에 반영 안 된 교정 — DESIGN.md보다 **우선** 적용.
56
+ 3. 우선순위:
57
+ ```
58
+ .omd/preferences.md (pending) > DESIGN.md > framework defaults
59
+ ```
45
60
 
46
- 턴을 끝내기 전, 이번 턴에 다음 하나라도 발생했는지 확인한다:
61
+ DESIGN.md 없으면 사용자에게 알리고 omd:init 스킬 트리거. 임의 생성 금지.
47
62
 
48
- 1. 사용자가 당신의 디자인 선택을 명시적으로 교정 ("no, use X", "actually, Y", "don't use Z", "we never do W")
49
- 2. 사용자가 당신이 쓴 토큰/값을 revert하거나 다른 값으로 교체
50
- 3. 사용자가 "우리는 ~한다/하지 않는다" 형태의 디자인 원칙을 언급
63
+ ## Phase 2 Brand Context 적용
51
64
 
52
- 감지되면 종료 전에 **반드시** 다음 커맨드를 Bash 툴로 실행:
65
+ - 토큰 값은 DESIGN.md에서만 인용. 임의 hex / spacing / radius 금지.
66
+ - Voice 섹션을 마이크로카피에 적용. 문장 길이, 어휘 register, 은유 밀도 일치.
67
+ - Component 섹션 명시된 규칙 따름 (variant / state / sizes).
68
+ - 없는 토큰 지어내지 않음. 필요 시 사용자에게 "이건 DESIGN.md에 없는데, 어떻게 할까요?" 묻기.
53
69
 
54
- ```bash
55
- omd remember "<교정 내용을 한 문장으로 요약>" --context "<수정된 파일 경로 또는 사용자 발화 요약>"
56
- ```
70
+ ## Phase 3 — 교정 캡처
57
71
 
58
- `--agent` 플래그는 생략한다. CLI가 환경변수(`CLAUDECODE`, `CODEX_SESSION_ID`, `OPENCODE` 등)로 자동 감지한다. 명시 override가 필요한 경우만 `--agent <name>`.
72
+ 종료 전에 다음 하나가 있었는지 확인:
59
73
 
60
- 예시:
61
- - 사용자: "CTAs는 절대 uppercase 쓰지 마"
62
- `omd remember "CTA buttons must never use uppercase text"`
63
- - 사용자가 `bg-brand-600`을 `bg-brand-500`으로 직접 바꿈
64
- → `omd remember "primary CTA background should be brand-500, not brand-600" --context "src/components/Button.tsx"`
65
- - 사용자: "우리는 4pt grid가 아니라 8pt 써"
66
- → `omd remember "spacing scale uses 8pt base, not 4pt"`
74
+ 1. 사용자가 디자인 선택을 명시적 교정 ("no, use X", "actually, Y", "don't use Z", "we never do W")
75
+ 2. 사용자가 토큰/값을 revert 또는 교체
76
+ 3. 사용자가 "우리는 ~한다/하지 않는다" 형태 원칙 언급
67
77
 
68
- scope는 note 내용에서 자동 추론된다 (keyword-based). 명시적으로 override가 필요하면 `--scope <value>` 추가 (e.g. `components.button`, `color`, `spacing`, `typography`, `voice`, `motion`, `layout`, `visualTheme`).
78
+ 감지되면 **omd:remember 스킬을 트리거**한다 (CLI 호출 X `.omd/preferences.md`에 직접 append). 트리거 메서드: omd-remember SKILL.md의 Step 1-6 절차를 따라 Edit 툴로 파일 수정.
69
79
 
70
- ## Phase 4 — 확인 메시지 (간결)
80
+ ## Phase 4 — 확인 메시지
71
81
 
72
- 교정을 기록했을 때만 턴 끝에 한 줄로 알린다:
82
+ 교정 기록 턴 끝에 한 줄:
73
83
 
74
84
  ```
75
- 📝 Logged to .omd/preferences.md — run `omd learn` to review, `omd learn --apply` to fold into DESIGN.md.
85
+ Logged to .omd/preferences.md — say "preference 정리해줘" later to fold into DESIGN.md.
76
86
  ```
77
87
 
78
- 일반 작업에서는 이 메시지 불필요. 과하게 알리지 말 것.
88
+ 일반 작업에는 불필요. 과한 알림 금지.
79
89
 
80
- ## 금지 사항
90
+ ## 금지
81
91
 
82
- - DESIGN.md 없는데 임의로 생성하지 (사용자에게 `omd init` 제안).
83
- - preferences.md에 직접 쓰지 것. 항상 `omd remember` 커맨드를 경유.
84
- - 교정 감지 시 사용자에게 "기록할까요?" 묻지 말 것. 자동 기록 사후 한 줄 알림.
85
- - 같은 턴 내에서 같은 교정을 중복 기록하지 말 것.
92
+ - DESIGN.md 없는데 임의 생성 금지 (사용자에게 omd:init 제안)
93
+ - 복합 작업을 인라인으로 처리 금지 (Phase 0 dispatch table 따라 라우팅)
94
+ - 교정 감지 시 "기록할까요?" 묻지 말 자동 기록 + 한 줄 알림
95
+ - 같은 턴 같은 교정 중복 기록 금지
96
+ - CLI 호출 (`omd remember`, `omd learn` 등) 금지 — 1.0.0부터 모두 스킬 prose