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
@@ -1 +1 @@
1
- {"version":3,"sources":["../../bin/oh-my-design.ts","../../src/cli/index.ts","../../src/cli/prompts.ts","../../src/core/preview-generator.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { readFileSync, existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { run } from '../src/cli/index.js';\n\nfunction readPackageVersion(): string {\n let cur = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 8; i++) {\n const pkg = join(cur, 'package.json');\n if (existsSync(pkg)) {\n try {\n return JSON.parse(readFileSync(pkg, 'utf8')).version ?? '0.0.0';\n } catch {\n return '0.0.0';\n }\n }\n const parent = dirname(cur);\n if (parent === cur) break;\n cur = parent;\n }\n return '0.0.0';\n}\n\nconst program = new Command();\n\nprogram\n .name('oh-my-design')\n .description('Interactive CLI to generate DESIGN.md files for AI coding agents')\n .version(readPackageVersion())\n .showSuggestionAfterError(true)\n .showHelpAfterError(true);\n\nprogram\n .command('generate')\n .description('Generate DESIGN.md and interactive preview')\n .option('--config <hash>', 'Apply a config hash from the web builder')\n .action(async (opts: { config?: string }) => {\n if (opts.config) {\n // Decode config hash and generate directly without prompts\n const { writeFileSync } = await import('fs');\n const { resolve } = await import('path');\n\n // Decode the hash — format: refId|primary|font|weight|radius|dark|components|stylePrefs\n // parts[7] (stylePrefs) was added in v2. Old hashes lack it and still decode.\n let b64 = opts.config.replace(/-/g, '+').replace(/_/g, '/');\n while (b64.length % 4) b64 += '=';\n const decoded = Buffer.from(b64, 'base64').toString('utf-8');\n const parts = decoded.split('|');\n\n const refId = parts[0] || 'vercel';\n const overrides = {\n primaryColor: parts[1] || undefined,\n fontFamily: parts[2] || undefined,\n headingWeight: parts[3] || undefined,\n borderRadius: parts[4] || undefined,\n darkMode: parts[5] === '1',\n };\n const components = parts[6] ? parts[6].split(',').filter(Boolean) : [];\n // stylePreferences are decoded but not yet applied here — the CLI's\n // customizer (src/core/customizer.ts) does not mirror the web's\n // stylePreferences inline-rewrite logic yet. Parsed here so old and\n // new hashes coexist without error; CLI integration is a follow-up.\n const stylePrefs: Record<string, string> = {};\n if (parts[7]) {\n for (const pair of parts[7].split(';')) {\n const [k, v] = pair.split('=');\n if (k && v) stylePrefs[k] = v;\n }\n }\n\n // Load reference and apply\n const { loadReference } = await import('../src/core/reference-parser.js');\n const { applyOverrides } = await import('../src/core/customizer.js');\n\n const ref = loadReference(refId);\n const mode = (overrides.primaryColor || overrides.fontFamily) ? 'customized' as const : 'as-is' as const;\n const { designMd, shadcnCss, previewData } = applyOverrides(ref, {\n primaryColor: overrides.primaryColor,\n fontFamily: overrides.fontFamily,\n headingWeight: overrides.headingWeight,\n borderRadius: overrides.borderRadius,\n darkMode: overrides.darkMode,\n }, mode, components);\n\n previewData.shadcnCss = shadcnCss;\n previewData.designMd = designMd;\n\n const outputDir = process.cwd();\n const mdPath = resolve(outputDir, 'DESIGN.md');\n writeFileSync(mdPath, designMd, 'utf-8');\n\n console.log(`\\x1b[32m✓\\x1b[0m DESIGN.md generated at ${mdPath}`);\n console.log(` Based on: ${ref.name}`);\n if (overrides.primaryColor) console.log(` Primary: ${overrides.primaryColor}`);\n if (overrides.fontFamily) console.log(` Font: ${overrides.fontFamily}`);\n if (overrides.headingWeight) console.log(` Weight: ${overrides.headingWeight}`);\n if (overrides.borderRadius) console.log(` Radius: ${overrides.borderRadius}`);\n if (overrides.darkMode) console.log(` Dark mode: included`);\n } else {\n await run();\n }\n });\n\nprogram\n .command('install-skills')\n .description('Install omd:* skill files into agent directories (.claude/, .codex/, .opencode/)')\n .option('--dir <path>', 'Project root (defaults to cwd)')\n .option('--agent <name...>', 'Restrict to specific agents (claude-code | codex | opencode)')\n .option('--force', 'Overwrite existing files even without the omd marker')\n .action(\n async (opts: { dir?: string; agent?: string[]; force?: boolean }) => {\n const { runInstallSkills } = await import('../src/cli/install-skills.js');\n const validAgents = ['claude-code', 'codex', 'opencode'] as const;\n type Agent = (typeof validAgents)[number];\n const agents = opts.agent\n ? (opts.agent.filter((a): a is Agent =>\n (validAgents as readonly string[]).includes(a)\n ) as Agent[])\n : undefined;\n const code = await runInstallSkills({\n dir: opts.dir,\n agents,\n force: opts.force,\n });\n if (code !== 0) process.exit(code);\n }\n );\n\nconst referenceCmd = program\n .command('reference')\n .description('Inspect bundled design references');\n\nreferenceCmd\n .command('list')\n .description('List all bundled reference ids')\n .action(async () => {\n const { runReferenceList } = await import('../src/cli/reference.js');\n process.exit(runReferenceList());\n });\n\nreferenceCmd\n .command('show <id>')\n .description('Print the full reference DESIGN.md to stdout')\n .action(async (id: string) => {\n const { runReferenceShow } = await import('../src/cli/reference.js');\n process.exit(runReferenceShow(id));\n });\n\nconst initCmd = program\n .command('init')\n .description('Bootstrap DESIGN.md from a reference + project description');\n\ninitCmd\n .command('recommend <description...>')\n .description('Recommend references matching a project description')\n .option('--top <n>', 'Number of recommendations', '5')\n .option('--json', 'Emit machine-readable JSON')\n .action(\n async (descParts: string[], opts: { top?: string; json?: boolean }) => {\n const { runInitRecommend } = await import('../src/cli/init.js');\n const code = runInitRecommend({\n description: descParts.join(' '),\n topK: opts.top ? Number(opts.top) : 5,\n json: opts.json,\n });\n if (code !== 0) process.exit(code);\n }\n );\n\ninitCmd\n .command('prepare')\n .description('Stage init context: rename existing DESIGN.md, compute delta_set, write .omd/init-context.json')\n .requiredOption('--ref <id>', 'Reference id (e.g. vercel, linear.app)')\n .requiredOption('--description <text>', 'Project description')\n .option('--dir <path>', 'Project root (defaults to cwd)')\n .option('--reason <text>', 'Reason for deprecation header', 'user-initiated omd init')\n .option('--json', 'Emit machine-readable JSON')\n .action(async (opts: { ref: string; description: string; dir?: string; reason?: string; json?: boolean }) => {\n const { runInitPrepare } = await import('../src/cli/init.js');\n const code = runInitPrepare(opts);\n if (code !== 0) process.exit(code);\n });\n\nprogram\n .command('learn')\n .description('List preferences and flip status (applied/rejected)')\n .option('--dir <path>', 'Project root (defaults to cwd)')\n .option('--all', 'Include all statuses (default: pending only)')\n .option('--status <s>', 'Filter by status: pending|applied|rejected|superseded')\n .option('--scope <s>', 'Filter by scope')\n .option('--mark-applied <id>', 'Mark entry as applied')\n .option('--mark-rejected <id>', 'Mark entry as rejected (requires --reason)')\n .option('--reason <text>', 'Reason for rejection')\n .option('--hash <value>', 'DESIGN.md hash to stamp on applied entry (defaults to current file)')\n .action(async (opts: Record<string, string | boolean>) => {\n const { runLearn } = await import('../src/cli/learn.js');\n const code = await runLearn(opts as never);\n if (code !== 0) process.exit(code);\n });\n\nprogram\n .command('remember <note...>')\n .description('Append a design preference/correction to .omd/preferences.md')\n .option('--dir <path>', 'Project root (defaults to cwd)')\n .option('--scope <scope>', 'Explicit scope (e.g. color, components.button)')\n .option('--agent <name>', 'Source agent (e.g. claude-code, codex)')\n .option('--context <text>', 'Source file/line context')\n .action(\n async (\n noteParts: string[],\n opts: {\n dir?: string;\n scope?: string;\n agent?: string;\n context?: string;\n }\n ) => {\n const { runRemember } = await import('../src/cli/remember.js');\n const code = await runRemember(noteParts.join(' '), opts as never);\n if (code !== 0) process.exit(code);\n }\n );\n\nprogram\n .command('sync')\n .description('Sync DESIGN.md shim files (CLAUDE.md, AGENTS.md, .cursor/rules/omd-design.mdc)')\n .option('--dir <path>', 'Project root (defaults to cwd)')\n .option('--force', 'Overwrite drift without prompting')\n .option('--check', 'Exit non-zero if any shim has drift; do not write')\n .action(async (opts: { dir?: string; force?: boolean; check?: boolean }) => {\n const { runSync } = await import('../src/cli/sync.js');\n const code = await runSync(opts);\n if (code !== 0) process.exit(code);\n });\n\nprogram\n .command('preview')\n .description('Open the preview HTML in the default browser')\n .action(async () => {\n const { existsSync } = await import('fs');\n const { resolve } = await import('path');\n const previewPath = resolve(process.cwd(), 'DESIGN.preview.html');\n\n if (!existsSync(previewPath)) {\n console.error('No DESIGN.preview.html found. Run `oh-my-design generate` first.');\n process.exit(1);\n }\n\n const { exec } = await import('child_process');\n const platform = process.platform;\n const cmd = platform === 'darwin' ? 'open' : platform === 'win32' ? 'start' : 'xdg-open';\n exec(`${cmd} \"${previewPath}\"`);\n console.log(`Opening ${previewPath}...`);\n });\n\nprogram.parse();\n","import * as p from '@clack/prompts';\nimport { writeFileSync } from 'fs';\nimport { resolve } from 'path';\nimport { runPrompts } from './prompts.js';\nimport { applyOverrides } from '../core/customizer.js';\nimport { generatePreviewHtml } from '../core/preview-generator.js';\n\nexport async function run() {\n const { reference, overrides, outputMode } = await runPrompts();\n\n const s = p.spinner();\n s.start(`Building design system based on ${reference.name}...`);\n\n // Apply overrides to reference DESIGN.md\n const { designMd, shadcnCss, previewData } = applyOverrides(reference, overrides, outputMode);\n\n // Fill in the preview data with generated content\n previewData.shadcnCss = shadcnCss;\n previewData.designMd = designMd;\n\n // Generate preview HTML\n const html = generatePreviewHtml(previewData);\n\n // Write files\n const outputDir = process.cwd();\n const mdPath = resolve(outputDir, 'DESIGN.md');\n const htmlPath = resolve(outputDir, 'DESIGN.preview.html');\n\n writeFileSync(mdPath, designMd, 'utf-8');\n writeFileSync(htmlPath, html, 'utf-8');\n\n s.stop('Done!');\n\n p.log.success(`DESIGN.md → ${mdPath}`);\n p.log.success(`Preview HTML → ${htmlPath}`);\n p.outro('Open DESIGN.preview.html in your browser to explore your design system.');\n}\n","import * as p from '@clack/prompts';\nimport pc from 'picocolors';\nimport { listReferences, loadReference } from '../core/reference-parser.js';\nimport type { ReferenceEntry } from '../core/reference-parser.js';\n\n// ── Types ────────────────────────────────────────────────────────\n\nexport interface CustomOverrides {\n primaryColor?: string; // override primary color hex\n headingWeight?: string; // override heading weight\n borderRadius?: string; // override base radius\n fontFamily?: string; // override primary font\n darkMode: boolean;\n additionalNotes?: string;\n}\n\nexport interface PromptResult {\n reference: ReferenceEntry;\n overrides: CustomOverrides;\n outputMode: 'as-is' | 'customized';\n}\n\n// ── Main ─────────────────────────────────────────────────────────\n\nexport async function runPrompts(): Promise<PromptResult> {\n p.intro(`${pc.bold('oh-my-design')} — Generate your DESIGN.md from real design systems`);\n\n // Step 1: List all references grouped by category\n const refs = listReferences();\n const categories = [...new Set(refs.map((r) => r.category))];\n\n // Step 1a: Pick category\n const category = await p.select({\n message: 'Pick a category:',\n options: categories.map((cat) => {\n const count = refs.filter((r) => r.category === cat).length;\n return { value: cat, label: `${cat}`, hint: `${count} references` };\n }),\n });\n if (p.isCancel(category)) { p.cancel('Cancelled.'); process.exit(0); }\n\n // Step 1b: Pick reference within category\n const categoryRefs = refs.filter((r) => r.category === category);\n const refId = await p.select({\n message: `Select a design system reference:`,\n options: categoryRefs.map((r) => ({\n value: r.id,\n label: `${r.name}`,\n hint: `${r.primaryColor}`,\n })),\n });\n if (p.isCancel(refId)) { p.cancel('Cancelled.'); process.exit(0); }\n\n const reference = loadReference(refId as string);\n\n p.log.info(`${pc.bold(reference.name)} loaded`);\n p.log.info(` Primary: ${pc.bold(reference.colors.primary)} ${reference.colors.primaryName}`);\n p.log.info(` Font: ${reference.typography.primary}`);\n p.log.info(` Radius: ${reference.radius}`);\n if (reference.mood) {\n p.log.info(` Mood: ${reference.mood.slice(0, 120)}...`);\n }\n\n // Step 2: Use as-is or customize?\n const mode = await p.select({\n message: 'How do you want to use this design system?',\n options: [\n { value: 'as-is', label: 'Use as-is', hint: 'Copy the reference DESIGN.md directly' },\n { value: 'customized', label: 'Customize', hint: 'Modify colors, fonts, radius, etc.' },\n ],\n });\n if (p.isCancel(mode)) { p.cancel('Cancelled.'); process.exit(0); }\n\n if (mode === 'as-is') {\n const darkMode = await p.confirm({\n message: 'Include dark mode notes?',\n initialValue: false,\n });\n if (p.isCancel(darkMode)) { p.cancel('Cancelled.'); process.exit(0); }\n\n return {\n reference,\n overrides: { darkMode },\n outputMode: 'as-is',\n };\n }\n\n // Step 3: Customization prompts\n const overrides = await p.group(\n {\n primaryColor: () =>\n p.text({\n message: `Primary color (current: ${reference.colors.primary}):`,\n placeholder: 'Press enter to keep, or type a hex like #6366f1',\n validate: (val) => {\n if (val && !/^#[0-9a-fA-F]{6}$/.test(val)) return 'Invalid hex color';\n },\n }),\n\n fontFamily: () =>\n p.select({\n message: `Primary font (current: ${reference.typography.primary}):`,\n options: [\n { value: '', label: `Keep \"${reference.typography.primary}\"`, hint: 'No change' },\n { value: 'Inter', label: 'Inter', hint: 'Clean, geometric sans-serif' },\n { value: 'system-ui', label: 'System UI', hint: 'Native OS fonts' },\n { value: 'JetBrains Mono', label: 'JetBrains Mono', hint: 'Monospace, technical' },\n { value: 'Geist', label: 'Geist', hint: 'Vercel\\'s modern sans' },\n ],\n }),\n\n headingWeight: () =>\n p.select({\n message: `Heading weight (current: ${reference.typography.headingWeight}):`,\n options: [\n { value: '', label: `Keep ${reference.typography.headingWeight}`, hint: 'No change' },\n { value: '300', label: '300 — Light', hint: 'Whisper authority (Stripe style)' },\n { value: '400', label: '400 — Regular' },\n { value: '500', label: '500 — Medium', hint: 'Balanced emphasis' },\n { value: '600', label: '600 — Semibold', hint: 'Strong headings' },\n { value: '700', label: '700 — Bold', hint: 'Maximum impact' },\n ],\n }),\n\n borderRadius: () =>\n p.select({\n message: `Border radius (current: ${reference.radius}):`,\n options: [\n { value: '', label: `Keep \"${reference.radius}\"`, hint: 'No change' },\n { value: '2px', label: 'Sharp (2px)', hint: 'Technical, precise' },\n { value: '4px', label: 'Tight (4px)', hint: 'Conservative' },\n { value: '6px', label: 'Moderate (6px)', hint: 'Balanced' },\n { value: '8px', label: 'Comfortable (8px)', hint: 'Friendly' },\n { value: '12px', label: 'Rounded (12px)', hint: 'Soft, approachable' },\n { value: '9999px', label: 'Pill', hint: 'Fully rounded' },\n ],\n }),\n\n darkMode: () =>\n p.confirm({\n message: 'Generate dark mode variant?',\n initialValue: false,\n }),\n\n additionalNotes: () =>\n p.text({\n message: 'Any additional customization notes? (optional)',\n placeholder: 'e.g. \"Use more warm tones\" or press enter to skip',\n }),\n },\n {\n onCancel: () => { p.cancel('Cancelled.'); process.exit(0); },\n },\n );\n\n return {\n reference,\n overrides: {\n primaryColor: overrides.primaryColor || undefined,\n fontFamily: overrides.fontFamily || undefined,\n headingWeight: overrides.headingWeight || undefined,\n borderRadius: overrides.borderRadius || undefined,\n darkMode: overrides.darkMode,\n additionalNotes: overrides.additionalNotes || undefined,\n },\n outputMode: 'customized',\n };\n}\n","import type { PreviewData } from './customizer.js';\nimport {\n generateColorScale,\n contrastForeground,\n hslString,\n hexToHsl,\n hslToHex,\n lighten,\n darken,\n generateChartColors,\n} from '../utils/color.js';\n\nexport function generatePreviewHtml(data: PreviewData): string {\n const {\n name, basedOn, primary, background, foreground, font, headingWeight,\n radius, colors, darkMode, shadcnCss,\n } = data;\n\n const isLightBg = isLight(background);\n const scale = generateColorScale(primary);\n const radiusPx = radius === '9999px' ? '24px' : radius;\n const borderColor = colors.border;\n const accent = colors.accent;\n const muted = colors.muted;\n const chart = colors.chart;\n\n // Generate dark mode vars for CSS\n const darkBg = hslToHex(hexToHsl(primary)[0], 15, 7);\n const darkFg = '#fafafa';\n const darkBorder = hslToHex(hexToHsl(primary)[0], 10, 18);\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>${esc(name)} — Design System Preview</title>\n<style>\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n :root {\n --bg: ${background};\n --fg: ${foreground};\n --primary: ${primary};\n --primary-fg: ${contrastForeground(primary)};\n --accent: ${accent};\n --muted: ${muted};\n --muted-fg: ${lighten(foreground, 40)};\n --border: ${borderColor};\n --destructive: #ef4444;\n --card: ${isLightBg ? '#ffffff' : lighten(background, 3)};\n --radius: ${radiusPx};\n }\n\n .dark {\n --bg: ${darkBg};\n --fg: ${darkFg};\n --card: ${lighten(darkBg, 3)};\n --muted: ${hslToHex(hexToHsl(primary)[0], 10, 15)};\n --muted-fg: ${darken(darkFg, 35)};\n --border: ${darkBorder};\n }\n\n body {\n font-family: \"${font}\", \"Inter\", system-ui, sans-serif;\n background: var(--bg);\n color: var(--fg);\n line-height: 1.5;\n transition: background 0.25s, color 0.25s;\n }\n\n .shell { max-width: 1100px; margin: 0 auto; padding: 40px 24px 80px; }\n\n /* ── Header ─────── */\n .header {\n display: flex; justify-content: space-between; align-items: center;\n padding-bottom: 24px; margin-bottom: 40px;\n border-bottom: 1px solid var(--border);\n }\n .header h1 {\n font-size: 2rem; font-weight: ${headingWeight};\n letter-spacing: -0.02em;\n }\n .header .sub {\n font-size: 0.875rem; color: var(--muted-fg); margin-top: 4px;\n }\n .controls { display: flex; gap: 8px; }\n .ctrl-btn {\n padding: 8px 16px; border-radius: var(--radius);\n font-size: 0.8125rem; font-family: inherit; cursor: pointer;\n transition: all 0.15s; border: 1px solid var(--border);\n background: var(--card); color: var(--fg);\n }\n .ctrl-btn:hover { opacity: 0.85; }\n .ctrl-btn.primary { background: var(--primary); color: var(--primary-fg); border-color: transparent; }\n\n /* ── Sections ───── */\n .section { margin-bottom: 48px; }\n .section-title {\n font-size: 1.375rem; font-weight: ${headingWeight};\n letter-spacing: -0.01em; margin-bottom: 16px;\n }\n .section-sub { font-size: 0.8125rem; color: var(--muted-fg); margin-bottom: 16px; }\n\n /* ── Color Scale ── */\n .scale-row {\n display: flex; border-radius: var(--radius); overflow: hidden;\n border: 1px solid var(--border); margin-bottom: 24px;\n }\n .scale-stop {\n flex: 1; padding: 28px 4px 8px; text-align: center;\n font-size: 10px; font-family: monospace; cursor: pointer;\n transition: transform 0.1s; position: relative;\n }\n .scale-stop:hover { z-index: 1; transform: scaleY(1.12); }\n .scale-stop .lbl { display: block; opacity: 0.7; margin-bottom: 2px; }\n .scale-stop .hex {\n background: rgba(0,0,0,0.2); color: #fff;\n padding: 1px 4px; border-radius: 3px; font-size: 9px;\n }\n\n /* ── Color Chips ── */\n .chip-grid {\n display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 12px;\n }\n .chip {\n border-radius: var(--radius); overflow: hidden;\n border: 1px solid var(--border); cursor: pointer;\n transition: transform 0.15s, box-shadow 0.15s;\n }\n .chip:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.1); }\n .chip .sw { height: 64px; display: flex; align-items: end; padding: 6px; }\n .chip .sw span {\n font-size: 10px; font-family: monospace;\n background: rgba(0,0,0,0.25); color: #fff;\n padding: 1px 5px; border-radius: 3px;\n }\n .chip .inf { padding: 8px; background: var(--card); }\n .chip .inf .n { font-size: 0.75rem; font-weight: 500; }\n .chip .inf .v { font-size: 10px; color: var(--muted-fg); font-family: monospace; }\n\n /* ── Typography ── */\n .type-rows { display: flex; flex-direction: column; gap: 8px; }\n .type-row {\n display: flex; align-items: baseline; gap: 16px;\n padding: 12px 16px; border-radius: var(--radius);\n background: var(--card); border: 1px solid var(--border);\n }\n .type-row .lbl {\n min-width: 48px; font-size: 0.75rem; color: var(--muted-fg);\n font-family: monospace; flex-shrink: 0;\n }\n .type-row .sample { flex: 1; }\n\n /* ── Components ── */\n .comp-card {\n padding: 24px; border-radius: var(--radius);\n background: var(--card); border: 1px solid var(--border);\n margin-bottom: 16px;\n }\n .comp-card h3 { font-size: 1rem; font-weight: 600; margin-bottom: 16px; }\n .comp-row { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-bottom: 12px; }\n\n .btn {\n display: inline-flex; align-items: center; justify-content: center;\n font-family: inherit; font-weight: 500; cursor: pointer;\n transition: all 0.15s; border: none; font-size: 0.8125rem;\n padding: 8px 16px; height: 36px; border-radius: var(--radius);\n }\n .btn-primary { background: var(--primary); color: var(--primary-fg); }\n .btn-secondary { background: var(--muted); color: var(--fg); }\n .btn-outline { background: transparent; color: var(--fg); border: 1px solid var(--border); }\n .btn-ghost { background: transparent; color: var(--fg); }\n .btn-destructive { background: var(--destructive); color: #fff; }\n .btn:hover { opacity: 0.9; }\n\n .badge {\n display: inline-flex; font-size: 0.6875rem; font-weight: 500;\n padding: 2px 10px; border-radius: 9999px;\n }\n .badge-primary { background: var(--primary); color: var(--primary-fg); }\n .badge-secondary { background: var(--muted); color: var(--fg); }\n .badge-outline { background: transparent; color: var(--fg); border: 1px solid var(--border); }\n\n .input-demo {\n width: 100%; max-width: 320px; font-family: inherit;\n background: var(--bg); color: var(--fg);\n border: 1px solid var(--border); border-radius: var(--radius);\n padding: 8px 12px; font-size: 0.8125rem; outline: none;\n transition: border-color 0.15s;\n }\n .input-demo:focus {\n border-color: var(--primary);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--primary) 20%, transparent);\n }\n\n .demo-table {\n width: 100%; border-collapse: collapse; font-size: 0.8125rem;\n }\n .demo-table th, .demo-table td {\n padding: 10px 12px; text-align: left;\n border-bottom: 1px solid var(--border);\n }\n .demo-table th {\n font-weight: 500; color: var(--muted-fg);\n font-size: 0.75rem; text-transform: uppercase;\n letter-spacing: 0.05em;\n }\n .demo-table tr:hover td { background: var(--muted); }\n\n .demo-card {\n padding: 20px; border-radius: var(--radius);\n background: var(--card); border: 1px solid var(--border);\n max-width: 340px; box-shadow: 0 1px 3px rgba(0,0,0,0.06);\n }\n .demo-card h4 { font-weight: 600; margin-bottom: 6px; }\n .demo-card p { font-size: 0.8125rem; color: var(--muted-fg); margin-bottom: 14px; }\n\n .demo-dialog {\n padding: 24px; border-radius: var(--radius);\n background: var(--card); box-shadow: 0 20px 40px rgba(0,0,0,0.15);\n max-width: 400px; border: 1px solid var(--border);\n }\n .demo-dialog h4 { font-weight: 600; margin-bottom: 8px; }\n .demo-dialog p { font-size: 0.8125rem; color: var(--muted-fg); margin-bottom: 16px; }\n .demo-dialog .acts { display: flex; justify-content: flex-end; gap: 8px; }\n\n .tabs {\n display: flex; background: var(--muted); border-radius: var(--radius); padding: 4px; gap: 2px;\n }\n .tab {\n padding: 6px 14px; border-radius: calc(var(--radius) - 2px);\n font-size: 0.8125rem; font-family: inherit; font-weight: 500;\n border: none; cursor: pointer; background: transparent;\n color: var(--muted-fg); transition: all 0.15s;\n }\n .tab.active {\n background: var(--bg); color: var(--fg);\n box-shadow: 0 1px 2px rgba(0,0,0,0.05);\n }\n .tab:hover:not(.active) { color: var(--fg); }\n\n /* ── CSS Block ──── */\n .css-block {\n position: relative; background: var(--card);\n border: 1px solid var(--border); border-radius: var(--radius);\n overflow: hidden;\n }\n .css-block pre {\n padding: 20px; overflow-x: auto; font-family: monospace;\n font-size: 11px; line-height: 1.6; color: var(--fg);\n }\n .css-block .copy-css { position: absolute; top: 8px; right: 8px; }\n\n /* ── Spacing ────── */\n .spacing-vis {\n display: flex; align-items: end; gap: 8px; padding: 24px;\n background: var(--card); border: 1px solid var(--border);\n border-radius: var(--radius);\n }\n .sp-bar { display: flex; flex-direction: column; align-items: center; gap: 4px; }\n .sp-bar .bar { background: var(--primary); border-radius: 2px; width: 28px; }\n .sp-bar .val { font-size: 10px; font-family: monospace; color: var(--muted-fg); }\n\n /* ── Radius ─────── */\n .radius-grid { display: flex; gap: 16px; flex-wrap: wrap; }\n .radius-demo {\n width: 72px; height: 72px; background: var(--primary);\n display: flex; align-items: center; justify-content: center;\n color: var(--primary-fg); font-size: 0.6875rem; font-family: monospace;\n }\n\n /* ── Toast ──────── */\n .toast-popup {\n position: fixed; bottom: 24px; right: 24px;\n padding: 10px 20px; background: var(--fg); color: var(--bg);\n border-radius: var(--radius); font-size: 0.8125rem;\n box-shadow: 0 8px 20px rgba(0,0,0,0.15);\n transform: translateY(100px); opacity: 0;\n transition: all 0.3s; z-index: 100;\n }\n .toast-popup.show { transform: translateY(0); opacity: 1; }\n\n @media (max-width: 768px) {\n .shell { padding: 16px 12px 48px; }\n .header { flex-direction: column; gap: 12px; align-items: flex-start; }\n .chip-grid { grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); }\n }\n</style>\n</head>\n<body>\n<div class=\"shell\">\n\n <div class=\"header\">\n <div>\n <h1>${esc(name)}</h1>\n <div class=\"sub\">Based on ${esc(basedOn)} &middot; Generated by oh-my-design</div>\n </div>\n <div class=\"controls\">\n ${darkMode ? '<button class=\"ctrl-btn\" onclick=\"toggleTheme()\" id=\"theme-btn\">🌙 Dark</button>' : ''}\n <button class=\"ctrl-btn primary\" onclick=\"copyCss()\">Copy CSS</button>\n </div>\n </div>\n\n <!-- Primary Scale -->\n <div class=\"section\">\n <h2 class=\"section-title\">Primary Color Scale</h2>\n <div class=\"section-sub\">Click to copy hex value</div>\n <div class=\"scale-row\">\n${Object.entries(scale).map(([stop, hex]) =>\n ` <div class=\"scale-stop\" style=\"background:${hex};color:${isLight(hex) ? '#000' : '#fff'}\" onclick=\"copy('${hex}')\">\n <span class=\"lbl\">${stop}</span><span class=\"hex\">${hex}</span>\n </div>`).join('\\n')}\n </div>\n </div>\n\n <!-- Semantic Colors -->\n <div class=\"section\">\n <h2 class=\"section-title\">Semantic Colors</h2>\n <div class=\"chip-grid\">\n${[\n { n: 'Primary', bg: primary, v: '--primary' },\n { n: 'Accent', bg: accent, v: '--accent' },\n { n: 'Muted', bg: muted, v: '--muted' },\n { n: 'Destructive', bg: '#ef4444', v: '--destructive' },\n { n: 'Background', bg: background, v: '--background' },\n { n: 'Foreground', bg: foreground, v: '--foreground' },\n { n: 'Border', bg: borderColor, v: '--border' },\n { n: 'Card', bg: isLightBg ? '#ffffff' : lighten(background, 3), v: '--card' },\n].map((c) => ` <div class=\"chip\" onclick=\"copy('${c.bg}')\">\n <div class=\"sw\" style=\"background:${c.bg}\"><span>${c.bg}</span></div>\n <div class=\"inf\"><div class=\"n\">${c.n}</div><div class=\"v\">${c.v}</div></div>\n </div>`).join('\\n')}\n </div>\n </div>\n\n <!-- Chart Colors -->\n <div class=\"section\">\n <h2 class=\"section-title\">Chart Colors</h2>\n <div style=\"display:flex;gap:8px;\">\n${chart.map((c, i) => ` <div style=\"width:48px;height:48px;border-radius:var(--radius);background:${c};cursor:pointer;\" onclick=\"copy('${c}')\" title=\"Chart ${i + 1}: ${c}\"></div>`).join('\\n')}\n </div>\n </div>\n\n <!-- Typography -->\n <div class=\"section\">\n <h2 class=\"section-title\">Typography</h2>\n <div class=\"type-rows\">\n <div class=\"type-row\">\n <span class=\"lbl\">H1</span>\n <span class=\"sample\" style=\"font-size:2.25rem;font-weight:${headingWeight};letter-spacing:-0.02em;line-height:1.2\">Page Title Heading</span>\n </div>\n <div class=\"type-row\">\n <span class=\"lbl\">H2</span>\n <span class=\"sample\" style=\"font-size:1.875rem;font-weight:${headingWeight};letter-spacing:-0.01em;line-height:1.25\">Section Heading</span>\n </div>\n <div class=\"type-row\">\n <span class=\"lbl\">H3</span>\n <span class=\"sample\" style=\"font-size:1.5rem;font-weight:${headingWeight};line-height:1.3\">Subsection</span>\n </div>\n <div class=\"type-row\">\n <span class=\"lbl\">body</span>\n <span class=\"sample\" style=\"font-size:1rem;\">The quick brown fox jumps over the lazy dog. 다람쥐 헌 쳇바퀴에 타고파.</span>\n </div>\n <div class=\"type-row\">\n <span class=\"lbl\">sm</span>\n <span class=\"sample\" style=\"font-size:0.875rem;color:var(--muted-fg)\">Secondary text, labels, and metadata</span>\n </div>\n <div class=\"type-row\">\n <span class=\"lbl\">mono</span>\n <span class=\"sample\" style=\"font-family:monospace;font-size:0.875rem\">const theme = generateDesignSystem();</span>\n </div>\n </div>\n </div>\n\n <!-- Radius -->\n <div class=\"section\">\n <h2 class=\"section-title\">Border Radius</h2>\n <div class=\"radius-grid\">\n <div style=\"text-align:center\"><div class=\"radius-demo\" style=\"border-radius:0\">0</div><div style=\"font-size:10px;margin-top:4px;color:var(--muted-fg)\">none</div></div>\n <div style=\"text-align:center\"><div class=\"radius-demo\" style=\"border-radius:${radiusPx}\">${radiusPx}</div><div style=\"font-size:10px;margin-top:4px;color:var(--muted-fg)\">base</div></div>\n <div style=\"text-align:center\"><div class=\"radius-demo\" style=\"border-radius:${parseInt(radiusPx) * 2}px\">${parseInt(radiusPx) * 2}px</div><div style=\"font-size:10px;margin-top:4px;color:var(--muted-fg)\">lg</div></div>\n <div style=\"text-align:center\"><div class=\"radius-demo\" style=\"border-radius:9999px\">pill</div><div style=\"font-size:10px;margin-top:4px;color:var(--muted-fg)\">full</div></div>\n </div>\n </div>\n\n <!-- Components -->\n <div class=\"section\">\n <h2 class=\"section-title\">Components</h2>\n\n <!-- Buttons -->\n <div class=\"comp-card\">\n <h3>Buttons</h3>\n <div class=\"comp-row\">\n <button class=\"btn btn-primary\">Primary</button>\n <button class=\"btn btn-secondary\">Secondary</button>\n <button class=\"btn btn-outline\">Outline</button>\n <button class=\"btn btn-ghost\">Ghost</button>\n <button class=\"btn btn-destructive\">Delete</button>\n </div>\n </div>\n\n <!-- Badges -->\n <div class=\"comp-card\">\n <h3>Badges</h3>\n <div class=\"comp-row\">\n <span class=\"badge badge-primary\">Active</span>\n <span class=\"badge badge-secondary\">Draft</span>\n <span class=\"badge badge-outline\">Archived</span>\n </div>\n </div>\n\n <!-- Input -->\n <div class=\"comp-card\">\n <h3>Input</h3>\n <div style=\"display:flex;flex-direction:column;gap:12px;max-width:400px;\">\n <div>\n <label style=\"font-size:0.8125rem;font-weight:500;display:block;margin-bottom:4px;\">Email</label>\n <input class=\"input-demo\" type=\"email\" placeholder=\"you@example.com\">\n </div>\n <div>\n <label style=\"font-size:0.8125rem;font-weight:500;display:block;margin-bottom:4px;\">Password</label>\n <input class=\"input-demo\" type=\"password\" placeholder=\"••••••••\">\n </div>\n </div>\n </div>\n\n <!-- Card -->\n <div class=\"comp-card\">\n <h3>Card</h3>\n <div class=\"comp-row\" style=\"align-items:stretch;\">\n <div class=\"demo-card\">\n <h4>Project Overview</h4>\n <p>A summary card showing key metrics and status for your project.</p>\n <button class=\"btn btn-primary\" style=\"height:32px;font-size:0.75rem;\">View Details</button>\n </div>\n </div>\n </div>\n\n <!-- Table -->\n <div class=\"comp-card\">\n <h3>Table</h3>\n <div style=\"overflow-x:auto;\">\n <table class=\"demo-table\">\n <thead><tr><th>Name</th><th>Status</th><th>Role</th><th>Email</th></tr></thead>\n <tbody>\n <tr><td>Kim Minjae</td><td><span class=\"badge badge-primary\" style=\"font-size:10px;\">Active</span></td><td>Developer</td><td>minjae@example.com</td></tr>\n <tr><td>Lee Soyeon</td><td><span class=\"badge badge-secondary\" style=\"font-size:10px;\">Pending</span></td><td>Designer</td><td>soyeon@example.com</td></tr>\n <tr><td>Park Jiwoo</td><td><span class=\"badge badge-outline\" style=\"font-size:10px;\">Inactive</span></td><td>PM</td><td>jiwoo@example.com</td></tr>\n </tbody>\n </table>\n </div>\n </div>\n\n <!-- Dialog -->\n <div class=\"comp-card\">\n <h3>Dialog</h3>\n <div class=\"demo-dialog\">\n <h4>Delete item?</h4>\n <p>This action cannot be undone.</p>\n <div class=\"acts\">\n <button class=\"btn btn-outline\" style=\"height:32px;\">Cancel</button>\n <button class=\"btn btn-destructive\" style=\"height:32px;\">Delete</button>\n </div>\n </div>\n </div>\n\n <!-- Tabs -->\n <div class=\"comp-card\">\n <h3>Tabs</h3>\n <div class=\"tabs\">\n <button class=\"tab active\">Overview</button>\n <button class=\"tab\">Analytics</button>\n <button class=\"tab\">Settings</button>\n </div>\n </div>\n </div>\n\n <!-- shadcn CSS -->\n <div class=\"section\">\n <h2 class=\"section-title\">shadcn/ui CSS Variables</h2>\n <div class=\"section-sub\">Ready to paste into globals.css</div>\n <div class=\"css-block\">\n <button class=\"ctrl-btn copy-css\" onclick=\"copyCss()\" style=\"position:absolute;top:8px;right:8px;\">Copy</button>\n <pre id=\"css-output\">${escHtml(shadcnCss)}</pre>\n </div>\n </div>\n\n</div>\n\n<div class=\"toast-popup\" id=\"toast\">Copied!</div>\n\n<script>\nlet isDark = false;\nfunction toggleTheme() {\n isDark = !isDark;\n document.body.classList.toggle('dark', isDark);\n const btn = document.getElementById('theme-btn');\n if (btn) btn.textContent = isDark ? '☀️ Light' : '🌙 Dark';\n}\nfunction showToast(msg) {\n const el = document.getElementById('toast');\n el.textContent = msg;\n el.classList.add('show');\n setTimeout(() => el.classList.remove('show'), 1800);\n}\nfunction copy(val) {\n navigator.clipboard.writeText(val).then(() => showToast('Copied: ' + val));\n}\nfunction copyCss() {\n const css = document.getElementById('css-output').textContent;\n navigator.clipboard.writeText(css).then(() => showToast('CSS variables copied!'));\n}\ndocument.querySelectorAll('.tab').forEach(t => {\n t.addEventListener('click', () => {\n t.parentElement.querySelectorAll('.tab').forEach(x => x.classList.remove('active'));\n t.classList.add('active');\n });\n});\n</script>\n</body>\n</html>`;\n}\n\nfunction isLight(hex: string): boolean {\n const h = hex.replace('#', '');\n const r = parseInt(h.slice(0, 2), 16);\n const g = parseInt(h.slice(2, 4), 16);\n const b = parseInt(h.slice(4, 6), 16);\n return (r * 299 + g * 587 + b * 114) / 1000 > 140;\n}\n\nfunction esc(s: string): string {\n return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\n}\n\nfunction escHtml(s: string): string {\n return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA,SAAS,eAAe;AACxB,SAAS,cAAc,kBAAkB;AACzC,SAAS,SAAS,YAAY;AAC9B,SAAS,qBAAqB;;;ACH9B,YAAYA,QAAO;AACnB,SAAS,qBAAqB;AAC9B,SAAS,eAAe;;;ACFxB,YAAY,OAAO;AACnB,OAAO,QAAQ;AAuBf,eAAsB,aAAoC;AACxD,EAAE,QAAM,GAAG,GAAG,KAAK,cAAc,CAAC,0DAAqD;AAGvF,QAAM,OAAO,eAAe;AAC5B,QAAM,aAAa,CAAC,GAAG,IAAI,IAAI,KAAK,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AAG3D,QAAM,WAAW,MAAQ,SAAO;AAAA,IAC9B,SAAS;AAAA,IACT,SAAS,WAAW,IAAI,CAAC,QAAQ;AAC/B,YAAM,QAAQ,KAAK,OAAO,CAAC,MAAM,EAAE,aAAa,GAAG,EAAE;AACrD,aAAO,EAAE,OAAO,KAAK,OAAO,GAAG,GAAG,IAAI,MAAM,GAAG,KAAK,cAAc;AAAA,IACpE,CAAC;AAAA,EACH,CAAC;AACD,MAAM,WAAS,QAAQ,GAAG;AAAE,IAAE,SAAO,YAAY;AAAG,YAAQ,KAAK,CAAC;AAAA,EAAG;AAGrE,QAAM,eAAe,KAAK,OAAO,CAAC,MAAM,EAAE,aAAa,QAAQ;AAC/D,QAAM,QAAQ,MAAQ,SAAO;AAAA,IAC3B,SAAS;AAAA,IACT,SAAS,aAAa,IAAI,CAAC,OAAO;AAAA,MAChC,OAAO,EAAE;AAAA,MACT,OAAO,GAAG,EAAE,IAAI;AAAA,MAChB,MAAM,GAAG,EAAE,YAAY;AAAA,IACzB,EAAE;AAAA,EACJ,CAAC;AACD,MAAM,WAAS,KAAK,GAAG;AAAE,IAAE,SAAO,YAAY;AAAG,YAAQ,KAAK,CAAC;AAAA,EAAG;AAElE,QAAM,YAAY,cAAc,KAAe;AAE/C,EAAE,MAAI,KAAK,GAAG,GAAG,KAAK,UAAU,IAAI,CAAC,SAAS;AAC9C,EAAE,MAAI,KAAK,cAAc,GAAG,KAAK,UAAU,OAAO,OAAO,CAAC,IAAI,UAAU,OAAO,WAAW,EAAE;AAC5F,EAAE,MAAI,KAAK,WAAW,UAAU,WAAW,OAAO,EAAE;AACpD,EAAE,MAAI,KAAK,aAAa,UAAU,MAAM,EAAE;AAC1C,MAAI,UAAU,MAAM;AAClB,IAAE,MAAI,KAAK,WAAW,UAAU,KAAK,MAAM,GAAG,GAAG,CAAC,KAAK;AAAA,EACzD;AAGA,QAAM,OAAO,MAAQ,SAAO;AAAA,IAC1B,SAAS;AAAA,IACT,SAAS;AAAA,MACP,EAAE,OAAO,SAAS,OAAO,aAAa,MAAM,wCAAwC;AAAA,MACpF,EAAE,OAAO,cAAc,OAAO,aAAa,MAAM,qCAAqC;AAAA,IACxF;AAAA,EACF,CAAC;AACD,MAAM,WAAS,IAAI,GAAG;AAAE,IAAE,SAAO,YAAY;AAAG,YAAQ,KAAK,CAAC;AAAA,EAAG;AAEjE,MAAI,SAAS,SAAS;AACpB,UAAM,WAAW,MAAQ,UAAQ;AAAA,MAC/B,SAAS;AAAA,MACT,cAAc;AAAA,IAChB,CAAC;AACD,QAAM,WAAS,QAAQ,GAAG;AAAE,MAAE,SAAO,YAAY;AAAG,cAAQ,KAAK,CAAC;AAAA,IAAG;AAErE,WAAO;AAAA,MACL;AAAA,MACA,WAAW,EAAE,SAAS;AAAA,MACtB,YAAY;AAAA,IACd;AAAA,EACF;AAGA,QAAM,YAAY,MAAQ;AAAA,IACxB;AAAA,MACE,cAAc,MACV,OAAK;AAAA,QACL,SAAS,2BAA2B,UAAU,OAAO,OAAO;AAAA,QAC5D,aAAa;AAAA,QACb,UAAU,CAAC,QAAQ;AACjB,cAAI,OAAO,CAAC,oBAAoB,KAAK,GAAG,EAAG,QAAO;AAAA,QACpD;AAAA,MACF,CAAC;AAAA,MAEH,YAAY,MACR,SAAO;AAAA,QACP,SAAS,0BAA0B,UAAU,WAAW,OAAO;AAAA,QAC/D,SAAS;AAAA,UACP,EAAE,OAAO,IAAI,OAAO,SAAS,UAAU,WAAW,OAAO,KAAK,MAAM,YAAY;AAAA,UAChF,EAAE,OAAO,SAAS,OAAO,SAAS,MAAM,8BAA8B;AAAA,UACtE,EAAE,OAAO,aAAa,OAAO,aAAa,MAAM,kBAAkB;AAAA,UAClE,EAAE,OAAO,kBAAkB,OAAO,kBAAkB,MAAM,uBAAuB;AAAA,UACjF,EAAE,OAAO,SAAS,OAAO,SAAS,MAAM,uBAAwB;AAAA,QAClE;AAAA,MACF,CAAC;AAAA,MAEH,eAAe,MACX,SAAO;AAAA,QACP,SAAS,4BAA4B,UAAU,WAAW,aAAa;AAAA,QACvE,SAAS;AAAA,UACP,EAAE,OAAO,IAAI,OAAO,QAAQ,UAAU,WAAW,aAAa,IAAI,MAAM,YAAY;AAAA,UACpF,EAAE,OAAO,OAAO,OAAO,oBAAe,MAAM,mCAAmC;AAAA,UAC/E,EAAE,OAAO,OAAO,OAAO,qBAAgB;AAAA,UACvC,EAAE,OAAO,OAAO,OAAO,qBAAgB,MAAM,oBAAoB;AAAA,UACjE,EAAE,OAAO,OAAO,OAAO,uBAAkB,MAAM,kBAAkB;AAAA,UACjE,EAAE,OAAO,OAAO,OAAO,mBAAc,MAAM,iBAAiB;AAAA,QAC9D;AAAA,MACF,CAAC;AAAA,MAEH,cAAc,MACV,SAAO;AAAA,QACP,SAAS,2BAA2B,UAAU,MAAM;AAAA,QACpD,SAAS;AAAA,UACP,EAAE,OAAO,IAAI,OAAO,SAAS,UAAU,MAAM,KAAK,MAAM,YAAY;AAAA,UACpE,EAAE,OAAO,OAAO,OAAO,eAAe,MAAM,qBAAqB;AAAA,UACjE,EAAE,OAAO,OAAO,OAAO,eAAe,MAAM,eAAe;AAAA,UAC3D,EAAE,OAAO,OAAO,OAAO,kBAAkB,MAAM,WAAW;AAAA,UAC1D,EAAE,OAAO,OAAO,OAAO,qBAAqB,MAAM,WAAW;AAAA,UAC7D,EAAE,OAAO,QAAQ,OAAO,kBAAkB,MAAM,qBAAqB;AAAA,UACrE,EAAE,OAAO,UAAU,OAAO,QAAQ,MAAM,gBAAgB;AAAA,QAC1D;AAAA,MACF,CAAC;AAAA,MAEH,UAAU,MACN,UAAQ;AAAA,QACR,SAAS;AAAA,QACT,cAAc;AAAA,MAChB,CAAC;AAAA,MAEH,iBAAiB,MACb,OAAK;AAAA,QACL,SAAS;AAAA,QACT,aAAa;AAAA,MACf,CAAC;AAAA,IACL;AAAA,IACA;AAAA,MACE,UAAU,MAAM;AAAE,QAAE,SAAO,YAAY;AAAG,gBAAQ,KAAK,CAAC;AAAA,MAAG;AAAA,IAC7D;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA,WAAW;AAAA,MACT,cAAc,UAAU,gBAAgB;AAAA,MACxC,YAAY,UAAU,cAAc;AAAA,MACpC,eAAe,UAAU,iBAAiB;AAAA,MAC1C,cAAc,UAAU,gBAAgB;AAAA,MACxC,UAAU,UAAU;AAAA,MACpB,iBAAiB,UAAU,mBAAmB;AAAA,IAChD;AAAA,IACA,YAAY;AAAA,EACd;AACF;;;AC3JO,SAAS,oBAAoB,MAA2B;AAC7D,QAAM;AAAA,IACJ;AAAA,IAAM;AAAA,IAAS;AAAA,IAAS;AAAA,IAAY;AAAA,IAAY;AAAA,IAAM;AAAA,IACtD;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAU;AAAA,EAC5B,IAAI;AAEJ,QAAM,YAAY,QAAQ,UAAU;AACpC,QAAM,QAAQ,mBAAmB,OAAO;AACxC,QAAM,WAAW,WAAW,WAAW,SAAS;AAChD,QAAM,cAAc,OAAO;AAC3B,QAAM,SAAS,OAAO;AACtB,QAAM,QAAQ,OAAO;AACrB,QAAM,QAAQ,OAAO;AAGrB,QAAM,SAAS,SAAS,SAAS,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC;AACnD,QAAM,SAAS;AACf,QAAM,aAAa,SAAS,SAAS,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE;AAExD,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA,SAKA,IAAI,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,YAKN,UAAU;AAAA,YACV,UAAU;AAAA,iBACL,OAAO;AAAA,oBACJ,mBAAmB,OAAO,CAAC;AAAA,gBAC/B,MAAM;AAAA,eACP,KAAK;AAAA,kBACF,QAAQ,YAAY,EAAE,CAAC;AAAA,gBACzB,WAAW;AAAA;AAAA,cAEb,YAAY,YAAY,QAAQ,YAAY,CAAC,CAAC;AAAA,gBAC5C,QAAQ;AAAA;AAAA;AAAA;AAAA,YAIZ,MAAM;AAAA,YACN,MAAM;AAAA,cACJ,QAAQ,QAAQ,CAAC,CAAC;AAAA,eACjB,SAAS,SAAS,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC;AAAA,kBACnC,OAAO,QAAQ,EAAE,CAAC;AAAA,gBACpB,UAAU;AAAA;AAAA;AAAA;AAAA,oBAIN,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oCAgBY,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wCAmBT,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAoMzC,IAAI,IAAI,CAAC;AAAA,kCACa,IAAI,OAAO,CAAC;AAAA;AAAA;AAAA,QAGtC,WAAW,4FAAqF,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUxG,OAAO,QAAQ,KAAK,EAAE,IAAI,CAAC,CAAC,MAAM,GAAG,MACrC,mDAAmD,GAAG,UAAU,QAAQ,GAAG,IAAI,SAAS,MAAM,oBAAoB,GAAG;AAAA,4BAC3F,IAAI,4BAA4B,GAAG;AAAA,aAClD,EAAE,KAAK,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQvB;AAAA,IACA,EAAE,GAAG,WAAW,IAAI,SAAS,GAAG,YAAY;AAAA,IAC5C,EAAE,GAAG,UAAU,IAAI,QAAQ,GAAG,WAAW;AAAA,IACzC,EAAE,GAAG,SAAS,IAAI,OAAO,GAAG,UAAU;AAAA,IACtC,EAAE,GAAG,eAAe,IAAI,WAAW,GAAG,gBAAgB;AAAA,IACtD,EAAE,GAAG,cAAc,IAAI,YAAY,GAAG,eAAe;AAAA,IACrD,EAAE,GAAG,cAAc,IAAI,YAAY,GAAG,eAAe;AAAA,IACrD,EAAE,GAAG,UAAU,IAAI,aAAa,GAAG,WAAW;AAAA,IAC9C,EAAE,GAAG,QAAQ,IAAI,YAAY,YAAY,QAAQ,YAAY,CAAC,GAAG,GAAG,SAAS;AAAA,EAC/E,EAAE,IAAI,CAAC,MAAM,0CAA0C,EAAE,EAAE;AAAA,4CACf,EAAE,EAAE,WAAW,EAAE,EAAE;AAAA,0CACrB,EAAE,CAAC,wBAAwB,EAAE,CAAC;AAAA,aAC3D,EAAE,KAAK,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQvB,MAAM,IAAI,CAAC,GAAG,MAAM,mFAAmF,CAAC,oCAAoC,CAAC,oBAAoB,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oEAUhI,aAAa;AAAA;AAAA;AAAA;AAAA,qEAIZ,aAAa;AAAA;AAAA;AAAA;AAAA,mEAIf,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qFAsBK,QAAQ,KAAK,QAAQ;AAAA,qFACrB,SAAS,QAAQ,IAAI,CAAC,OAAO,SAAS,QAAQ,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6BAuG3G,QAAQ,SAAS,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsC/C;AAEA,SAAS,QAAQ,KAAsB;AACrC,QAAM,IAAI,IAAI,QAAQ,KAAK,EAAE;AAC7B,QAAM,IAAI,SAAS,EAAE,MAAM,GAAG,CAAC,GAAG,EAAE;AACpC,QAAM,IAAI,SAAS,EAAE,MAAM,GAAG,CAAC,GAAG,EAAE;AACpC,QAAM,IAAI,SAAS,EAAE,MAAM,GAAG,CAAC,GAAG,EAAE;AACpC,UAAQ,IAAI,MAAM,IAAI,MAAM,IAAI,OAAO,MAAO;AAChD;AAEA,SAAS,IAAI,GAAmB;AAC9B,SAAO,EAAE,QAAQ,MAAM,OAAO,EAAE,QAAQ,MAAM,MAAM,EAAE,QAAQ,MAAM,MAAM,EAAE,QAAQ,MAAM,QAAQ;AACpG;AAEA,SAAS,QAAQ,GAAmB;AAClC,SAAO,EAAE,QAAQ,MAAM,OAAO,EAAE,QAAQ,MAAM,MAAM,EAAE,QAAQ,MAAM,MAAM;AAC5E;;;AFnhBA,eAAsB,MAAM;AAC1B,QAAM,EAAE,WAAW,WAAW,WAAW,IAAI,MAAM,WAAW;AAE9D,QAAM,IAAM,WAAQ;AACpB,IAAE,MAAM,mCAAmC,UAAU,IAAI,KAAK;AAG9D,QAAM,EAAE,UAAU,WAAW,YAAY,IAAI,eAAe,WAAW,WAAW,UAAU;AAG5F,cAAY,YAAY;AACxB,cAAY,WAAW;AAGvB,QAAM,OAAO,oBAAoB,WAAW;AAG5C,QAAM,YAAY,QAAQ,IAAI;AAC9B,QAAM,SAAS,QAAQ,WAAW,WAAW;AAC7C,QAAM,WAAW,QAAQ,WAAW,qBAAqB;AAEzD,gBAAc,QAAQ,UAAU,OAAO;AACvC,gBAAc,UAAU,MAAM,OAAO;AAErC,IAAE,KAAK,OAAO;AAEd,EAAE,OAAI,QAAQ,0BAAqB,MAAM,EAAE;AAC3C,EAAE,OAAI,QAAQ,0BAAqB,QAAQ,EAAE;AAC7C,EAAE,SAAM,yEAAyE;AACnF;;;AD9BA,SAAS,qBAA6B;AACpC,MAAI,MAAM,QAAQ,cAAc,YAAY,GAAG,CAAC;AAChD,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,MAAM,KAAK,KAAK,cAAc;AACpC,QAAI,WAAW,GAAG,GAAG;AACnB,UAAI;AACF,eAAO,KAAK,MAAM,aAAa,KAAK,MAAM,CAAC,EAAE,WAAW;AAAA,MAC1D,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AACA,UAAM,SAAS,QAAQ,GAAG;AAC1B,QAAI,WAAW,IAAK;AACpB,UAAM;AAAA,EACR;AACA,SAAO;AACT;AAEA,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,cAAc,EACnB,YAAY,kEAAkE,EAC9E,QAAQ,mBAAmB,CAAC,EAC5B,yBAAyB,IAAI,EAC7B,mBAAmB,IAAI;AAE1B,QACG,QAAQ,UAAU,EAClB,YAAY,4CAA4C,EACxD,OAAO,mBAAmB,0CAA0C,EACpE,OAAO,OAAO,SAA8B;AAC3C,MAAI,KAAK,QAAQ;AAEf,UAAM,EAAE,eAAAC,eAAc,IAAI,MAAM,OAAO,IAAI;AAC3C,UAAM,EAAE,SAAAC,SAAQ,IAAI,MAAM,OAAO,MAAM;AAIvC,QAAI,MAAM,KAAK,OAAO,QAAQ,MAAM,GAAG,EAAE,QAAQ,MAAM,GAAG;AAC1D,WAAO,IAAI,SAAS,EAAG,QAAO;AAC9B,UAAM,UAAU,OAAO,KAAK,KAAK,QAAQ,EAAE,SAAS,OAAO;AAC3D,UAAM,QAAQ,QAAQ,MAAM,GAAG;AAE/B,UAAM,QAAQ,MAAM,CAAC,KAAK;AAC1B,UAAM,YAAY;AAAA,MAChB,cAAc,MAAM,CAAC,KAAK;AAAA,MAC1B,YAAY,MAAM,CAAC,KAAK;AAAA,MACxB,eAAe,MAAM,CAAC,KAAK;AAAA,MAC3B,cAAc,MAAM,CAAC,KAAK;AAAA,MAC1B,UAAU,MAAM,CAAC,MAAM;AAAA,IACzB;AACA,UAAM,aAAa,MAAM,CAAC,IAAI,MAAM,CAAC,EAAE,MAAM,GAAG,EAAE,OAAO,OAAO,IAAI,CAAC;AAKrE,UAAM,aAAqC,CAAC;AAC5C,QAAI,MAAM,CAAC,GAAG;AACZ,iBAAW,QAAQ,MAAM,CAAC,EAAE,MAAM,GAAG,GAAG;AACtC,cAAM,CAAC,GAAG,CAAC,IAAI,KAAK,MAAM,GAAG;AAC7B,YAAI,KAAK,EAAG,YAAW,CAAC,IAAI;AAAA,MAC9B;AAAA,IACF;AAGA,UAAM,EAAE,eAAAC,eAAc,IAAI,MAAM,OAAO,iCAAiC;AACxE,UAAM,EAAE,gBAAAC,gBAAe,IAAI,MAAM,OAAO,2BAA2B;AAEnE,UAAM,MAAMD,eAAc,KAAK;AAC/B,UAAM,OAAQ,UAAU,gBAAgB,UAAU,aAAc,eAAwB;AACxF,UAAM,EAAE,UAAU,WAAW,YAAY,IAAIC,gBAAe,KAAK;AAAA,MAC/D,cAAc,UAAU;AAAA,MACxB,YAAY,UAAU;AAAA,MACtB,eAAe,UAAU;AAAA,MACzB,cAAc,UAAU;AAAA,MACxB,UAAU,UAAU;AAAA,IACtB,GAAG,MAAM,UAAU;AAEnB,gBAAY,YAAY;AACxB,gBAAY,WAAW;AAEvB,UAAM,YAAY,QAAQ,IAAI;AAC9B,UAAM,SAASF,SAAQ,WAAW,WAAW;AAC7C,IAAAD,eAAc,QAAQ,UAAU,OAAO;AAEvC,YAAQ,IAAI,gDAA2C,MAAM,EAAE;AAC/D,YAAQ,IAAI,eAAe,IAAI,IAAI,EAAE;AACrC,QAAI,UAAU,aAAc,SAAQ,IAAI,cAAc,UAAU,YAAY,EAAE;AAC9E,QAAI,UAAU,WAAY,SAAQ,IAAI,WAAW,UAAU,UAAU,EAAE;AACvE,QAAI,UAAU,cAAe,SAAQ,IAAI,aAAa,UAAU,aAAa,EAAE;AAC/E,QAAI,UAAU,aAAc,SAAQ,IAAI,aAAa,UAAU,YAAY,EAAE;AAC7E,QAAI,UAAU,SAAU,SAAQ,IAAI,uBAAuB;AAAA,EAC7D,OAAO;AACL,UAAM,IAAI;AAAA,EACZ;AACF,CAAC;AAEH,QACG,QAAQ,gBAAgB,EACxB,YAAY,kFAAkF,EAC9F,OAAO,gBAAgB,gCAAgC,EACvD,OAAO,qBAAqB,8DAA8D,EAC1F,OAAO,WAAW,sDAAsD,EACxE;AAAA,EACC,OAAO,SAA8D;AACnE,UAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,+BAA8B;AACxE,UAAM,cAAc,CAAC,eAAe,SAAS,UAAU;AAEvD,UAAM,SAAS,KAAK,QACf,KAAK,MAAM;AAAA,MAAO,CAAC,MACjB,YAAkC,SAAS,CAAC;AAAA,IAC/C,IACA;AACJ,UAAM,OAAO,MAAM,iBAAiB;AAAA,MAClC,KAAK,KAAK;AAAA,MACV;AAAA,MACA,OAAO,KAAK;AAAA,IACd,CAAC;AACD,QAAI,SAAS,EAAG,SAAQ,KAAK,IAAI;AAAA,EACnC;AACF;AAEF,IAAM,eAAe,QAClB,QAAQ,WAAW,EACnB,YAAY,mCAAmC;AAElD,aACG,QAAQ,MAAM,EACd,YAAY,gCAAgC,EAC5C,OAAO,YAAY;AAClB,QAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,0BAAyB;AACnE,UAAQ,KAAK,iBAAiB,CAAC;AACjC,CAAC;AAEH,aACG,QAAQ,WAAW,EACnB,YAAY,8CAA8C,EAC1D,OAAO,OAAO,OAAe;AAC5B,QAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,0BAAyB;AACnE,UAAQ,KAAK,iBAAiB,EAAE,CAAC;AACnC,CAAC;AAEH,IAAM,UAAU,QACb,QAAQ,MAAM,EACd,YAAY,4DAA4D;AAE3E,QACG,QAAQ,4BAA4B,EACpC,YAAY,qDAAqD,EACjE,OAAO,aAAa,6BAA6B,GAAG,EACpD,OAAO,UAAU,4BAA4B,EAC7C;AAAA,EACC,OAAO,WAAqB,SAA2C;AACrE,UAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,qBAAoB;AAC9D,UAAM,OAAO,iBAAiB;AAAA,MAC5B,aAAa,UAAU,KAAK,GAAG;AAAA,MAC/B,MAAM,KAAK,MAAM,OAAO,KAAK,GAAG,IAAI;AAAA,MACpC,MAAM,KAAK;AAAA,IACb,CAAC;AACD,QAAI,SAAS,EAAG,SAAQ,KAAK,IAAI;AAAA,EACnC;AACF;AAEF,QACG,QAAQ,SAAS,EACjB,YAAY,gGAAgG,EAC5G,eAAe,cAAc,wCAAwC,EACrE,eAAe,wBAAwB,qBAAqB,EAC5D,OAAO,gBAAgB,gCAAgC,EACvD,OAAO,mBAAmB,iCAAiC,yBAAyB,EACpF,OAAO,UAAU,4BAA4B,EAC7C,OAAO,OAAO,SAA8F;AAC3G,QAAM,EAAE,eAAe,IAAI,MAAM,OAAO,qBAAoB;AAC5D,QAAM,OAAO,eAAe,IAAI;AAChC,MAAI,SAAS,EAAG,SAAQ,KAAK,IAAI;AACnC,CAAC;AAEH,QACG,QAAQ,OAAO,EACf,YAAY,qDAAqD,EACjE,OAAO,gBAAgB,gCAAgC,EACvD,OAAO,SAAS,8CAA8C,EAC9D,OAAO,gBAAgB,uDAAuD,EAC9E,OAAO,eAAe,iBAAiB,EACvC,OAAO,uBAAuB,uBAAuB,EACrD,OAAO,wBAAwB,4CAA4C,EAC3E,OAAO,mBAAmB,sBAAsB,EAChD,OAAO,kBAAkB,qEAAqE,EAC9F,OAAO,OAAO,SAA2C;AACxD,QAAM,EAAE,SAAS,IAAI,MAAM,OAAO,sBAAqB;AACvD,QAAM,OAAO,MAAM,SAAS,IAAa;AACzC,MAAI,SAAS,EAAG,SAAQ,KAAK,IAAI;AACnC,CAAC;AAEH,QACG,QAAQ,oBAAoB,EAC5B,YAAY,8DAA8D,EAC1E,OAAO,gBAAgB,gCAAgC,EACvD,OAAO,mBAAmB,gDAAgD,EAC1E,OAAO,kBAAkB,wCAAwC,EACjE,OAAO,oBAAoB,0BAA0B,EACrD;AAAA,EACC,OACE,WACA,SAMG;AACH,UAAM,EAAE,YAAY,IAAI,MAAM,OAAO,yBAAwB;AAC7D,UAAM,OAAO,MAAM,YAAY,UAAU,KAAK,GAAG,GAAG,IAAa;AACjE,QAAI,SAAS,EAAG,SAAQ,KAAK,IAAI;AAAA,EACnC;AACF;AAEF,QACG,QAAQ,MAAM,EACd,YAAY,gFAAgF,EAC5F,OAAO,gBAAgB,gCAAgC,EACvD,OAAO,WAAW,mCAAmC,EACrD,OAAO,WAAW,mDAAmD,EACrE,OAAO,OAAO,SAA6D;AAC1E,QAAM,EAAE,QAAQ,IAAI,MAAM,OAAO,qBAAoB;AACrD,QAAM,OAAO,MAAM,QAAQ,IAAI;AAC/B,MAAI,SAAS,EAAG,SAAQ,KAAK,IAAI;AACnC,CAAC;AAEH,QACG,QAAQ,SAAS,EACjB,YAAY,8CAA8C,EAC1D,OAAO,YAAY;AAClB,QAAM,EAAE,YAAAI,YAAW,IAAI,MAAM,OAAO,IAAI;AACxC,QAAM,EAAE,SAAAH,SAAQ,IAAI,MAAM,OAAO,MAAM;AACvC,QAAM,cAAcA,SAAQ,QAAQ,IAAI,GAAG,qBAAqB;AAEhE,MAAI,CAACG,YAAW,WAAW,GAAG;AAC5B,YAAQ,MAAM,kEAAkE;AAChF,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,EAAE,KAAK,IAAI,MAAM,OAAO,eAAe;AAC7C,QAAM,WAAW,QAAQ;AACzB,QAAM,MAAM,aAAa,WAAW,SAAS,aAAa,UAAU,UAAU;AAC9E,OAAK,GAAG,GAAG,KAAK,WAAW,GAAG;AAC9B,UAAQ,IAAI,WAAW,WAAW,KAAK;AACzC,CAAC;AAEH,QAAQ,MAAM;","names":["p","writeFileSync","resolve","loadReference","applyOverrides","existsSync"]}
1
+ {"version":3,"sources":["../../bin/oh-my-design.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { readFileSync, existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nfunction readPackageVersion(): string {\n let cur = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 8; i++) {\n const pkg = join(cur, 'package.json');\n if (existsSync(pkg)) {\n try {\n return JSON.parse(readFileSync(pkg, 'utf8')).version ?? '0.0.0';\n } catch {\n return '0.0.0';\n }\n }\n const parent = dirname(cur);\n if (parent === cur) break;\n cur = parent;\n }\n return '0.0.0';\n}\n\nconst program = new Command();\n\nprogram\n .name('oh-my-design')\n .description('Bootstrap oh-my-design skills + agents into your project. After install, talk to your agent in natural language — no other CLI commands.')\n .version(readPackageVersion())\n .showSuggestionAfterError(true)\n .showHelpAfterError(true);\n\nprogram\n .command('install-skills')\n .description('Install omd skill files + canonical agents into agent directories (.claude/, .codex/, .opencode/)')\n .option('--dir <path>', 'Project root (defaults to cwd)')\n .option('--agent <name...>', 'Restrict to specific agents (claude-code | codex | opencode)')\n .option('--force', 'Overwrite existing files even without the omd marker')\n .action(\n async (opts: { dir?: string; agent?: string[]; force?: boolean }) => {\n const { runInstallSkills } = await import('../src/cli/install-skills.js');\n const validAgents = ['claude-code', 'codex', 'opencode'] as const;\n type Agent = (typeof validAgents)[number];\n const agents = opts.agent\n ? (opts.agent.filter((a): a is Agent =>\n (validAgents as readonly string[]).includes(a)\n ) as Agent[])\n : undefined;\n const code = await runInstallSkills({\n dir: opts.dir,\n agents,\n force: opts.force,\n });\n if (code !== 0) process.exit(code);\n }\n );\n\nprogram.parse();\n"],"mappings":";;;AAAA,SAAS,eAAe;AACxB,SAAS,cAAc,kBAAkB;AACzC,SAAS,SAAS,YAAY;AAC9B,SAAS,qBAAqB;AAE9B,SAAS,qBAA6B;AACpC,MAAI,MAAM,QAAQ,cAAc,YAAY,GAAG,CAAC;AAChD,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,MAAM,KAAK,KAAK,cAAc;AACpC,QAAI,WAAW,GAAG,GAAG;AACnB,UAAI;AACF,eAAO,KAAK,MAAM,aAAa,KAAK,MAAM,CAAC,EAAE,WAAW;AAAA,MAC1D,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AACA,UAAM,SAAS,QAAQ,GAAG;AAC1B,QAAI,WAAW,IAAK;AACpB,UAAM;AAAA,EACR;AACA,SAAO;AACT;AAEA,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,cAAc,EACnB,YAAY,+IAA0I,EACtJ,QAAQ,mBAAmB,CAAC,EAC5B,yBAAyB,IAAI,EAC7B,mBAAmB,IAAI;AAE1B,QACG,QAAQ,gBAAgB,EACxB,YAAY,mGAAmG,EAC/G,OAAO,gBAAgB,gCAAgC,EACvD,OAAO,qBAAqB,8DAA8D,EAC1F,OAAO,WAAW,sDAAsD,EACxE;AAAA,EACC,OAAO,SAA8D;AACnE,UAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,+BAA8B;AACxE,UAAM,cAAc,CAAC,eAAe,SAAS,UAAU;AAEvD,UAAM,SAAS,KAAK,QACf,KAAK,MAAM;AAAA,MAAO,CAAC,MACjB,YAAkC,SAAS,CAAC;AAAA,IAC/C,IACA;AACJ,UAAM,OAAO,MAAM,iBAAiB;AAAA,MAClC,KAAK,KAAK;AAAA,MACV;AAAA,MACA,OAAO,KAAK;AAAA,IACd,CAAC;AACD,QAAI,SAAS,EAAG,SAAQ,KAAK,IAAI;AAAA,EACnC;AACF;AAEF,QAAQ,MAAM;","names":[]}
@@ -0,0 +1,442 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli/install-skills.ts
4
+ import * as p from "@clack/prompts";
5
+ import pc from "picocolors";
6
+ import {
7
+ readFileSync,
8
+ readdirSync,
9
+ writeFileSync,
10
+ existsSync as existsSync2,
11
+ mkdirSync
12
+ } from "fs";
13
+ import { join as join2, dirname, relative } from "path";
14
+ import { fileURLToPath } from "url";
15
+
16
+ // src/core/agent-detect.ts
17
+ import { existsSync } from "fs";
18
+ import { join } from "path";
19
+ function detectInstalledAgents(projectRoot) {
20
+ return {
21
+ claudeCode: existsSync(join(projectRoot, ".claude")) || existsSync(join(projectRoot, "CLAUDE.md")),
22
+ codex: existsSync(join(projectRoot, ".codex")) || existsSync(join(projectRoot, "AGENTS.md")) || existsSync(join(projectRoot, "AGENTS.override.md")),
23
+ opencode: existsSync(join(projectRoot, ".opencode")) || existsSync(join(projectRoot, "opencode.json")) || existsSync(join(projectRoot, "opencode.jsonc")),
24
+ cursor: existsSync(join(projectRoot, ".cursor")) || existsSync(join(projectRoot, ".cursorrules"))
25
+ };
26
+ }
27
+
28
+ // src/cli/install-skills.ts
29
+ function findPackageRoot() {
30
+ let cur = dirname(fileURLToPath(import.meta.url));
31
+ for (let i = 0; i < 8; i++) {
32
+ if (existsSync2(join2(cur, "skills"))) return cur;
33
+ const parent = dirname(cur);
34
+ if (parent === cur) break;
35
+ cur = parent;
36
+ }
37
+ return null;
38
+ }
39
+ function listShippedSkills(packageRoot) {
40
+ const skillsDir = join2(packageRoot, "skills");
41
+ if (!existsSync2(skillsDir)) return [];
42
+ return readdirSync(skillsDir).filter((name) => existsSync2(join2(skillsDir, name, "SKILL.md"))).sort();
43
+ }
44
+ function listCanonicalAgents(packageRoot) {
45
+ const dir = join2(packageRoot, "agents");
46
+ if (!existsSync2(dir)) return [];
47
+ return readdirSync(dir).filter((name) => name.startsWith("omd-") && name.endsWith(".md")).sort();
48
+ }
49
+ function parseCanonicalAgent(packageRoot, filename) {
50
+ const src = readFileSync(join2(packageRoot, "agents", filename), "utf8");
51
+ const match = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/.exec(src);
52
+ if (!match) {
53
+ throw new Error(`agents/${filename}: missing YAML frontmatter`);
54
+ }
55
+ const fm = match[1];
56
+ const body = match[2];
57
+ const grab = (key) => {
58
+ const re = new RegExp(`^${key}:\\s*(.+)$`, "m");
59
+ const m = re.exec(fm);
60
+ return m ? m[1].trim().replace(/^["']|["']$/g, "") : "";
61
+ };
62
+ return {
63
+ name: grab("name") || filename.replace(/\.md$/, ""),
64
+ description: grab("description"),
65
+ tools: grab("tools").split(",").map((t) => t.trim()).filter(Boolean),
66
+ model: grab("model") || "sonnet",
67
+ body
68
+ };
69
+ }
70
+ function claudeToolsToCodex(tools) {
71
+ const m = {
72
+ Read: "read_file",
73
+ Write: "write_file",
74
+ Edit: "edit_file",
75
+ Bash: "shell",
76
+ Glob: "search",
77
+ Grep: "search",
78
+ WebFetch: "web_fetch",
79
+ WebSearch: "search",
80
+ Agent: "spawn_agent",
81
+ TaskCreate: "task",
82
+ TaskUpdate: "task",
83
+ TaskList: "task"
84
+ };
85
+ const out = /* @__PURE__ */ new Set();
86
+ for (const t of tools) out.add(m[t] ?? t.toLowerCase());
87
+ return [...out].sort();
88
+ }
89
+ function claudeModelToCodex(model) {
90
+ const m = {
91
+ haiku: "gpt-4.1-mini",
92
+ sonnet: "gpt-4.1",
93
+ opus: "gpt-4.1"
94
+ };
95
+ return m[model.toLowerCase()] ?? "gpt-4.1";
96
+ }
97
+ function renderClaudeAgent(a) {
98
+ const fm = [
99
+ "---",
100
+ `name: ${a.name}`,
101
+ `description: ${a.description}`,
102
+ `tools: ${a.tools.join(", ")}`,
103
+ `model: ${a.model}`,
104
+ `omd_managed: true`,
105
+ "---",
106
+ ""
107
+ ].join("\n");
108
+ return fm + a.body;
109
+ }
110
+ function renderCodexAgent(a) {
111
+ const tools = claudeToolsToCodex(a.tools);
112
+ const model = claudeModelToCodex(a.model);
113
+ const desc = a.description.replace(/"/g, '\\"');
114
+ return [
115
+ `[agent]`,
116
+ `name = "${a.name}"`,
117
+ `description = "${desc}"`,
118
+ `model = "${model}"`,
119
+ `max_threads = 1`,
120
+ `allowed_tools = [${tools.map((t) => `"${t}"`).join(", ")}]`,
121
+ "",
122
+ `instructions = """`,
123
+ `Source of truth: agents/${a.name}.md (canonical). The full role spec is`,
124
+ `mirrored to .claude/agents/${a.name}.md when installed for Claude Code.`,
125
+ `Follow that spec verbatim regardless of channel.`,
126
+ "",
127
+ `Codex notes:`,
128
+ `- Spawn sub-agents via spawn_agent with names matching .codex/agents/<name>.toml`,
129
+ `- Use shell to invoke CLI helpers (omd init prepare, omd remember, git apply, npx axe-core, npx lighthouse)`,
130
+ `- All artifacts go inside .omd/runs/run-<latest>/ (or skills/omd-lab-02-design-harness/runs/<lab-version>-...)`,
131
+ `"""`,
132
+ ""
133
+ ].join("\n");
134
+ }
135
+ function planForTarget(projectRoot, target) {
136
+ switch (target) {
137
+ case "claude-code":
138
+ return {
139
+ target,
140
+ destDir: join2(projectRoot, ".claude", "skills"),
141
+ layout: "folder"
142
+ };
143
+ case "codex":
144
+ return {
145
+ target,
146
+ destDir: join2(projectRoot, ".codex", "skills"),
147
+ layout: "folder"
148
+ };
149
+ case "opencode":
150
+ return {
151
+ target,
152
+ destDir: join2(projectRoot, ".opencode", "agents"),
153
+ layout: "flat"
154
+ };
155
+ }
156
+ }
157
+ var MANAGED_HEADER = "<!-- omd:installed-skill \u2014 managed by `omd install-skills`. Do not edit; rerun the command to refresh. -->";
158
+ function installOne(packageRoot, plan, skill, force) {
159
+ const src = readFileSync(
160
+ join2(packageRoot, "skills", skill, "SKILL.md"),
161
+ "utf8"
162
+ );
163
+ const managed = MANAGED_HEADER + "\n\n" + src;
164
+ const destPath = plan.layout === "folder" ? join2(plan.destDir, skill, "SKILL.md") : join2(plan.destDir, skill + ".md");
165
+ const exists = existsSync2(destPath);
166
+ const existing = exists ? readFileSync(destPath, "utf8") : "";
167
+ if (exists && existing === managed) {
168
+ return { target: plan.target, skill, destPath, status: "unchanged" };
169
+ }
170
+ if (exists && !existing.startsWith(MANAGED_HEADER) && !force) {
171
+ return { target: plan.target, skill, destPath, status: "skipped-drift" };
172
+ }
173
+ mkdirSync(dirname(destPath), { recursive: true });
174
+ writeFileSync(destPath, managed, "utf8");
175
+ return {
176
+ target: plan.target,
177
+ skill,
178
+ destPath,
179
+ status: exists ? "updated" : "created"
180
+ };
181
+ }
182
+ function installHookFile(packageRoot, projectRoot, filename, force) {
183
+ const target = "claude-code";
184
+ const skillLabel = `hook:${filename}`;
185
+ const srcPath = join2(packageRoot, ".claude", "hooks", filename);
186
+ const destPath = join2(projectRoot, ".claude", "hooks", filename);
187
+ if (!existsSync2(srcPath)) {
188
+ return { target, skill: skillLabel, destPath, status: "skipped-drift" };
189
+ }
190
+ const src = readFileSync(srcPath, "utf8");
191
+ const exists = existsSync2(destPath);
192
+ const existing = exists ? readFileSync(destPath, "utf8") : "";
193
+ if (exists && existing === src) {
194
+ return { target, skill: skillLabel, destPath, status: "unchanged" };
195
+ }
196
+ if (exists && !force) {
197
+ return { target, skill: skillLabel, destPath, status: "skipped-drift" };
198
+ }
199
+ mkdirSync(dirname(destPath), { recursive: true });
200
+ writeFileSync(destPath, src);
201
+ return { target, skill: skillLabel, destPath, status: exists ? "updated" : "created" };
202
+ }
203
+ function installSkillRules(packageRoot, projectRoot, force) {
204
+ const target = "claude-code";
205
+ const skillLabel = "rules:skill-rules.json";
206
+ const srcPath = join2(packageRoot, ".claude", "skills", "skill-rules.json");
207
+ const destPath = join2(projectRoot, ".claude", "skills", "skill-rules.json");
208
+ if (!existsSync2(srcPath)) {
209
+ return { target, skill: skillLabel, destPath, status: "skipped-drift" };
210
+ }
211
+ const src = readFileSync(srcPath, "utf8");
212
+ const exists = existsSync2(destPath);
213
+ const existing = exists ? readFileSync(destPath, "utf8") : "";
214
+ if (exists && existing === src) {
215
+ return { target, skill: skillLabel, destPath, status: "unchanged" };
216
+ }
217
+ if (exists && !force) {
218
+ return { target, skill: skillLabel, destPath, status: "skipped-drift" };
219
+ }
220
+ mkdirSync(dirname(destPath), { recursive: true });
221
+ writeFileSync(destPath, src);
222
+ return { target, skill: skillLabel, destPath, status: exists ? "updated" : "created" };
223
+ }
224
+ function installSettingsJson(packageRoot, projectRoot, force) {
225
+ const target = "claude-code";
226
+ const skillLabel = "settings:.claude/settings.json";
227
+ const srcPath = join2(packageRoot, ".claude", "settings.json");
228
+ const destPath = join2(projectRoot, ".claude", "settings.json");
229
+ if (!existsSync2(srcPath)) {
230
+ return { target, skill: skillLabel, destPath, status: "skipped-drift" };
231
+ }
232
+ const src = readFileSync(srcPath, "utf8");
233
+ const exists = existsSync2(destPath);
234
+ const existing = exists ? readFileSync(destPath, "utf8") : "";
235
+ if (exists && existing === src) {
236
+ return { target, skill: skillLabel, destPath, status: "unchanged" };
237
+ }
238
+ if (exists && !force) {
239
+ try {
240
+ const parsed = JSON.parse(existing);
241
+ if (typeof parsed._doc === "string" && parsed._doc.includes("OmD")) {
242
+ } else {
243
+ return { target, skill: skillLabel, destPath, status: "skipped-drift" };
244
+ }
245
+ } catch {
246
+ return { target, skill: skillLabel, destPath, status: "skipped-drift" };
247
+ }
248
+ }
249
+ mkdirSync(dirname(destPath), { recursive: true });
250
+ writeFileSync(destPath, src);
251
+ return { target, skill: skillLabel, destPath, status: exists ? "updated" : "created" };
252
+ }
253
+ function installDataFile(packageRoot, projectRoot, channelDir, dataFilename, force) {
254
+ const target = channelDir === ".claude" ? "claude-code" : "codex";
255
+ const skillLabel = `data:${dataFilename}`;
256
+ const srcPath = join2(packageRoot, "data", dataFilename);
257
+ const destPath = join2(projectRoot, channelDir, "data", dataFilename);
258
+ if (!existsSync2(srcPath)) {
259
+ return { target, skill: skillLabel, destPath, status: "skipped-drift" };
260
+ }
261
+ const src = readFileSync(srcPath, "utf8");
262
+ const exists = existsSync2(destPath);
263
+ const existing = exists ? readFileSync(destPath, "utf8") : "";
264
+ if (exists && existing === src) {
265
+ return { target, skill: skillLabel, destPath, status: "unchanged" };
266
+ }
267
+ if (exists && !force) {
268
+ return { target, skill: skillLabel, destPath, status: "skipped-drift" };
269
+ }
270
+ mkdirSync(dirname(destPath), { recursive: true });
271
+ writeFileSync(destPath, src, "utf8");
272
+ return {
273
+ target,
274
+ skill: skillLabel,
275
+ destPath,
276
+ status: exists ? "updated" : "created"
277
+ };
278
+ }
279
+ function installAgentFile(packageRoot, projectRoot, channel, filename, force) {
280
+ const target = channel === "claude" ? "claude-code" : "codex";
281
+ const skillLabel = `agent:${filename}`;
282
+ const parsed = parseCanonicalAgent(packageRoot, filename);
283
+ const rendered = channel === "claude" ? renderClaudeAgent(parsed) : renderCodexAgent(parsed);
284
+ const destFilename = channel === "claude" ? filename : filename.replace(/\.md$/, ".toml");
285
+ const destPath = join2(
286
+ projectRoot,
287
+ channel === "claude" ? ".claude" : ".codex",
288
+ "agents",
289
+ destFilename
290
+ );
291
+ const managed = channel === "claude" ? rendered : "# omd:installed-agent \u2014 generated from agents/" + filename + " by `omd install-skills`. Do not edit; rerun the command to refresh.\n\n" + rendered;
292
+ const exists = existsSync2(destPath);
293
+ const existing = exists ? readFileSync(destPath, "utf8") : "";
294
+ if (exists && existing === managed) {
295
+ return { target, skill: skillLabel, destPath, status: "unchanged" };
296
+ }
297
+ const isManaged = channel === "claude" ? /\nomd_managed:\s*true\b/.test(existing) : existing.startsWith("# omd:installed-agent");
298
+ if (exists && !isManaged && !force) {
299
+ return { target, skill: skillLabel, destPath, status: "skipped-drift" };
300
+ }
301
+ mkdirSync(dirname(destPath), { recursive: true });
302
+ writeFileSync(destPath, managed, "utf8");
303
+ return {
304
+ target,
305
+ skill: skillLabel,
306
+ destPath,
307
+ status: exists ? "updated" : "created"
308
+ };
309
+ }
310
+ var STATUS_LABEL = {
311
+ created: pc.green("created"),
312
+ updated: pc.cyan("updated"),
313
+ unchanged: pc.dim("unchanged"),
314
+ "skipped-drift": pc.yellow("skipped")
315
+ };
316
+ function autoDetectTargets(projectRoot) {
317
+ const presence = detectInstalledAgents(projectRoot);
318
+ const targets = [];
319
+ if (presence.claudeCode) targets.push("claude-code");
320
+ if (presence.codex) targets.push("codex");
321
+ if (presence.opencode) targets.push("opencode");
322
+ if (targets.length === 0) {
323
+ return ["claude-code", "codex", "opencode"];
324
+ }
325
+ return targets;
326
+ }
327
+ async function runInstallSkills(opts = {}) {
328
+ const projectRoot = opts.dir ?? process.cwd();
329
+ const packageRoot = findPackageRoot();
330
+ if (!packageRoot) {
331
+ console.error(pc.red("omd install-skills: package data not found"));
332
+ return 1;
333
+ }
334
+ const skills = listShippedSkills(packageRoot);
335
+ if (skills.length === 0) {
336
+ console.error(pc.red("omd install-skills: no skills found in package"));
337
+ return 1;
338
+ }
339
+ const targets = opts.agents ?? autoDetectTargets(projectRoot);
340
+ const plans = targets.map((t) => planForTarget(projectRoot, t));
341
+ const force = opts.force ?? false;
342
+ p.intro(
343
+ pc.bold("omd install-skills") + pc.dim(` (${relative(process.cwd(), projectRoot) || "."})`)
344
+ );
345
+ p.log.message(
346
+ pc.bold("Targets: ") + targets.map((t) => pc.cyan(t)).join(", ")
347
+ );
348
+ p.log.message(
349
+ pc.bold("Skills: ") + skills.map((s) => pc.cyan(s)).join(", ")
350
+ );
351
+ const results = [];
352
+ for (const plan of plans) {
353
+ for (const skill of skills) {
354
+ results.push(installOne(packageRoot, plan, skill, force));
355
+ }
356
+ }
357
+ const canonicalAgents = listCanonicalAgents(packageRoot);
358
+ for (const target of targets) {
359
+ if (target === "claude-code") {
360
+ for (const filename of canonicalAgents) {
361
+ results.push(installAgentFile(packageRoot, projectRoot, "claude", filename, force));
362
+ }
363
+ } else if (target === "codex") {
364
+ for (const filename of canonicalAgents) {
365
+ results.push(installAgentFile(packageRoot, projectRoot, "codex", filename, force));
366
+ }
367
+ }
368
+ }
369
+ const dataFiles = [
370
+ "reference-fingerprints.json",
371
+ "reference-tags.md",
372
+ "vocabulary.json",
373
+ "synonyms.json",
374
+ "opt-out-corpus.json"
375
+ ];
376
+ for (const target of targets) {
377
+ if (target === "claude-code") {
378
+ for (const dataFile of dataFiles) {
379
+ results.push(installDataFile(packageRoot, projectRoot, ".claude", dataFile, force));
380
+ }
381
+ } else if (target === "codex") {
382
+ for (const dataFile of dataFiles) {
383
+ results.push(installDataFile(packageRoot, projectRoot, ".codex", dataFile, force));
384
+ }
385
+ }
386
+ }
387
+ if (targets.includes("claude-code")) {
388
+ for (const hookFile of [
389
+ "skill-activation.cjs",
390
+ "session-state-loader.cjs",
391
+ "post-edit-watch.cjs",
392
+ "session-end-foldin.cjs"
393
+ ]) {
394
+ results.push(installHookFile(packageRoot, projectRoot, hookFile, force));
395
+ }
396
+ results.push(installSkillRules(packageRoot, projectRoot, force));
397
+ results.push(installSettingsJson(packageRoot, projectRoot, force));
398
+ }
399
+ p.log.message(pc.bold("\nResults:"));
400
+ for (const r of results) {
401
+ const rel = relative(projectRoot, r.destPath);
402
+ p.log.message(
403
+ ` ${STATUS_LABEL[r.status]} ${pc.dim(r.target.padEnd(12))} ${rel}`
404
+ );
405
+ }
406
+ const driftCount = results.filter((r) => r.status === "skipped-drift").length;
407
+ const writtenCount = results.filter(
408
+ (r) => r.status === "created" || r.status === "updated"
409
+ ).length;
410
+ if (driftCount > 0) {
411
+ p.outro(
412
+ pc.yellow(
413
+ `${writtenCount} written, ${driftCount} skipped (existing files lack the omd marker \u2014 rerun with --force to overwrite).`
414
+ )
415
+ );
416
+ return 0;
417
+ }
418
+ const nextSteps = [
419
+ `${pc.bold("Open Claude Code (or Codex). Just say what you want:")}`,
420
+ "",
421
+ ` ${pc.dim('"\uD1A0\uC2A4 \uC2A4\uD0C0\uC77C \uAC00\uC871 \uC2DD\uB2E8 \uACF5\uC720 \uC571 \uBA54\uC778 \uD654\uBA74 \uB514\uC790\uC778\uD574\uC918"')}`,
422
+ ` ${pc.dim('"Linear-clone B2B SaaS dashboard \uB9CC\uB4E4\uACE0 \uC2F6\uC5B4"')}`,
423
+ ` ${pc.dim('"\uC774 \uCE74\uB4DC \uC880 \uB354 \uC138\uB828\uB418\uAC8C"')} ${pc.dim("# \uC791\uC5C5 \uC911 \uC790\uC5F0\uC5B4 \u2014 \uC790\uB3D9 hook \uB77C\uC6B0\uD305")}`,
424
+ "",
425
+ `${pc.bold("Hook\uC774 \uC790\uB3D9\uC73C\uB85C \uB77C\uC6B0\uD305")} ${pc.dim("\u2014 \uB514\uC790\uC778 \uC758\uB3C4 \uAC10\uC9C0\uD574\uC11C \uD558\uB124\uC2A4/\uC2A4\uD0AC \uD638\uCD9C. \uC2AC\uB798\uC2DC \uBA85\uB839 \uC548 \uCCD0\uB3C4 \uB428.")}`,
426
+ "",
427
+ `${pc.dim("Power user shortcut: ")}${pc.cyan("/omd-harness <task>")} ${pc.dim("\u2014 hook \uC6B0\uD68C, \uC989\uC2DC \uC9C4\uC785.")}`,
428
+ "",
429
+ `${pc.yellow("\u26A0 Already-running Claude Code session?")} ${pc.dim("Run `/agents` inside the session to reload \u2014 or quit (Cmd+Q on macOS) and relaunch. Without reload, hooks/agents do not load.")}`
430
+ ].join("\n");
431
+ p.note(nextSteps, "Next");
432
+ p.outro(
433
+ pc.green(
434
+ `Done. 6 skills \xB7 11 sub-agents \xB7 4 hooks installed (${writtenCount} files).`
435
+ )
436
+ );
437
+ return 0;
438
+ }
439
+ export {
440
+ runInstallSkills
441
+ };
442
+ //# sourceMappingURL=install-skills-SVIYKXOE.js.map