oh-my-design-cli 0.1.0 → 0.1.1

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.
@@ -730,8 +730,8 @@ function readPackageVersion() {
730
730
  return "0.0.0";
731
731
  }
732
732
  var program = new Command();
733
- program.name("oh-my-design").description("Interactive CLI to generate DESIGN.md files for AI coding agents").version(readPackageVersion());
734
- program.command("generate", { isDefault: true }).description("Generate DESIGN.md and interactive preview").option("--config <hash>", "Apply a config hash from the web builder").action(async (opts) => {
733
+ program.name("oh-my-design").description("Interactive CLI to generate DESIGN.md files for AI coding agents").version(readPackageVersion()).showSuggestionAfterError(true).showHelpAfterError(true);
734
+ program.command("generate").description("Generate DESIGN.md and interactive preview").option("--config <hash>", "Apply a config hash from the web builder").action(async (opts) => {
735
735
  if (opts.config) {
736
736
  const { writeFileSync: writeFileSync2 } = await import("fs");
737
737
  const { resolve: resolve2 } = await import("path");
@@ -809,7 +809,7 @@ referenceCmd.command("show <id>").description("Print the full reference DESIGN.m
809
809
  var initCmd = program.command("init").description("Bootstrap DESIGN.md from a reference + project description");
810
810
  initCmd.command("recommend <description...>").description("Recommend references matching a project description").option("--top <n>", "Number of recommendations", "5").option("--json", "Emit machine-readable JSON").action(
811
811
  async (descParts, opts) => {
812
- const { runInitRecommend } = await import("../init-STACB7E5.js");
812
+ const { runInitRecommend } = await import("../init-UMM4XIV5.js");
813
813
  const code = runInitRecommend({
814
814
  description: descParts.join(" "),
815
815
  topK: opts.top ? Number(opts.top) : 5,
@@ -819,7 +819,7 @@ initCmd.command("recommend <description...>").description("Recommend references
819
819
  }
820
820
  );
821
821
  initCmd.command("prepare").description("Stage init context: rename existing DESIGN.md, compute delta_set, write .omd/init-context.json").requiredOption("--ref <id>", "Reference id (e.g. vercel, linear.app)").requiredOption("--description <text>", "Project description").option("--dir <path>", "Project root (defaults to cwd)").option("--reason <text>", "Reason for deprecation header", "user-initiated omd init").option("--json", "Emit machine-readable JSON").action(async (opts) => {
822
- const { runInitPrepare } = await import("../init-STACB7E5.js");
822
+ const { runInitPrepare } = await import("../init-UMM4XIV5.js");
823
823
  const code = runInitPrepare(opts);
824
824
  if (code !== 0) process.exit(code);
825
825
  });
@@ -836,7 +836,7 @@ program.command("remember <note...>").description("Append a design preference/co
836
836
  }
837
837
  );
838
838
  program.command("sync").description("Sync DESIGN.md shim files (CLAUDE.md, AGENTS.md, .cursor/rules/omd-design.mdc)").option("--dir <path>", "Project root (defaults to cwd)").option("--force", "Overwrite drift without prompting").option("--check", "Exit non-zero if any shim has drift; do not write").action(async (opts) => {
839
- const { runSync } = await import("../sync-P7X4S2DK.js");
839
+ const { runSync } = await import("../sync-FDYRKNFE.js");
840
840
  const code = await runSync(opts);
841
841
  if (code !== 0) process.exit(code);
842
842
  });
@@ -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\nprogram\n .command('generate', { isDefault: true })\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":"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","names":["p","writeFileSync","resolve","loadReference","applyOverrides","existsSync"]}
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":"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","names":["p","writeFileSync","resolve","loadReference","applyOverrides","existsSync"]}
@@ -509,6 +509,21 @@ function deprecateDesignMd(opts) {
509
509
 
510
510
  // src/cli/init.ts
511
511
  function runInitRecommend(opts) {
512
+ const trimmed = opts.description.trim();
513
+ if (!trimmed) {
514
+ if (opts.json) {
515
+ process.stdout.write(
516
+ JSON.stringify({ error: "description is empty" }, null, 2) + "\n"
517
+ );
518
+ } else {
519
+ console.error(
520
+ pc.red(
521
+ 'omd init recommend: description is required. Try a few keywords like "warm fintech dashboard" or "minimal dev tool".'
522
+ )
523
+ );
524
+ }
525
+ return 1;
526
+ }
512
527
  const hits = recommend(opts.description, { topK: opts.topK ?? 5 });
513
528
  const delta = buildDeltaSet(opts.description);
514
529
  if (opts.json) {
@@ -533,6 +548,10 @@ function runInitRecommend(opts) {
533
548
  p.log.message(
534
549
  pc.bold("Matched keywords: ") + delta.matchedKeywords.map((k) => pc.cyan(k.keyword) + pc.dim(` (${k.modifier.toFixed(2)})`)).join(", ")
535
550
  );
551
+ } else {
552
+ p.log.warn(
553
+ "No vocabulary keywords matched. Recommendations will rank by tag overlap only \u2014 try adding tone words (warm / minimal / playful / premium / dense / casual / formal / etc.) for stronger matches. See https://github.com/kwakseongjae/oh-my-design#vocabulary for the full list."
554
+ );
536
555
  }
537
556
  if (delta.warnings.length > 0) {
538
557
  for (const w of delta.warnings) p.log.warn(w);
@@ -553,9 +572,22 @@ function runInitRecommend(opts) {
553
572
  function runInitPrepare(opts) {
554
573
  const projectRoot = opts.dir ?? process.cwd();
555
574
  const relRoot = relative(process.cwd(), projectRoot) || ".";
575
+ if (!opts.description?.trim()) {
576
+ console.error(
577
+ pc.red(
578
+ "omd init prepare: --description is required and cannot be empty."
579
+ )
580
+ );
581
+ return 1;
582
+ }
556
583
  const refPath = findReferencePath(opts.ref);
557
584
  if (!refPath) {
558
- console.error(pc.red(`omd init prepare: reference not found: ${opts.ref}`));
585
+ console.error(
586
+ pc.red(`omd init prepare: reference not found: ${opts.ref}`)
587
+ );
588
+ console.error(
589
+ pc.dim(" Run `omd reference list` to see all available references.")
590
+ );
559
591
  return 1;
560
592
  }
561
593
  const referenceMd = readFileSync4(refPath, "utf8");
@@ -605,6 +637,11 @@ function runInitPrepare(opts) {
605
637
  p.log.success(
606
638
  `Context staged \u2192 ${relative(projectRoot, contextPath)}`
607
639
  );
640
+ if (!skillsInstalled(projectRoot)) {
641
+ p.log.warn(
642
+ "No omd:* skills installed in this project \u2014 your agent won't know how to consume this context. Run `npx oh-my-design-cli install-skills` first."
643
+ );
644
+ }
608
645
  p.outro(
609
646
  pc.dim(
610
647
  "Next: have your agent (Claude Code / Codex / OpenCode) run the `omd:init` skill to generate DESIGN.md from this context."
@@ -612,6 +649,9 @@ function runInitPrepare(opts) {
612
649
  );
613
650
  return 0;
614
651
  }
652
+ function skillsInstalled(projectRoot) {
653
+ return existsSync2(join4(projectRoot, ".claude", "skills", "omd-init", "SKILL.md")) || existsSync2(join4(projectRoot, ".codex", "skills", "omd-init", "SKILL.md")) || existsSync2(join4(projectRoot, ".opencode", "agents", "omd-init.md"));
654
+ }
615
655
  function findReferencePath(refId) {
616
656
  const root = findRepoRoot();
617
657
  if (!root) return null;
@@ -632,4 +672,4 @@ export {
632
672
  runInitPrepare,
633
673
  runInitRecommend
634
674
  };
635
- //# sourceMappingURL=init-STACB7E5.js.map
675
+ //# sourceMappingURL=init-UMM4XIV5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/init.ts","../src/core/vocabulary.ts","../src/core/recommend.ts","../src/core/init-deprecate.ts"],"sourcesContent":["import * as p from '@clack/prompts';\nimport pc from 'picocolors';\nimport {\n writeFileSync,\n readFileSync,\n mkdirSync,\n existsSync,\n} from 'node:fs';\nimport { join, relative, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { buildDeltaSet } from '../core/vocabulary.js';\nimport { recommend } from '../core/recommend.js';\nimport { deprecateDesignMd } from '../core/init-deprecate.js';\n\nexport interface InitRecommendOptions {\n description: string;\n topK?: number;\n json?: boolean;\n}\n\nexport interface InitPrepareOptions {\n dir?: string;\n ref: string;\n description: string;\n reason?: string;\n json?: boolean;\n}\n\nexport function runInitRecommend(opts: InitRecommendOptions): number {\n const trimmed = opts.description.trim();\n if (!trimmed) {\n if (opts.json) {\n process.stdout.write(\n JSON.stringify({ error: 'description is empty' }, null, 2) + '\\n'\n );\n } else {\n console.error(\n pc.red(\n 'omd init recommend: description is required. Try a few keywords like \"warm fintech dashboard\" or \"minimal dev tool\".'\n )\n );\n }\n return 1;\n }\n\n const hits = recommend(opts.description, { topK: opts.topK ?? 5 });\n const delta = buildDeltaSet(opts.description);\n\n if (opts.json) {\n process.stdout.write(\n JSON.stringify(\n {\n description: opts.description,\n recommendations: hits,\n delta_set: delta,\n },\n null,\n 2\n )\n );\n process.stdout.write('\\n');\n return 0;\n }\n\n p.intro(pc.bold('omd init — recommend'));\n p.log.message(pc.dim(`Query: \"${opts.description}\"\\n`));\n\n if (delta.matchedKeywords.length > 0) {\n p.log.message(\n pc.bold('Matched keywords: ') +\n delta.matchedKeywords\n .map((k) => pc.cyan(k.keyword) + pc.dim(` (${k.modifier.toFixed(2)})`))\n .join(', ')\n );\n } else {\n p.log.warn(\n 'No vocabulary keywords matched. Recommendations will rank by tag overlap only — try adding tone words (warm / minimal / playful / premium / dense / casual / formal / etc.) for stronger matches. See https://github.com/kwakseongjae/oh-my-design#vocabulary for the full list.'\n );\n }\n if (delta.warnings.length > 0) {\n for (const w of delta.warnings) p.log.warn(w);\n }\n\n p.log.message(pc.bold('\\nTop references:'));\n for (const [i, hit] of hits.entries()) {\n const scoreStr = pc.dim(`[${hit.score.toFixed(2)}]`);\n const matched = hit.matchedKeywords.length > 0\n ? pc.green(hit.matchedKeywords.join(', '))\n : pc.dim('(no direct tag match)');\n p.log.message(\n ` ${i + 1}. ${pc.bold(hit.id.padEnd(14))} ${scoreStr} ${pc.dim(hit.category.padEnd(14))} ${matched}`\n );\n }\n\n p.outro(\n pc.dim('Next: `omd init prepare --ref <id> --description \"...\"` to stage.')\n );\n return 0;\n}\n\nexport function runInitPrepare(opts: InitPrepareOptions): number {\n const projectRoot = opts.dir ?? process.cwd();\n const relRoot = relative(process.cwd(), projectRoot) || '.';\n\n if (!opts.description?.trim()) {\n console.error(\n pc.red(\n 'omd init prepare: --description is required and cannot be empty.'\n )\n );\n return 1;\n }\n\n const refPath = findReferencePath(opts.ref);\n if (!refPath) {\n console.error(\n pc.red(`omd init prepare: reference not found: ${opts.ref}`)\n );\n console.error(\n pc.dim(' Run `omd reference list` to see all available references.')\n );\n return 1;\n }\n\n const referenceMd = readFileSync(refPath, 'utf8');\n const delta = buildDeltaSet(opts.description);\n\n // Handle existing DESIGN.md\n const deprecate = deprecateDesignMd({\n projectRoot,\n newReference: opts.ref,\n reason: opts.reason ?? 'user-initiated omd init',\n });\n\n // Write init-context.json that the omd:init skill consumes.\n // Note: we deliberately do NOT persist reference_path (absolute paths are\n // fragile across machines / npm reinstalls). The skill reads reference_md\n // directly from this command's --json output, and can re-fetch reference\n // content via `omd reference show <id>` if needed.\n const contextPath = join(projectRoot, '.omd', 'init-context.json');\n mkdirSync(join(projectRoot, '.omd'), { recursive: true });\n const context = {\n schema: 'omd.init-context/v1',\n created_at: new Date().toISOString(),\n reference_id: opts.ref,\n description: opts.description,\n delta_set: delta,\n deprecated_from: deprecate.renamed ? deprecate.to : null,\n };\n writeFileSync(contextPath, JSON.stringify(context, null, 2) + '\\n', 'utf8');\n\n if (opts.json) {\n process.stdout.write(\n JSON.stringify(\n {\n project_root: projectRoot,\n reference_path: refPath,\n context_path: contextPath,\n deprecated_from: deprecate.renamed ? deprecate.to : null,\n reference_md: referenceMd,\n delta_set: delta,\n },\n null,\n 2\n )\n );\n process.stdout.write('\\n');\n return 0;\n }\n\n p.intro(pc.bold('omd init — prepare') + pc.dim(` (${relRoot})`));\n p.log.message(`Reference: ${pc.cyan(opts.ref)}`);\n p.log.message(`Description: ${pc.dim(opts.description)}`);\n if (deprecate.renamed) {\n p.log.warn(\n `Existing DESIGN.md renamed → ${relative(projectRoot, deprecate.to)}`\n );\n }\n p.log.success(\n `Context staged → ${relative(projectRoot, contextPath)}`\n );\n\n if (!skillsInstalled(projectRoot)) {\n p.log.warn(\n 'No omd:* skills installed in this project — your agent won\\'t know how to consume this context. Run `npx oh-my-design-cli install-skills` first.'\n );\n }\n\n p.outro(\n pc.dim(\n 'Next: have your agent (Claude Code / Codex / OpenCode) run the `omd:init` skill to generate DESIGN.md from this context.'\n )\n );\n return 0;\n}\n\nfunction skillsInstalled(projectRoot: string): boolean {\n return (\n existsSync(join(projectRoot, '.claude', 'skills', 'omd-init', 'SKILL.md')) ||\n existsSync(join(projectRoot, '.codex', 'skills', 'omd-init', 'SKILL.md')) ||\n existsSync(join(projectRoot, '.opencode', 'agents', 'omd-init.md'))\n );\n}\n\nfunction findReferencePath(refId: string): string | null {\n const root = findRepoRoot();\n if (!root) return null;\n const path = join(root, 'references', refId, 'DESIGN.md');\n return existsSync(path) ? path : null;\n}\n\nfunction findRepoRoot(): string | null {\n let cur = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 8; i++) {\n if (existsSync(join(cur, 'references'))) return cur;\n const parent = dirname(cur);\n if (parent === cur) break;\n cur = parent;\n }\n return null;\n}\n","import { readFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, join } from 'node:path';\n\nexport interface AxisSpec {\n type: 'int' | 'float' | 'enum';\n domain: [number, number];\n unit?: string;\n step?: number;\n}\n\nexport interface KeywordAxes {\n [axis: string]: { delta: number; range: [number, number] };\n}\n\nexport interface KeywordSpec {\n group: 'tone' | 'density' | 'formality' | 'domain';\n axes: KeywordAxes;\n conflicts_with: string[];\n voice_hints: string[];\n coupled_axes?: string[];\n sources?: string[];\n}\n\nexport interface Vocabulary {\n version: number;\n generated_at: string;\n axis_registry: Record<string, AxisSpec>;\n modifiers: Record<string, number>;\n keywords: Record<string, KeywordSpec>;\n}\n\nexport interface SynonymsFile {\n version: number;\n map: Record<string, string>;\n}\n\nlet cachedVocab: Vocabulary | null = null;\nlet cachedSynonyms: SynonymsFile | null = null;\n\nfunction dataDir(): string {\n const here = dirname(fileURLToPath(import.meta.url));\n // When built (dist/) we are at dist/*.js → data/ is ../data\n // When running from src/ via tsx we are at src/core/ → data/ is ../../data\n const candidates = [\n join(here, '..', 'data'),\n join(here, '..', '..', 'data'),\n ];\n for (const c of candidates) {\n try {\n readFileSync(join(c, 'vocabulary.json'), 'utf8');\n return c;\n } catch {\n // continue\n }\n }\n throw new Error('data/vocabulary.json not found relative to ' + here);\n}\n\nexport function loadVocabulary(): Vocabulary {\n if (cachedVocab) return cachedVocab;\n const path = join(dataDir(), 'vocabulary.json');\n cachedVocab = JSON.parse(readFileSync(path, 'utf8')) as Vocabulary;\n return cachedVocab;\n}\n\nexport function loadSynonyms(): SynonymsFile {\n if (cachedSynonyms) return cachedSynonyms;\n const path = join(dataDir(), 'synonyms.json');\n cachedSynonyms = JSON.parse(readFileSync(path, 'utf8')) as SynonymsFile;\n return cachedSynonyms;\n}\n\nexport interface ResolvedKeyword {\n keyword: string;\n modifier: number;\n matchedAs: 'direct' | 'synonym';\n synonymSource?: string;\n}\n\nconst MODIFIER_RE = /\\b(primarily|mostly|slightly|very|not)\\s+([a-z][a-z-]*)/gi;\n\nexport function tokenize(description: string): string[] {\n return description\n .toLowerCase()\n .split(/[^a-z-]+/)\n .filter(Boolean);\n}\n\nexport function extractKeywords(description: string): ResolvedKeyword[] {\n const vocab = loadVocabulary();\n const { map: synonyms } = loadSynonyms();\n\n const lower = description.toLowerCase();\n const modifierOverrides = new Map<string, number>();\n let match: RegExpExecArray | null;\n const modRe = new RegExp(MODIFIER_RE.source, MODIFIER_RE.flags);\n while ((match = modRe.exec(lower)) !== null) {\n const modName = match[1];\n const target = match[2];\n const value = vocab.modifiers[modName];\n if (value !== undefined) modifierOverrides.set(target, value);\n }\n\n const tokens = tokenize(description);\n const seen = new Set<string>();\n const results: ResolvedKeyword[] = [];\n\n for (const token of tokens) {\n if (seen.has(token)) continue;\n seen.add(token);\n\n if (vocab.keywords[token]) {\n results.push({\n keyword: token,\n modifier: modifierOverrides.get(token) ?? 1.0,\n matchedAs: 'direct',\n });\n continue;\n }\n\n const syn = synonyms[token];\n if (syn && vocab.keywords[syn]) {\n results.push({\n keyword: syn,\n modifier: modifierOverrides.get(token) ?? 1.0,\n matchedAs: 'synonym',\n synonymSource: token,\n });\n }\n }\n\n return results;\n}\n\nexport interface ConflictResolution {\n kept: ResolvedKeyword[];\n dropped: Array<{ keyword: string; reason: string }>;\n warnings: string[];\n}\n\nexport function resolveConflicts(\n keywords: ResolvedKeyword[]\n): ConflictResolution {\n const vocab = loadVocabulary();\n const kept: ResolvedKeyword[] = [];\n const dropped: Array<{ keyword: string; reason: string }> = [];\n\n // Classify each keyword: kept if strictly higher modifier than any conflicter, else dropped.\n for (const kw of keywords) {\n const spec = vocab.keywords[kw.keyword];\n if (!spec) continue;\n\n const conflictingHere = keywords.filter(\n (other) =>\n other.keyword !== kw.keyword &&\n spec.conflicts_with.includes(other.keyword)\n );\n\n if (conflictingHere.length === 0) {\n kept.push(kw);\n continue;\n }\n\n const strictlyHigherThanAll = conflictingHere.every(\n (c) => kw.modifier > c.modifier + 1e-9\n );\n if (strictlyHigherThanAll) {\n kept.push(kw);\n continue;\n }\n\n dropped.push({\n keyword: kw.keyword,\n reason: `conflicts with ${conflictingHere.map((c) => c.keyword).join(',')}`,\n });\n }\n\n // Emit warnings ONLY for conflict groups where no member was kept\n // (genuine tie — user should disambiguate).\n const warnings: string[] = [];\n const keptSet = new Set(kept.map((k) => k.keyword));\n const warned = new Set<string>();\n for (const kw of keywords) {\n const spec = vocab.keywords[kw.keyword];\n if (!spec) continue;\n const conflictingHere = keywords.filter(\n (other) =>\n other.keyword !== kw.keyword &&\n spec.conflicts_with.includes(other.keyword)\n );\n if (conflictingHere.length === 0) continue;\n const groupKeys = [kw.keyword, ...conflictingHere.map((c) => c.keyword)];\n const groupHasWinner = groupKeys.some((k) => keptSet.has(k));\n if (groupHasWinner) continue;\n const pairKey = [...new Set(groupKeys)].sort().join(',');\n if (warned.has(pairKey)) continue;\n warned.add(pairKey);\n warnings.push(\n `${kw.keyword} ↔ ${conflictingHere.map((c) => c.keyword).join(',')}: use \"primarily <kw>\" or \"very <kw>\" to pick a winner`\n );\n }\n\n return { kept, dropped, warnings };\n}\n\nexport interface DeltaSet {\n axes: Record<string, { value: number; rangeUnion: [number, number]; sources: string[] }>;\n voiceHints: string[];\n unresolved: string[];\n warnings: string[];\n droppedKeywords: Array<{ keyword: string; reason: string }>;\n matchedKeywords: ResolvedKeyword[];\n}\n\nfunction clamp(value: number, lo: number, hi: number): number {\n return Math.max(lo, Math.min(hi, value));\n}\n\nfunction snap(value: number, spec: AxisSpec): number {\n if (spec.type === 'int') return Math.round(value);\n if (spec.type === 'enum') {\n const [lo, hi] = spec.domain;\n return Math.abs(value - lo) < Math.abs(value - hi) ? lo : hi;\n }\n return Number(value.toFixed(3));\n}\n\nexport function buildDeltaSet(description: string): DeltaSet {\n const vocab = loadVocabulary();\n const matched = extractKeywords(description);\n const { kept, dropped, warnings } = resolveConflicts(matched);\n\n const axes: DeltaSet['axes'] = {};\n const voiceHintSet = new Set<string>();\n\n for (const kw of kept) {\n const spec = vocab.keywords[kw.keyword];\n const multiplier = kw.modifier;\n\n for (const hint of spec.voice_hints) voiceHintSet.add(hint);\n\n for (const [axisName, axisDelta] of Object.entries(spec.axes)) {\n const bucket = axes[axisName] ?? {\n value: 0,\n rangeUnion: [axisDelta.range[0], axisDelta.range[1]] as [number, number],\n sources: [] as string[],\n };\n bucket.value += axisDelta.delta * multiplier;\n bucket.rangeUnion = [\n Math.min(bucket.rangeUnion[0], axisDelta.range[0]),\n Math.max(bucket.rangeUnion[1], axisDelta.range[1]),\n ];\n bucket.sources.push(kw.keyword);\n axes[axisName] = bucket;\n }\n }\n\n for (const [axisName, bucket] of Object.entries(axes)) {\n const registry = vocab.axis_registry[axisName];\n if (!registry) continue;\n let v = clamp(bucket.value, bucket.rangeUnion[0], bucket.rangeUnion[1]);\n v = clamp(v, registry.domain[0], registry.domain[1]);\n bucket.value = snap(v, registry);\n bucket.sources.sort();\n }\n\n return {\n axes,\n voiceHints: [...voiceHintSet],\n unresolved: [],\n warnings,\n droppedKeywords: dropped,\n matchedKeywords: kept,\n };\n}\n\nexport function listKeywords(): string[] {\n return Object.keys(loadVocabulary().keywords).sort();\n}\n","import { readFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, join } from 'node:path';\nimport { tokenize } from './vocabulary.js';\n\nexport interface ReferenceTag {\n id: string;\n color: string;\n category: string;\n keywords: string[];\n}\n\nexport interface RecommendHit {\n id: string;\n category: string;\n color: string;\n keywords: string[];\n score: number;\n matchedKeywords: string[];\n matchedCategories: string[];\n}\n\nconst CATEGORY_HINTS: Record<string, string[]> = {\n Consumer: [\n 'marketplace',\n 'shopping',\n 'ecommerce',\n 'consumer',\n 'b2c',\n 'retail',\n 'subscription',\n 'family',\n 'families',\n 'meal',\n 'meals',\n 'meal-kit',\n 'food',\n 'travel',\n 'social',\n 'community',\n 'buyer',\n 'seller',\n 'parents',\n 'kids',\n 'lifestyle',\n 'recipe',\n 'recipes',\n ],\n Fintech: [\n 'fintech',\n 'banking',\n 'bank',\n 'payment',\n 'payments',\n 'crypto',\n 'trading',\n 'wallet',\n 'invest',\n 'investing',\n 'money',\n 'finance',\n 'financial',\n 'lending',\n 'remittance',\n 'tax',\n ],\n 'Developer Tools': [\n 'developer',\n 'devtool',\n 'devtools',\n 'dev-tool',\n 'deploy',\n 'deployment',\n 'build',\n 'ci',\n 'cd',\n 'cli',\n 'sdk',\n 'editor',\n 'ide',\n 'engineering',\n 'compiler',\n 'runtime',\n ],\n AI: [\n 'ai',\n 'ml',\n 'llm',\n 'agent',\n 'agents',\n 'model',\n 'models',\n 'inference',\n 'gpt',\n 'chatbot',\n 'rag',\n 'embedding',\n 'embeddings',\n 'mcp',\n ],\n 'Design Tools': [\n 'design',\n 'design-tool',\n 'whiteboard',\n 'prototype',\n 'prototyping',\n 'wireframe',\n 'wireframes',\n 'mockup',\n 'mockups',\n 'figma-like',\n 'illustration',\n 'canvas',\n ],\n Productivity: [\n 'saas',\n 'workspace',\n 'team',\n 'teams',\n 'project-management',\n 'enterprise',\n 'b2b',\n 'crm',\n 'docs',\n 'wiki',\n 'collaboration',\n 'kanban',\n 'scheduling',\n 'meetings',\n ],\n Backend: [\n 'backend',\n 'database',\n 'db',\n 'api',\n 'apis',\n 'observability',\n 'monitoring',\n 'logging',\n 'analytics',\n 'pipeline',\n 'data-pipeline',\n 'streaming',\n 'queue',\n 'cache',\n ],\n Automotive: [\n 'car',\n 'cars',\n 'vehicle',\n 'vehicles',\n 'auto',\n 'automotive',\n 'driving',\n 'ev',\n 'electric-vehicle',\n ],\n Marketing: [\n 'marketing',\n 'seo',\n 'campaign',\n 'campaigns',\n 'newsletter',\n 'email-marketing',\n 'attribution',\n ],\n};\n\nfunction matchedCategoriesFor(\n queryTokens: Set<string>,\n queryStems: Set<string>\n): Set<string> {\n const out = new Set<string>();\n for (const [category, hints] of Object.entries(CATEGORY_HINTS)) {\n for (const hint of hints) {\n if (queryTokens.has(hint) || queryStems.has(stem(hint))) {\n out.add(category);\n break;\n }\n }\n }\n return out;\n}\n\nlet cachedTags: ReferenceTag[] | null = null;\n\nfunction stem(s: string): string {\n // Minimal English suffix stripping for recall across -ing/-ed/-s variants.\n // Preserves stems of length ≥3 so we don't collapse short words.\n let out = s;\n for (const suffix of ['ing', 'ed', 'ly', 'es', 's']) {\n if (out.length - suffix.length >= 3 && out.endsWith(suffix)) {\n out = out.slice(0, -suffix.length);\n break;\n }\n }\n return out;\n}\n\nfunction tagsFilePath(): string {\n const here = dirname(fileURLToPath(import.meta.url));\n const candidates = [\n join(here, '..', 'data', 'reference-tags.md'),\n join(here, '..', '..', 'data', 'reference-tags.md'),\n ];\n for (const c of candidates) {\n try {\n readFileSync(c, 'utf8');\n return c;\n } catch {\n // continue\n }\n }\n throw new Error('data/reference-tags.md not found');\n}\n\nconst ROW_RE = /^\\|\\s*([a-z0-9._-]+)\\s*\\|\\s*([^|]*?)\\s*\\|\\s*([^|]*?)\\s*\\|\\s*([^|]*?)\\s*\\|$/i;\n\nexport function loadReferenceTags(): ReferenceTag[] {\n if (cachedTags) return cachedTags;\n const raw = readFileSync(tagsFilePath(), 'utf8');\n const rows: ReferenceTag[] = [];\n for (const line of raw.split('\\n')) {\n const m = ROW_RE.exec(line);\n if (!m) continue;\n const [, id, color, category, keywordsRaw] = m;\n if (id === 'id') continue; // header\n if (id.startsWith('---')) continue;\n rows.push({\n id: id.trim(),\n color: color.trim(),\n category: category.trim(),\n keywords: keywordsRaw\n .split(',')\n .map((k) => k.trim().toLowerCase())\n .filter(Boolean),\n });\n }\n cachedTags = rows;\n return rows;\n}\n\nexport interface RecommendOptions {\n topK?: number;\n diversityByCategory?: boolean;\n}\n\nexport function recommend(\n description: string,\n opts: RecommendOptions = {}\n): RecommendHit[] {\n const topK = opts.topK ?? 5;\n const diversityByCategory = opts.diversityByCategory ?? true;\n\n const tags = loadReferenceTags();\n const rawTokens = [\n ...tokenize(description),\n ...description\n .toLowerCase()\n .split(/[^a-z0-9-]+/)\n .filter(Boolean),\n ];\n const queryTokens = new Set(rawTokens);\n const queryStems = new Set(rawTokens.map(stem));\n const matchedCategories = matchedCategoriesFor(queryTokens, queryStems);\n\n // First pass: tag matches per ref, to know whether to gate category bonus.\n const tagMatchByRef = tags.map((t) =>\n t.keywords.filter(\n (kw) => queryTokens.has(kw) || queryStems.has(stem(kw))\n )\n );\n const totalTagMatches = tagMatchByRef.reduce((a, m) => a + m.length, 0);\n\n const scored: Array<RecommendHit & { _ratio: number }> = tags.map((t, i) => {\n const matched = tagMatchByRef[i];\n const tagScore = matched.length;\n const categoryHit = matchedCategories.has(t.category);\n // Category bonus normally requires ≥1 tag match (prevents flooding).\n // But when the description produces zero tag matches anywhere, we\n // fall back to category-only scoring so users still get a useful\n // top-K instead of an alphabetical no-op.\n const categoryBonus =\n categoryHit && (tagScore > 0 || totalTagMatches === 0) ? 0.5 : 0;\n const score = tagScore + categoryBonus;\n const ratio = matched.length / Math.max(1, t.keywords.length);\n return {\n id: t.id,\n category: t.category,\n color: t.color,\n keywords: t.keywords,\n score,\n matchedKeywords: matched,\n matchedCategories: categoryHit ? [t.category] : [],\n _ratio: ratio,\n };\n });\n\n scored.sort(\n (a, b) =>\n b.score - a.score || b._ratio - a._ratio || a.id.localeCompare(b.id)\n );\n\n const stripRatio = (s: typeof scored): RecommendHit[] =>\n s.map(({ _ratio, ...rest }) => {\n void _ratio;\n return rest;\n });\n\n if (!diversityByCategory) return stripRatio(scored.slice(0, topK));\n\n // Category-bucketed diversity: one top hit per category, fill with next best.\n const picked = stripRatio(scored).slice(0, 0);\n const pickedSet = new Set<string>();\n const usedCategories = new Set<string>();\n const allHits = stripRatio(scored);\n for (const hit of allHits) {\n if (picked.length >= topK) break;\n if (usedCategories.has(hit.category)) continue;\n picked.push(hit);\n pickedSet.add(hit.id);\n usedCategories.add(hit.category);\n }\n for (const hit of allHits) {\n if (picked.length >= topK) break;\n if (pickedSet.has(hit.id)) continue;\n picked.push(hit);\n pickedSet.add(hit.id);\n }\n return picked;\n}\n","import {\n existsSync,\n readFileSync,\n writeFileSync,\n unlinkSync,\n mkdirSync,\n} from 'node:fs';\nimport { dirname, join } from 'node:path';\n\nexport interface DeprecateOptions {\n projectRoot: string;\n previousReference?: string;\n newReference: string;\n preferencesReplayed?: number;\n preferencesOrphaned?: number;\n orphanFile?: string;\n reason: string;\n now?: Date;\n}\n\nexport interface DeprecateResult {\n renamed: boolean;\n from: string;\n to: string;\n}\n\nfunction deprecationHeader(opts: DeprecateOptions): string {\n const now = (opts.now ?? new Date()).toISOString();\n const lines = [\n '<!--',\n 'omd:deprecated',\n ` deprecated_at: ${now}`,\n ];\n if (opts.previousReference)\n lines.push(` previous_reference: ${opts.previousReference}`);\n lines.push(` new_reference: ${opts.newReference}`);\n if (opts.preferencesReplayed !== undefined)\n lines.push(` preferences_replayed: ${opts.preferencesReplayed}`);\n if (opts.preferencesOrphaned !== undefined)\n lines.push(` preferences_orphaned: ${opts.preferencesOrphaned}`);\n if (opts.orphanFile) lines.push(` orphan_file: ${opts.orphanFile}`);\n lines.push(` reason: ${opts.reason}`);\n lines.push('-->', '', '');\n return lines.join('\\n');\n}\n\nexport function deprecateDesignMd(opts: DeprecateOptions): DeprecateResult {\n const from = join(opts.projectRoot, 'DESIGN.md');\n const baseTo = join(opts.projectRoot, 'DESIGN_DEPRECATED.md');\n\n if (!existsSync(from)) {\n return { renamed: false, from, to: baseTo };\n }\n\n let target = baseTo;\n if (existsSync(baseTo)) {\n const ts = (opts.now ?? new Date()).toISOString().replace(/[:.]/g, '-');\n target = join(opts.projectRoot, `DESIGN_DEPRECATED.${ts}.md`);\n }\n\n mkdirSync(dirname(target), { recursive: true });\n const prior = readFileSync(from, 'utf8');\n writeFileSync(target, deprecationHeader(opts) + prior, 'utf8');\n unlinkSync(from);\n\n return { renamed: true, from, to: target };\n}\n"],"mappings":"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A;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,mBAAmB;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,IAAI;AAAA,IACF;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,gBAAgB;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,cAAc;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAA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","names":["writeFileSync","readFileSync","mkdirSync","existsSync","join","dirname","fileURLToPath","readFileSync","fileURLToPath","dirname","join","dirname","fileURLToPath","join","readFileSync","readFileSync","dirname","join","readFileSync","join","mkdirSync","writeFileSync","existsSync","dirname","fileURLToPath"]}
@@ -9,7 +9,8 @@ import {
9
9
  // src/cli/sync.ts
10
10
  import * as p from "@clack/prompts";
11
11
  import pc from "picocolors";
12
- import { relative } from "path";
12
+ import { existsSync as existsSync3 } from "fs";
13
+ import { join as join3, relative } from "path";
13
14
 
14
15
  // src/core/shims.ts
15
16
  import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync2 } from "fs";
@@ -286,6 +287,9 @@ function refreshDesignMdHash(projectRoot) {
286
287
  }
287
288
 
288
289
  // src/cli/sync.ts
290
+ var DESIGN_MD_NAME = "DESIGN.md";
291
+ var DEPRECATED_MD_NAME = "DESIGN_DEPRECATED.md";
292
+ var INIT_CONTEXT_PATH = ".omd/init-context.json";
289
293
  var STATUS_LABEL = {
290
294
  missing: pc.yellow("missing"),
291
295
  clean: pc.green("clean"),
@@ -337,6 +341,10 @@ async function runSync(opts = {}) {
337
341
  const rel = relative(projectRoot, result.path);
338
342
  p.log.message(` ${STATUS_LABEL[result.status]} ${rel}`);
339
343
  }
344
+ const designMdMissing = !existsSync3(join3(projectRoot, DESIGN_MD_NAME));
345
+ const initContextExists = existsSync3(join3(projectRoot, INIT_CONTEXT_PATH));
346
+ const deprecatedExists = existsSync3(join3(projectRoot, DEPRECATED_MD_NAME));
347
+ const midInitFlow = designMdMissing && (initContextExists || deprecatedExists);
340
348
  if (opts.check) {
341
349
  const unsynced = inspections.filter(
342
350
  (i) => i.result.status !== "clean"
@@ -349,7 +357,12 @@ async function runSync(opts = {}) {
349
357
  );
350
358
  return 1;
351
359
  }
352
- p.outro(pc.green("All clean."));
360
+ if (designMdMissing) {
361
+ p.log.warn(
362
+ midInitFlow ? "DESIGN.md is missing but init-context is staged \u2014 your agent still needs to run the `omd:init` skill to write DESIGN.md." : 'DESIGN.md is missing \u2014 run `npx oh-my-design-cli init recommend "<description>"` to bootstrap, or have your agent run the `omd:init` skill.'
363
+ );
364
+ }
365
+ p.outro(pc.green("Shims clean."));
353
366
  return 0;
354
367
  }
355
368
  const results = [];
@@ -401,4 +414,4 @@ async function runSync(opts = {}) {
401
414
  export {
402
415
  runSync
403
416
  };
404
- //# sourceMappingURL=sync-P7X4S2DK.js.map
417
+ //# sourceMappingURL=sync-FDYRKNFE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/sync.ts","../src/core/shims.ts","../src/core/sync-lock.ts"],"sourcesContent":["import * as p from '@clack/prompts';\nimport pc from 'picocolors';\nimport { existsSync } from 'node:fs';\nimport { join, relative } from 'node:path';\nimport {\n ALL_SHIMS,\n inspectShim,\n writeShim,\n refreshDesignMdHash,\n type Shim,\n type InspectResult,\n type WriteShimResult,\n} from '../core/shims.js';\n\nconst DESIGN_MD_NAME = 'DESIGN.md';\nconst DEPRECATED_MD_NAME = 'DESIGN_DEPRECATED.md';\nconst INIT_CONTEXT_PATH = '.omd/init-context.json';\n\nexport interface SyncOptions {\n dir?: string;\n force?: boolean;\n check?: boolean;\n}\n\nconst STATUS_LABEL: Record<InspectResult['status'], string> = {\n missing: pc.yellow('missing'),\n clean: pc.green('clean'),\n drifted: pc.red('drifted'),\n 'out-of-date': pc.cyan('out-of-date'),\n};\n\nconst WRITE_LABEL: Record<WriteShimResult['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 printDiff(label: string, current: string, proposed: string): void {\n p.log.message(pc.bold('─── current (' + label + ') ───'));\n p.log.message(current || pc.dim('(empty)'));\n p.log.message(pc.bold('─── proposed ───'));\n p.log.message(proposed);\n}\n\ntype DriftChoice = 'overwrite' | 'skip' | 'show' | 'quit';\n\nasync function promptDrift(shim: Shim, inspection: InspectResult): Promise<DriftChoice> {\n while (true) {\n const choice = await p.select<DriftChoice>({\n message: `${pc.bold(shim.relPath)} has drift — choose:`,\n options: [\n { value: 'overwrite', label: 'Overwrite with rendered content' },\n { value: 'skip', label: 'Skip (keep user edits)' },\n { value: 'show', label: 'Show diff' },\n { value: 'quit', label: 'Quit sync' },\n ],\n });\n\n if (p.isCancel(choice)) return 'quit';\n if (choice !== 'show') return choice;\n\n printDiff(\n shim.relPath,\n inspection.existing ?? '',\n inspection.rendered\n );\n }\n}\n\nexport async function runSync(opts: SyncOptions = {}): Promise<number> {\n const projectRoot = opts.dir ?? process.cwd();\n const relRoot = relative(process.cwd(), projectRoot) || '.';\n\n p.intro(pc.bold('omd sync') + pc.dim(` (${relRoot})`));\n\n const inspections = ALL_SHIMS.map((shim) => ({\n shim,\n result: inspectShim(projectRoot, shim),\n }));\n\n p.log.message(pc.bold('Status:'));\n for (const { result } of inspections) {\n const rel = relative(projectRoot, result.path);\n p.log.message(` ${STATUS_LABEL[result.status]} ${rel}`);\n }\n\n // Mid-flow detection: shims may be clean while DESIGN.md is mid-init\n // (after `omd init prepare` renamed the old one but before the agent\n // wrote the new one). Surface this as a soft warning so users don't\n // mistake \"All clean\" for \"fully done\".\n const designMdMissing = !existsSync(join(projectRoot, DESIGN_MD_NAME));\n const initContextExists = existsSync(join(projectRoot, INIT_CONTEXT_PATH));\n const deprecatedExists = existsSync(join(projectRoot, DEPRECATED_MD_NAME));\n const midInitFlow =\n designMdMissing && (initContextExists || deprecatedExists);\n\n if (opts.check) {\n const unsynced = inspections.filter(\n (i) => i.result.status !== 'clean'\n );\n if (unsynced.length > 0) {\n p.outro(\n pc.red(\n `${unsynced.length} not in sync — rerun without --check to resolve.`\n )\n );\n return 1;\n }\n if (designMdMissing) {\n p.log.warn(\n midInitFlow\n ? 'DESIGN.md is missing but init-context is staged — your agent still needs to run the `omd:init` skill to write DESIGN.md.'\n : 'DESIGN.md is missing — run `npx oh-my-design-cli init recommend \"<description>\"` to bootstrap, or have your agent run the `omd:init` skill.'\n );\n }\n p.outro(pc.green('Shims clean.'));\n return 0;\n }\n\n const results: WriteShimResult[] = [];\n\n for (const { shim, result } of inspections) {\n if (result.status === 'clean') {\n results.push({\n id: shim.id,\n path: result.path,\n hash: '',\n status: 'unchanged',\n });\n continue;\n }\n\n if (result.status === 'drifted' && !opts.force) {\n const choice = await promptDrift(shim, result);\n if (choice === 'quit') {\n p.outro(pc.yellow('Aborted by user.'));\n return 1;\n }\n if (choice === 'skip') {\n results.push(\n writeShim(projectRoot, shim, { onDrift: 'skip' })\n );\n continue;\n }\n // overwrite\n results.push(\n writeShim(projectRoot, shim, { onDrift: 'overwrite' })\n );\n continue;\n }\n\n // missing / out-of-date / (drifted with --force)\n const onDrift = opts.force ? 'overwrite' : 'error';\n results.push(writeShim(projectRoot, shim, { onDrift }));\n }\n\n refreshDesignMdHash(projectRoot);\n\n p.log.message(pc.bold('\\nResults:'));\n for (const r of results) {\n const rel = relative(projectRoot, r.path);\n p.log.message(` ${WRITE_LABEL[r.status]} ${rel}`);\n }\n\n const drifted = results.filter((r) => r.status === 'skipped-drift').length;\n const changed = results.filter(\n (r) => r.status === 'created' || r.status === 'updated'\n ).length;\n\n p.outro(\n drifted > 0\n ? pc.yellow(`${changed} written, ${drifted} skipped (drift).`)\n : pc.green(`${changed} written.`)\n );\n\n return 0;\n}\n","import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport {\n parseBlock,\n writeBlock,\n hashContent,\n hasDrift,\n} from './sync-marker.js';\nimport { updateTarget, updateDesignMdHash } from './sync-lock.js';\n\nexport const MANAGED_BLOCK_VERSION = 1;\n\nexport type ShimId = 'claude' | 'agents' | 'cursor';\n\nexport type ShimMode = 'block' | 'whole';\n\nexport interface Shim {\n id: ShimId;\n relPath: string;\n mode: ShimMode;\n render(): string;\n}\n\nconst CLAUDE_BODY = `# Design System (oh-my-design)\n\nThe authoritative brand & UI spec is **@./DESIGN.md**.\nRead before any UI/styling/microcopy/motion work.\n\nPreference log (pending corrections): @./.omd/preferences.md\n\nPrecedence: DESIGN.md > preferences.md > your defaults.`;\n\nconst AGENTS_BODY = `## Design System (oh-my-design)\n\n**Before any UI, styling, copy, or motion change, open and read \\`./DESIGN.md\\` in full.** It is the authoritative brand/design spec. Treat its tokens, voice, and component rules as binding unless the user overrides in chat.\n\nIf present, read \\`./.omd/preferences.md\\` — pending corrections not yet folded into DESIGN.md. Apply them; flag conflicts.`;\n\nconst CURSOR_FRONTMATTER = `---\ndescription: Authoritative brand & UI design system. Read DESIGN.md before UI work.\nglobs:\n - \"**/*.tsx\"\n - \"**/*.jsx\"\n - \"**/*.vue\"\n - \"**/*.svelte\"\n - \"**/*.css\"\n - \"**/*.scss\"\n - \"**/tailwind.config.*\"\n - \"**/components/**\"\n - \"**/app/**/page.*\"\nalwaysApply: false\n---`;\n\nconst CURSOR_BODY = `The authoritative design spec lives at \\`@DESIGN.md\\` (repo root). Open and read before generating/modifying UI.\n\nPending preference corrections: \\`@.omd/preferences.md\\`.\n\nPrecedence: DESIGN.md > preferences.md > framework defaults.`;\n\nexport const CLAUDE_SHIM: Shim = {\n id: 'claude',\n relPath: 'CLAUDE.md',\n mode: 'block',\n render: () => CLAUDE_BODY,\n};\n\nexport const AGENTS_SHIM: Shim = {\n id: 'agents',\n relPath: 'AGENTS.md',\n mode: 'block',\n render: () => AGENTS_BODY,\n};\n\nexport const CURSOR_SHIM: Shim = {\n id: 'cursor',\n relPath: '.cursor/rules/omd-design.mdc',\n mode: 'whole',\n render: () => {\n const hash = hashContent(CURSOR_BODY);\n return `${CURSOR_FRONTMATTER}\\n\\n<!-- omd:start v=${MANAGED_BLOCK_VERSION} hash=${hash} -->\\n${CURSOR_BODY}\\n<!-- omd:end -->\\n`;\n },\n};\n\nexport const ALL_SHIMS: readonly Shim[] = [\n CLAUDE_SHIM,\n AGENTS_SHIM,\n CURSOR_SHIM,\n] as const;\n\nexport type DriftAction = 'overwrite' | 'skip' | 'error';\n\nexport interface WriteShimResult {\n id: ShimId;\n path: string;\n hash: string;\n status: 'created' | 'updated' | 'unchanged' | 'skipped-drift';\n}\n\nexport interface WriteShimsOptions {\n onDrift?: DriftAction;\n}\n\nfunction resolvePath(projectRoot: string, relPath: string): string {\n return join(projectRoot, relPath);\n}\n\nfunction readFileOrEmpty(path: string): string {\n return existsSync(path) ? readFileSync(path, 'utf8') : '';\n}\n\nfunction ensureDir(path: string): void {\n mkdirSync(dirname(path), { recursive: true });\n}\n\nexport type InspectStatus =\n | 'missing'\n | 'clean'\n | 'drifted'\n | 'out-of-date';\n\nexport interface InspectResult {\n id: ShimId;\n path: string;\n status: InspectStatus;\n existing?: string;\n rendered: string;\n}\n\nexport function inspectShim(projectRoot: string, shim: Shim): InspectResult {\n const abs = resolvePath(projectRoot, shim.relPath);\n const existing = readFileOrEmpty(abs);\n const fileExists = existing !== '';\n\n if (shim.mode === 'whole') {\n const rendered = shim.render();\n if (!fileExists) {\n return { id: shim.id, path: abs, status: 'missing', rendered };\n }\n if (existing === rendered) {\n return { id: shim.id, path: abs, status: 'clean', existing, rendered };\n }\n return { id: shim.id, path: abs, status: 'drifted', existing, rendered };\n }\n\n const managed = shim.render();\n const block = parseBlock(existing);\n\n if (!block) {\n return {\n id: shim.id,\n path: abs,\n status: 'missing',\n existing: fileExists ? existing : undefined,\n rendered: managed,\n };\n }\n\n if (hasDrift(block)) {\n return {\n id: shim.id,\n path: abs,\n status: 'drifted',\n existing: block.content,\n rendered: managed,\n };\n }\n\n if (block.content === managed) {\n return {\n id: shim.id,\n path: abs,\n status: 'clean',\n existing: block.content,\n rendered: managed,\n };\n }\n\n return {\n id: shim.id,\n path: abs,\n status: 'out-of-date',\n existing: block.content,\n rendered: managed,\n };\n}\n\nexport function inspectAllShims(projectRoot: string): InspectResult[] {\n return ALL_SHIMS.map((s) => inspectShim(projectRoot, s));\n}\n\nexport function writeShim(\n projectRoot: string,\n shim: Shim,\n opts: WriteShimsOptions = {}\n): WriteShimResult {\n const onDrift = opts.onDrift ?? 'error';\n const abs = resolvePath(projectRoot, shim.relPath);\n const existing = readFileOrEmpty(abs);\n const fileExists = existing !== '';\n\n if (shim.mode === 'whole') {\n const rendered = shim.render();\n const newHash = hashContent(rendered);\n\n if (fileExists && existing !== rendered) {\n const existingHash = hashContent(existing);\n if (onDrift === 'error') {\n throw new Error(\n `drift detected in ${shim.relPath} (existing hash ${existingHash} != rendered ${newHash}); rerun with onDrift=overwrite to force`\n );\n }\n if (onDrift === 'skip') {\n updateTarget(projectRoot, shim.relPath, existingHash);\n return {\n id: shim.id,\n path: abs,\n hash: existingHash,\n status: 'skipped-drift',\n };\n }\n }\n\n if (fileExists && existing === rendered) {\n updateTarget(projectRoot, shim.relPath, newHash);\n return { id: shim.id, path: abs, hash: newHash, status: 'unchanged' };\n }\n\n ensureDir(abs);\n writeFileSync(abs, rendered, 'utf8');\n updateTarget(projectRoot, shim.relPath, newHash);\n return {\n id: shim.id,\n path: abs,\n hash: newHash,\n status: fileExists ? 'updated' : 'created',\n };\n }\n\n const managed = shim.render();\n const existingBlock = parseBlock(existing);\n\n if (existingBlock && hasDrift(existingBlock)) {\n if (onDrift === 'error') {\n throw new Error(\n `managed block in ${shim.relPath} was hand-edited; rerun with onDrift=overwrite to force`\n );\n }\n if (onDrift === 'skip') {\n updateTarget(projectRoot, shim.relPath, existingBlock.hash);\n return {\n id: shim.id,\n path: abs,\n hash: existingBlock.hash,\n status: 'skipped-drift',\n };\n }\n }\n\n if (existingBlock && existingBlock.content === managed && !hasDrift(existingBlock)) {\n updateTarget(projectRoot, shim.relPath, existingBlock.hash);\n return {\n id: shim.id,\n path: abs,\n hash: existingBlock.hash,\n status: 'unchanged',\n };\n }\n\n const { updated, hash } = writeBlock(existing, managed, MANAGED_BLOCK_VERSION);\n ensureDir(abs);\n writeFileSync(abs, updated, 'utf8');\n updateTarget(projectRoot, shim.relPath, hash);\n return {\n id: shim.id,\n path: abs,\n hash,\n status: existingBlock ? 'updated' : 'created',\n };\n}\n\nexport function writeAllShims(\n projectRoot: string,\n opts: WriteShimsOptions = {}\n): WriteShimResult[] {\n const results = ALL_SHIMS.map((shim) => writeShim(projectRoot, shim, opts));\n refreshDesignMdHash(projectRoot);\n return results;\n}\n\nexport function refreshDesignMdHash(projectRoot: string): string | null {\n const designMdPath = join(projectRoot, 'DESIGN.md');\n if (!existsSync(designMdPath)) return null;\n const hash = hashContent(readFileSync(designMdPath, 'utf8'));\n updateDesignMdHash(projectRoot, hash);\n return hash;\n}\n","import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { z } from 'zod';\n\nexport const SYNC_LOCK_VERSION = 1;\nexport const SYNC_LOCK_PATH = '.omd/sync.lock.json';\n\nconst TargetSchema = z.object({\n managed_hash: z.string(),\n last_synced: z.string(),\n});\n\nconst SyncLockSchema = z.object({\n version: z.number().int().positive(),\n design_md_hash: z.string(),\n targets: z.record(z.string(), TargetSchema),\n});\n\nexport type SyncTarget = z.infer<typeof TargetSchema>;\nexport type SyncLock = z.infer<typeof SyncLockSchema>;\n\nfunction lockPath(projectRoot: string): string {\n return join(projectRoot, SYNC_LOCK_PATH);\n}\n\nexport function readLock(projectRoot: string): SyncLock | null {\n const path = lockPath(projectRoot);\n if (!existsSync(path)) return null;\n const raw = readFileSync(path, 'utf8');\n const parsed = JSON.parse(raw);\n return SyncLockSchema.parse(parsed);\n}\n\nexport function writeLock(projectRoot: string, lock: SyncLock): void {\n const validated = SyncLockSchema.parse(lock);\n const path = lockPath(projectRoot);\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, JSON.stringify(validated, null, 2) + '\\n', 'utf8');\n}\n\nexport function createLock(designMdHash: string): SyncLock {\n return {\n version: SYNC_LOCK_VERSION,\n design_md_hash: designMdHash,\n targets: {},\n };\n}\n\nexport function updateTarget(\n projectRoot: string,\n target: string,\n managedHash: string\n): SyncLock {\n const existing = readLock(projectRoot) ?? createLock('');\n const next: SyncLock = {\n ...existing,\n targets: {\n ...existing.targets,\n [target]: {\n managed_hash: managedHash,\n last_synced: new Date().toISOString(),\n },\n },\n };\n writeLock(projectRoot, next);\n return next;\n}\n\nexport function updateDesignMdHash(\n projectRoot: string,\n designMdHash: string\n): SyncLock {\n const existing = readLock(projectRoot) ?? createLock(designMdHash);\n const next: SyncLock = { ...existing, design_md_hash: designMdHash };\n writeLock(projectRoot, next);\n return next;\n}\n"],"mappings":"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","names":["existsSync","join","readFileSync","writeFileSync","mkdirSync","existsSync","dirname","join","join","existsSync","readFileSync","mkdirSync","dirname","writeFileSync","join","existsSync","readFileSync","existsSync","join"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-design-cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Interactive CLI to generate DESIGN.md files for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cli/init.ts","../src/core/vocabulary.ts","../src/core/recommend.ts","../src/core/init-deprecate.ts"],"sourcesContent":["import * as p from '@clack/prompts';\nimport pc from 'picocolors';\nimport {\n writeFileSync,\n readFileSync,\n mkdirSync,\n existsSync,\n} from 'node:fs';\nimport { join, relative, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { buildDeltaSet } from '../core/vocabulary.js';\nimport { recommend } from '../core/recommend.js';\nimport { deprecateDesignMd } from '../core/init-deprecate.js';\n\nexport interface InitRecommendOptions {\n description: string;\n topK?: number;\n json?: boolean;\n}\n\nexport interface InitPrepareOptions {\n dir?: string;\n ref: string;\n description: string;\n reason?: string;\n json?: boolean;\n}\n\nexport function runInitRecommend(opts: InitRecommendOptions): number {\n const hits = recommend(opts.description, { topK: opts.topK ?? 5 });\n const delta = buildDeltaSet(opts.description);\n\n if (opts.json) {\n process.stdout.write(\n JSON.stringify(\n {\n description: opts.description,\n recommendations: hits,\n delta_set: delta,\n },\n null,\n 2\n )\n );\n process.stdout.write('\\n');\n return 0;\n }\n\n p.intro(pc.bold('omd init — recommend'));\n p.log.message(pc.dim(`Query: \"${opts.description}\"\\n`));\n\n if (delta.matchedKeywords.length > 0) {\n p.log.message(\n pc.bold('Matched keywords: ') +\n delta.matchedKeywords\n .map((k) => pc.cyan(k.keyword) + pc.dim(` (${k.modifier.toFixed(2)})`))\n .join(', ')\n );\n }\n if (delta.warnings.length > 0) {\n for (const w of delta.warnings) p.log.warn(w);\n }\n\n p.log.message(pc.bold('\\nTop references:'));\n for (const [i, hit] of hits.entries()) {\n const scoreStr = pc.dim(`[${hit.score.toFixed(2)}]`);\n const matched = hit.matchedKeywords.length > 0\n ? pc.green(hit.matchedKeywords.join(', '))\n : pc.dim('(no direct tag match)');\n p.log.message(\n ` ${i + 1}. ${pc.bold(hit.id.padEnd(14))} ${scoreStr} ${pc.dim(hit.category.padEnd(14))} ${matched}`\n );\n }\n\n p.outro(\n pc.dim('Next: `omd init prepare --ref <id> --description \"...\"` to stage.')\n );\n return 0;\n}\n\nexport function runInitPrepare(opts: InitPrepareOptions): number {\n const projectRoot = opts.dir ?? process.cwd();\n const relRoot = relative(process.cwd(), projectRoot) || '.';\n\n const refPath = findReferencePath(opts.ref);\n if (!refPath) {\n console.error(pc.red(`omd init prepare: reference not found: ${opts.ref}`));\n return 1;\n }\n\n const referenceMd = readFileSync(refPath, 'utf8');\n const delta = buildDeltaSet(opts.description);\n\n // Handle existing DESIGN.md\n const deprecate = deprecateDesignMd({\n projectRoot,\n newReference: opts.ref,\n reason: opts.reason ?? 'user-initiated omd init',\n });\n\n // Write init-context.json that the omd:init skill consumes.\n // Note: we deliberately do NOT persist reference_path (absolute paths are\n // fragile across machines / npm reinstalls). The skill reads reference_md\n // directly from this command's --json output, and can re-fetch reference\n // content via `omd reference show <id>` if needed.\n const contextPath = join(projectRoot, '.omd', 'init-context.json');\n mkdirSync(join(projectRoot, '.omd'), { recursive: true });\n const context = {\n schema: 'omd.init-context/v1',\n created_at: new Date().toISOString(),\n reference_id: opts.ref,\n description: opts.description,\n delta_set: delta,\n deprecated_from: deprecate.renamed ? deprecate.to : null,\n };\n writeFileSync(contextPath, JSON.stringify(context, null, 2) + '\\n', 'utf8');\n\n if (opts.json) {\n process.stdout.write(\n JSON.stringify(\n {\n project_root: projectRoot,\n reference_path: refPath,\n context_path: contextPath,\n deprecated_from: deprecate.renamed ? deprecate.to : null,\n reference_md: referenceMd,\n delta_set: delta,\n },\n null,\n 2\n )\n );\n process.stdout.write('\\n');\n return 0;\n }\n\n p.intro(pc.bold('omd init — prepare') + pc.dim(` (${relRoot})`));\n p.log.message(`Reference: ${pc.cyan(opts.ref)}`);\n p.log.message(`Description: ${pc.dim(opts.description)}`);\n if (deprecate.renamed) {\n p.log.warn(\n `Existing DESIGN.md renamed → ${relative(projectRoot, deprecate.to)}`\n );\n }\n p.log.success(\n `Context staged → ${relative(projectRoot, contextPath)}`\n );\n p.outro(\n pc.dim(\n 'Next: have your agent (Claude Code / Codex / OpenCode) run the `omd:init` skill to generate DESIGN.md from this context.'\n )\n );\n return 0;\n}\n\nfunction findReferencePath(refId: string): string | null {\n const root = findRepoRoot();\n if (!root) return null;\n const path = join(root, 'references', refId, 'DESIGN.md');\n return existsSync(path) ? path : null;\n}\n\nfunction findRepoRoot(): string | null {\n let cur = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 8; i++) {\n if (existsSync(join(cur, 'references'))) return cur;\n const parent = dirname(cur);\n if (parent === cur) break;\n cur = parent;\n }\n return null;\n}\n","import { readFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, join } from 'node:path';\n\nexport interface AxisSpec {\n type: 'int' | 'float' | 'enum';\n domain: [number, number];\n unit?: string;\n step?: number;\n}\n\nexport interface KeywordAxes {\n [axis: string]: { delta: number; range: [number, number] };\n}\n\nexport interface KeywordSpec {\n group: 'tone' | 'density' | 'formality' | 'domain';\n axes: KeywordAxes;\n conflicts_with: string[];\n voice_hints: string[];\n coupled_axes?: string[];\n sources?: string[];\n}\n\nexport interface Vocabulary {\n version: number;\n generated_at: string;\n axis_registry: Record<string, AxisSpec>;\n modifiers: Record<string, number>;\n keywords: Record<string, KeywordSpec>;\n}\n\nexport interface SynonymsFile {\n version: number;\n map: Record<string, string>;\n}\n\nlet cachedVocab: Vocabulary | null = null;\nlet cachedSynonyms: SynonymsFile | null = null;\n\nfunction dataDir(): string {\n const here = dirname(fileURLToPath(import.meta.url));\n // When built (dist/) we are at dist/*.js → data/ is ../data\n // When running from src/ via tsx we are at src/core/ → data/ is ../../data\n const candidates = [\n join(here, '..', 'data'),\n join(here, '..', '..', 'data'),\n ];\n for (const c of candidates) {\n try {\n readFileSync(join(c, 'vocabulary.json'), 'utf8');\n return c;\n } catch {\n // continue\n }\n }\n throw new Error('data/vocabulary.json not found relative to ' + here);\n}\n\nexport function loadVocabulary(): Vocabulary {\n if (cachedVocab) return cachedVocab;\n const path = join(dataDir(), 'vocabulary.json');\n cachedVocab = JSON.parse(readFileSync(path, 'utf8')) as Vocabulary;\n return cachedVocab;\n}\n\nexport function loadSynonyms(): SynonymsFile {\n if (cachedSynonyms) return cachedSynonyms;\n const path = join(dataDir(), 'synonyms.json');\n cachedSynonyms = JSON.parse(readFileSync(path, 'utf8')) as SynonymsFile;\n return cachedSynonyms;\n}\n\nexport interface ResolvedKeyword {\n keyword: string;\n modifier: number;\n matchedAs: 'direct' | 'synonym';\n synonymSource?: string;\n}\n\nconst MODIFIER_RE = /\\b(primarily|mostly|slightly|very|not)\\s+([a-z][a-z-]*)/gi;\n\nexport function tokenize(description: string): string[] {\n return description\n .toLowerCase()\n .split(/[^a-z-]+/)\n .filter(Boolean);\n}\n\nexport function extractKeywords(description: string): ResolvedKeyword[] {\n const vocab = loadVocabulary();\n const { map: synonyms } = loadSynonyms();\n\n const lower = description.toLowerCase();\n const modifierOverrides = new Map<string, number>();\n let match: RegExpExecArray | null;\n const modRe = new RegExp(MODIFIER_RE.source, MODIFIER_RE.flags);\n while ((match = modRe.exec(lower)) !== null) {\n const modName = match[1];\n const target = match[2];\n const value = vocab.modifiers[modName];\n if (value !== undefined) modifierOverrides.set(target, value);\n }\n\n const tokens = tokenize(description);\n const seen = new Set<string>();\n const results: ResolvedKeyword[] = [];\n\n for (const token of tokens) {\n if (seen.has(token)) continue;\n seen.add(token);\n\n if (vocab.keywords[token]) {\n results.push({\n keyword: token,\n modifier: modifierOverrides.get(token) ?? 1.0,\n matchedAs: 'direct',\n });\n continue;\n }\n\n const syn = synonyms[token];\n if (syn && vocab.keywords[syn]) {\n results.push({\n keyword: syn,\n modifier: modifierOverrides.get(token) ?? 1.0,\n matchedAs: 'synonym',\n synonymSource: token,\n });\n }\n }\n\n return results;\n}\n\nexport interface ConflictResolution {\n kept: ResolvedKeyword[];\n dropped: Array<{ keyword: string; reason: string }>;\n warnings: string[];\n}\n\nexport function resolveConflicts(\n keywords: ResolvedKeyword[]\n): ConflictResolution {\n const vocab = loadVocabulary();\n const kept: ResolvedKeyword[] = [];\n const dropped: Array<{ keyword: string; reason: string }> = [];\n\n // Classify each keyword: kept if strictly higher modifier than any conflicter, else dropped.\n for (const kw of keywords) {\n const spec = vocab.keywords[kw.keyword];\n if (!spec) continue;\n\n const conflictingHere = keywords.filter(\n (other) =>\n other.keyword !== kw.keyword &&\n spec.conflicts_with.includes(other.keyword)\n );\n\n if (conflictingHere.length === 0) {\n kept.push(kw);\n continue;\n }\n\n const strictlyHigherThanAll = conflictingHere.every(\n (c) => kw.modifier > c.modifier + 1e-9\n );\n if (strictlyHigherThanAll) {\n kept.push(kw);\n continue;\n }\n\n dropped.push({\n keyword: kw.keyword,\n reason: `conflicts with ${conflictingHere.map((c) => c.keyword).join(',')}`,\n });\n }\n\n // Emit warnings ONLY for conflict groups where no member was kept\n // (genuine tie — user should disambiguate).\n const warnings: string[] = [];\n const keptSet = new Set(kept.map((k) => k.keyword));\n const warned = new Set<string>();\n for (const kw of keywords) {\n const spec = vocab.keywords[kw.keyword];\n if (!spec) continue;\n const conflictingHere = keywords.filter(\n (other) =>\n other.keyword !== kw.keyword &&\n spec.conflicts_with.includes(other.keyword)\n );\n if (conflictingHere.length === 0) continue;\n const groupKeys = [kw.keyword, ...conflictingHere.map((c) => c.keyword)];\n const groupHasWinner = groupKeys.some((k) => keptSet.has(k));\n if (groupHasWinner) continue;\n const pairKey = [...new Set(groupKeys)].sort().join(',');\n if (warned.has(pairKey)) continue;\n warned.add(pairKey);\n warnings.push(\n `${kw.keyword} ↔ ${conflictingHere.map((c) => c.keyword).join(',')}: use \"primarily <kw>\" or \"very <kw>\" to pick a winner`\n );\n }\n\n return { kept, dropped, warnings };\n}\n\nexport interface DeltaSet {\n axes: Record<string, { value: number; rangeUnion: [number, number]; sources: string[] }>;\n voiceHints: string[];\n unresolved: string[];\n warnings: string[];\n droppedKeywords: Array<{ keyword: string; reason: string }>;\n matchedKeywords: ResolvedKeyword[];\n}\n\nfunction clamp(value: number, lo: number, hi: number): number {\n return Math.max(lo, Math.min(hi, value));\n}\n\nfunction snap(value: number, spec: AxisSpec): number {\n if (spec.type === 'int') return Math.round(value);\n if (spec.type === 'enum') {\n const [lo, hi] = spec.domain;\n return Math.abs(value - lo) < Math.abs(value - hi) ? lo : hi;\n }\n return Number(value.toFixed(3));\n}\n\nexport function buildDeltaSet(description: string): DeltaSet {\n const vocab = loadVocabulary();\n const matched = extractKeywords(description);\n const { kept, dropped, warnings } = resolveConflicts(matched);\n\n const axes: DeltaSet['axes'] = {};\n const voiceHintSet = new Set<string>();\n\n for (const kw of kept) {\n const spec = vocab.keywords[kw.keyword];\n const multiplier = kw.modifier;\n\n for (const hint of spec.voice_hints) voiceHintSet.add(hint);\n\n for (const [axisName, axisDelta] of Object.entries(spec.axes)) {\n const bucket = axes[axisName] ?? {\n value: 0,\n rangeUnion: [axisDelta.range[0], axisDelta.range[1]] as [number, number],\n sources: [] as string[],\n };\n bucket.value += axisDelta.delta * multiplier;\n bucket.rangeUnion = [\n Math.min(bucket.rangeUnion[0], axisDelta.range[0]),\n Math.max(bucket.rangeUnion[1], axisDelta.range[1]),\n ];\n bucket.sources.push(kw.keyword);\n axes[axisName] = bucket;\n }\n }\n\n for (const [axisName, bucket] of Object.entries(axes)) {\n const registry = vocab.axis_registry[axisName];\n if (!registry) continue;\n let v = clamp(bucket.value, bucket.rangeUnion[0], bucket.rangeUnion[1]);\n v = clamp(v, registry.domain[0], registry.domain[1]);\n bucket.value = snap(v, registry);\n bucket.sources.sort();\n }\n\n return {\n axes,\n voiceHints: [...voiceHintSet],\n unresolved: [],\n warnings,\n droppedKeywords: dropped,\n matchedKeywords: kept,\n };\n}\n\nexport function listKeywords(): string[] {\n return Object.keys(loadVocabulary().keywords).sort();\n}\n","import { readFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, join } from 'node:path';\nimport { tokenize } from './vocabulary.js';\n\nexport interface ReferenceTag {\n id: string;\n color: string;\n category: string;\n keywords: string[];\n}\n\nexport interface RecommendHit {\n id: string;\n category: string;\n color: string;\n keywords: string[];\n score: number;\n matchedKeywords: string[];\n matchedCategories: string[];\n}\n\nconst CATEGORY_HINTS: Record<string, string[]> = {\n Consumer: [\n 'marketplace',\n 'shopping',\n 'ecommerce',\n 'consumer',\n 'b2c',\n 'retail',\n 'subscription',\n 'family',\n 'families',\n 'meal',\n 'meals',\n 'meal-kit',\n 'food',\n 'travel',\n 'social',\n 'community',\n 'buyer',\n 'seller',\n 'parents',\n 'kids',\n 'lifestyle',\n 'recipe',\n 'recipes',\n ],\n Fintech: [\n 'fintech',\n 'banking',\n 'bank',\n 'payment',\n 'payments',\n 'crypto',\n 'trading',\n 'wallet',\n 'invest',\n 'investing',\n 'money',\n 'finance',\n 'financial',\n 'lending',\n 'remittance',\n 'tax',\n ],\n 'Developer Tools': [\n 'developer',\n 'devtool',\n 'devtools',\n 'dev-tool',\n 'deploy',\n 'deployment',\n 'build',\n 'ci',\n 'cd',\n 'cli',\n 'sdk',\n 'editor',\n 'ide',\n 'engineering',\n 'compiler',\n 'runtime',\n ],\n AI: [\n 'ai',\n 'ml',\n 'llm',\n 'agent',\n 'agents',\n 'model',\n 'models',\n 'inference',\n 'gpt',\n 'chatbot',\n 'rag',\n 'embedding',\n 'embeddings',\n 'mcp',\n ],\n 'Design Tools': [\n 'design',\n 'design-tool',\n 'whiteboard',\n 'prototype',\n 'prototyping',\n 'wireframe',\n 'wireframes',\n 'mockup',\n 'mockups',\n 'figma-like',\n 'illustration',\n 'canvas',\n ],\n Productivity: [\n 'saas',\n 'workspace',\n 'team',\n 'teams',\n 'project-management',\n 'enterprise',\n 'b2b',\n 'crm',\n 'docs',\n 'wiki',\n 'collaboration',\n 'kanban',\n 'scheduling',\n 'meetings',\n ],\n Backend: [\n 'backend',\n 'database',\n 'db',\n 'api',\n 'apis',\n 'observability',\n 'monitoring',\n 'logging',\n 'analytics',\n 'pipeline',\n 'data-pipeline',\n 'streaming',\n 'queue',\n 'cache',\n ],\n Automotive: [\n 'car',\n 'cars',\n 'vehicle',\n 'vehicles',\n 'auto',\n 'automotive',\n 'driving',\n 'ev',\n 'electric-vehicle',\n ],\n Marketing: [\n 'marketing',\n 'seo',\n 'campaign',\n 'campaigns',\n 'newsletter',\n 'email-marketing',\n 'attribution',\n ],\n};\n\nfunction matchedCategoriesFor(\n queryTokens: Set<string>,\n queryStems: Set<string>\n): Set<string> {\n const out = new Set<string>();\n for (const [category, hints] of Object.entries(CATEGORY_HINTS)) {\n for (const hint of hints) {\n if (queryTokens.has(hint) || queryStems.has(stem(hint))) {\n out.add(category);\n break;\n }\n }\n }\n return out;\n}\n\nlet cachedTags: ReferenceTag[] | null = null;\n\nfunction stem(s: string): string {\n // Minimal English suffix stripping for recall across -ing/-ed/-s variants.\n // Preserves stems of length ≥3 so we don't collapse short words.\n let out = s;\n for (const suffix of ['ing', 'ed', 'ly', 'es', 's']) {\n if (out.length - suffix.length >= 3 && out.endsWith(suffix)) {\n out = out.slice(0, -suffix.length);\n break;\n }\n }\n return out;\n}\n\nfunction tagsFilePath(): string {\n const here = dirname(fileURLToPath(import.meta.url));\n const candidates = [\n join(here, '..', 'data', 'reference-tags.md'),\n join(here, '..', '..', 'data', 'reference-tags.md'),\n ];\n for (const c of candidates) {\n try {\n readFileSync(c, 'utf8');\n return c;\n } catch {\n // continue\n }\n }\n throw new Error('data/reference-tags.md not found');\n}\n\nconst ROW_RE = /^\\|\\s*([a-z0-9._-]+)\\s*\\|\\s*([^|]*?)\\s*\\|\\s*([^|]*?)\\s*\\|\\s*([^|]*?)\\s*\\|$/i;\n\nexport function loadReferenceTags(): ReferenceTag[] {\n if (cachedTags) return cachedTags;\n const raw = readFileSync(tagsFilePath(), 'utf8');\n const rows: ReferenceTag[] = [];\n for (const line of raw.split('\\n')) {\n const m = ROW_RE.exec(line);\n if (!m) continue;\n const [, id, color, category, keywordsRaw] = m;\n if (id === 'id') continue; // header\n if (id.startsWith('---')) continue;\n rows.push({\n id: id.trim(),\n color: color.trim(),\n category: category.trim(),\n keywords: keywordsRaw\n .split(',')\n .map((k) => k.trim().toLowerCase())\n .filter(Boolean),\n });\n }\n cachedTags = rows;\n return rows;\n}\n\nexport interface RecommendOptions {\n topK?: number;\n diversityByCategory?: boolean;\n}\n\nexport function recommend(\n description: string,\n opts: RecommendOptions = {}\n): RecommendHit[] {\n const topK = opts.topK ?? 5;\n const diversityByCategory = opts.diversityByCategory ?? true;\n\n const tags = loadReferenceTags();\n const rawTokens = [\n ...tokenize(description),\n ...description\n .toLowerCase()\n .split(/[^a-z0-9-]+/)\n .filter(Boolean),\n ];\n const queryTokens = new Set(rawTokens);\n const queryStems = new Set(rawTokens.map(stem));\n const matchedCategories = matchedCategoriesFor(queryTokens, queryStems);\n\n // First pass: tag matches per ref, to know whether to gate category bonus.\n const tagMatchByRef = tags.map((t) =>\n t.keywords.filter(\n (kw) => queryTokens.has(kw) || queryStems.has(stem(kw))\n )\n );\n const totalTagMatches = tagMatchByRef.reduce((a, m) => a + m.length, 0);\n\n const scored: Array<RecommendHit & { _ratio: number }> = tags.map((t, i) => {\n const matched = tagMatchByRef[i];\n const tagScore = matched.length;\n const categoryHit = matchedCategories.has(t.category);\n // Category bonus normally requires ≥1 tag match (prevents flooding).\n // But when the description produces zero tag matches anywhere, we\n // fall back to category-only scoring so users still get a useful\n // top-K instead of an alphabetical no-op.\n const categoryBonus =\n categoryHit && (tagScore > 0 || totalTagMatches === 0) ? 0.5 : 0;\n const score = tagScore + categoryBonus;\n const ratio = matched.length / Math.max(1, t.keywords.length);\n return {\n id: t.id,\n category: t.category,\n color: t.color,\n keywords: t.keywords,\n score,\n matchedKeywords: matched,\n matchedCategories: categoryHit ? [t.category] : [],\n _ratio: ratio,\n };\n });\n\n scored.sort(\n (a, b) =>\n b.score - a.score || b._ratio - a._ratio || a.id.localeCompare(b.id)\n );\n\n const stripRatio = (s: typeof scored): RecommendHit[] =>\n s.map(({ _ratio, ...rest }) => {\n void _ratio;\n return rest;\n });\n\n if (!diversityByCategory) return stripRatio(scored.slice(0, topK));\n\n // Category-bucketed diversity: one top hit per category, fill with next best.\n const picked = stripRatio(scored).slice(0, 0);\n const pickedSet = new Set<string>();\n const usedCategories = new Set<string>();\n const allHits = stripRatio(scored);\n for (const hit of allHits) {\n if (picked.length >= topK) break;\n if (usedCategories.has(hit.category)) continue;\n picked.push(hit);\n pickedSet.add(hit.id);\n usedCategories.add(hit.category);\n }\n for (const hit of allHits) {\n if (picked.length >= topK) break;\n if (pickedSet.has(hit.id)) continue;\n picked.push(hit);\n pickedSet.add(hit.id);\n }\n return picked;\n}\n","import {\n existsSync,\n readFileSync,\n writeFileSync,\n unlinkSync,\n mkdirSync,\n} from 'node:fs';\nimport { dirname, join } from 'node:path';\n\nexport interface DeprecateOptions {\n projectRoot: string;\n previousReference?: string;\n newReference: string;\n preferencesReplayed?: number;\n preferencesOrphaned?: number;\n orphanFile?: string;\n reason: string;\n now?: Date;\n}\n\nexport interface DeprecateResult {\n renamed: boolean;\n from: string;\n to: string;\n}\n\nfunction deprecationHeader(opts: DeprecateOptions): string {\n const now = (opts.now ?? new Date()).toISOString();\n const lines = [\n '<!--',\n 'omd:deprecated',\n ` deprecated_at: ${now}`,\n ];\n if (opts.previousReference)\n lines.push(` previous_reference: ${opts.previousReference}`);\n lines.push(` new_reference: ${opts.newReference}`);\n if (opts.preferencesReplayed !== undefined)\n lines.push(` preferences_replayed: ${opts.preferencesReplayed}`);\n if (opts.preferencesOrphaned !== undefined)\n lines.push(` preferences_orphaned: ${opts.preferencesOrphaned}`);\n if (opts.orphanFile) lines.push(` orphan_file: ${opts.orphanFile}`);\n lines.push(` reason: ${opts.reason}`);\n lines.push('-->', '', '');\n return lines.join('\\n');\n}\n\nexport function deprecateDesignMd(opts: DeprecateOptions): DeprecateResult {\n const from = join(opts.projectRoot, 'DESIGN.md');\n const baseTo = join(opts.projectRoot, 'DESIGN_DEPRECATED.md');\n\n if (!existsSync(from)) {\n return { renamed: false, from, to: baseTo };\n }\n\n let target = baseTo;\n if (existsSync(baseTo)) {\n const ts = (opts.now ?? new Date()).toISOString().replace(/[:.]/g, '-');\n target = join(opts.projectRoot, `DESIGN_DEPRECATED.${ts}.md`);\n }\n\n mkdirSync(dirname(target), { recursive: true });\n const prior = readFileSync(from, 'utf8');\n writeFileSync(target, deprecationHeader(opts) + prior, 'utf8');\n unlinkSync(from);\n\n return { renamed: true, from, to: target };\n}\n"],"mappings":"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A;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,mBAAmB;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,IAAI;AAAA,IACF;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,gBAAgB;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,cAAc;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAA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","names":["writeFileSync","readFileSync","mkdirSync","existsSync","join","dirname","fileURLToPath","readFileSync","fileURLToPath","dirname","join","dirname","fileURLToPath","join","readFileSync","readFileSync","dirname","join","readFileSync","join","mkdirSync","writeFileSync","existsSync","dirname","fileURLToPath"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cli/sync.ts","../src/core/shims.ts","../src/core/sync-lock.ts"],"sourcesContent":["import * as p from '@clack/prompts';\nimport pc from 'picocolors';\nimport { relative } from 'node:path';\nimport {\n ALL_SHIMS,\n inspectShim,\n writeShim,\n refreshDesignMdHash,\n type Shim,\n type InspectResult,\n type WriteShimResult,\n} from '../core/shims.js';\n\nexport interface SyncOptions {\n dir?: string;\n force?: boolean;\n check?: boolean;\n}\n\nconst STATUS_LABEL: Record<InspectResult['status'], string> = {\n missing: pc.yellow('missing'),\n clean: pc.green('clean'),\n drifted: pc.red('drifted'),\n 'out-of-date': pc.cyan('out-of-date'),\n};\n\nconst WRITE_LABEL: Record<WriteShimResult['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 printDiff(label: string, current: string, proposed: string): void {\n p.log.message(pc.bold('─── current (' + label + ') ───'));\n p.log.message(current || pc.dim('(empty)'));\n p.log.message(pc.bold('─── proposed ───'));\n p.log.message(proposed);\n}\n\ntype DriftChoice = 'overwrite' | 'skip' | 'show' | 'quit';\n\nasync function promptDrift(shim: Shim, inspection: InspectResult): Promise<DriftChoice> {\n while (true) {\n const choice = await p.select<DriftChoice>({\n message: `${pc.bold(shim.relPath)} has drift — choose:`,\n options: [\n { value: 'overwrite', label: 'Overwrite with rendered content' },\n { value: 'skip', label: 'Skip (keep user edits)' },\n { value: 'show', label: 'Show diff' },\n { value: 'quit', label: 'Quit sync' },\n ],\n });\n\n if (p.isCancel(choice)) return 'quit';\n if (choice !== 'show') return choice;\n\n printDiff(\n shim.relPath,\n inspection.existing ?? '',\n inspection.rendered\n );\n }\n}\n\nexport async function runSync(opts: SyncOptions = {}): Promise<number> {\n const projectRoot = opts.dir ?? process.cwd();\n const relRoot = relative(process.cwd(), projectRoot) || '.';\n\n p.intro(pc.bold('omd sync') + pc.dim(` (${relRoot})`));\n\n const inspections = ALL_SHIMS.map((shim) => ({\n shim,\n result: inspectShim(projectRoot, shim),\n }));\n\n p.log.message(pc.bold('Status:'));\n for (const { result } of inspections) {\n const rel = relative(projectRoot, result.path);\n p.log.message(` ${STATUS_LABEL[result.status]} ${rel}`);\n }\n\n if (opts.check) {\n const unsynced = inspections.filter(\n (i) => i.result.status !== 'clean'\n );\n if (unsynced.length > 0) {\n p.outro(\n pc.red(\n `${unsynced.length} not in sync — rerun without --check to resolve.`\n )\n );\n return 1;\n }\n p.outro(pc.green('All clean.'));\n return 0;\n }\n\n const results: WriteShimResult[] = [];\n\n for (const { shim, result } of inspections) {\n if (result.status === 'clean') {\n results.push({\n id: shim.id,\n path: result.path,\n hash: '',\n status: 'unchanged',\n });\n continue;\n }\n\n if (result.status === 'drifted' && !opts.force) {\n const choice = await promptDrift(shim, result);\n if (choice === 'quit') {\n p.outro(pc.yellow('Aborted by user.'));\n return 1;\n }\n if (choice === 'skip') {\n results.push(\n writeShim(projectRoot, shim, { onDrift: 'skip' })\n );\n continue;\n }\n // overwrite\n results.push(\n writeShim(projectRoot, shim, { onDrift: 'overwrite' })\n );\n continue;\n }\n\n // missing / out-of-date / (drifted with --force)\n const onDrift = opts.force ? 'overwrite' : 'error';\n results.push(writeShim(projectRoot, shim, { onDrift }));\n }\n\n refreshDesignMdHash(projectRoot);\n\n p.log.message(pc.bold('\\nResults:'));\n for (const r of results) {\n const rel = relative(projectRoot, r.path);\n p.log.message(` ${WRITE_LABEL[r.status]} ${rel}`);\n }\n\n const drifted = results.filter((r) => r.status === 'skipped-drift').length;\n const changed = results.filter(\n (r) => r.status === 'created' || r.status === 'updated'\n ).length;\n\n p.outro(\n drifted > 0\n ? pc.yellow(`${changed} written, ${drifted} skipped (drift).`)\n : pc.green(`${changed} written.`)\n );\n\n return 0;\n}\n","import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport {\n parseBlock,\n writeBlock,\n hashContent,\n hasDrift,\n} from './sync-marker.js';\nimport { updateTarget, updateDesignMdHash } from './sync-lock.js';\n\nexport const MANAGED_BLOCK_VERSION = 1;\n\nexport type ShimId = 'claude' | 'agents' | 'cursor';\n\nexport type ShimMode = 'block' | 'whole';\n\nexport interface Shim {\n id: ShimId;\n relPath: string;\n mode: ShimMode;\n render(): string;\n}\n\nconst CLAUDE_BODY = `# Design System (oh-my-design)\n\nThe authoritative brand & UI spec is **@./DESIGN.md**.\nRead before any UI/styling/microcopy/motion work.\n\nPreference log (pending corrections): @./.omd/preferences.md\n\nPrecedence: DESIGN.md > preferences.md > your defaults.`;\n\nconst AGENTS_BODY = `## Design System (oh-my-design)\n\n**Before any UI, styling, copy, or motion change, open and read \\`./DESIGN.md\\` in full.** It is the authoritative brand/design spec. Treat its tokens, voice, and component rules as binding unless the user overrides in chat.\n\nIf present, read \\`./.omd/preferences.md\\` — pending corrections not yet folded into DESIGN.md. Apply them; flag conflicts.`;\n\nconst CURSOR_FRONTMATTER = `---\ndescription: Authoritative brand & UI design system. Read DESIGN.md before UI work.\nglobs:\n - \"**/*.tsx\"\n - \"**/*.jsx\"\n - \"**/*.vue\"\n - \"**/*.svelte\"\n - \"**/*.css\"\n - \"**/*.scss\"\n - \"**/tailwind.config.*\"\n - \"**/components/**\"\n - \"**/app/**/page.*\"\nalwaysApply: false\n---`;\n\nconst CURSOR_BODY = `The authoritative design spec lives at \\`@DESIGN.md\\` (repo root). Open and read before generating/modifying UI.\n\nPending preference corrections: \\`@.omd/preferences.md\\`.\n\nPrecedence: DESIGN.md > preferences.md > framework defaults.`;\n\nexport const CLAUDE_SHIM: Shim = {\n id: 'claude',\n relPath: 'CLAUDE.md',\n mode: 'block',\n render: () => CLAUDE_BODY,\n};\n\nexport const AGENTS_SHIM: Shim = {\n id: 'agents',\n relPath: 'AGENTS.md',\n mode: 'block',\n render: () => AGENTS_BODY,\n};\n\nexport const CURSOR_SHIM: Shim = {\n id: 'cursor',\n relPath: '.cursor/rules/omd-design.mdc',\n mode: 'whole',\n render: () => {\n const hash = hashContent(CURSOR_BODY);\n return `${CURSOR_FRONTMATTER}\\n\\n<!-- omd:start v=${MANAGED_BLOCK_VERSION} hash=${hash} -->\\n${CURSOR_BODY}\\n<!-- omd:end -->\\n`;\n },\n};\n\nexport const ALL_SHIMS: readonly Shim[] = [\n CLAUDE_SHIM,\n AGENTS_SHIM,\n CURSOR_SHIM,\n] as const;\n\nexport type DriftAction = 'overwrite' | 'skip' | 'error';\n\nexport interface WriteShimResult {\n id: ShimId;\n path: string;\n hash: string;\n status: 'created' | 'updated' | 'unchanged' | 'skipped-drift';\n}\n\nexport interface WriteShimsOptions {\n onDrift?: DriftAction;\n}\n\nfunction resolvePath(projectRoot: string, relPath: string): string {\n return join(projectRoot, relPath);\n}\n\nfunction readFileOrEmpty(path: string): string {\n return existsSync(path) ? readFileSync(path, 'utf8') : '';\n}\n\nfunction ensureDir(path: string): void {\n mkdirSync(dirname(path), { recursive: true });\n}\n\nexport type InspectStatus =\n | 'missing'\n | 'clean'\n | 'drifted'\n | 'out-of-date';\n\nexport interface InspectResult {\n id: ShimId;\n path: string;\n status: InspectStatus;\n existing?: string;\n rendered: string;\n}\n\nexport function inspectShim(projectRoot: string, shim: Shim): InspectResult {\n const abs = resolvePath(projectRoot, shim.relPath);\n const existing = readFileOrEmpty(abs);\n const fileExists = existing !== '';\n\n if (shim.mode === 'whole') {\n const rendered = shim.render();\n if (!fileExists) {\n return { id: shim.id, path: abs, status: 'missing', rendered };\n }\n if (existing === rendered) {\n return { id: shim.id, path: abs, status: 'clean', existing, rendered };\n }\n return { id: shim.id, path: abs, status: 'drifted', existing, rendered };\n }\n\n const managed = shim.render();\n const block = parseBlock(existing);\n\n if (!block) {\n return {\n id: shim.id,\n path: abs,\n status: 'missing',\n existing: fileExists ? existing : undefined,\n rendered: managed,\n };\n }\n\n if (hasDrift(block)) {\n return {\n id: shim.id,\n path: abs,\n status: 'drifted',\n existing: block.content,\n rendered: managed,\n };\n }\n\n if (block.content === managed) {\n return {\n id: shim.id,\n path: abs,\n status: 'clean',\n existing: block.content,\n rendered: managed,\n };\n }\n\n return {\n id: shim.id,\n path: abs,\n status: 'out-of-date',\n existing: block.content,\n rendered: managed,\n };\n}\n\nexport function inspectAllShims(projectRoot: string): InspectResult[] {\n return ALL_SHIMS.map((s) => inspectShim(projectRoot, s));\n}\n\nexport function writeShim(\n projectRoot: string,\n shim: Shim,\n opts: WriteShimsOptions = {}\n): WriteShimResult {\n const onDrift = opts.onDrift ?? 'error';\n const abs = resolvePath(projectRoot, shim.relPath);\n const existing = readFileOrEmpty(abs);\n const fileExists = existing !== '';\n\n if (shim.mode === 'whole') {\n const rendered = shim.render();\n const newHash = hashContent(rendered);\n\n if (fileExists && existing !== rendered) {\n const existingHash = hashContent(existing);\n if (onDrift === 'error') {\n throw new Error(\n `drift detected in ${shim.relPath} (existing hash ${existingHash} != rendered ${newHash}); rerun with onDrift=overwrite to force`\n );\n }\n if (onDrift === 'skip') {\n updateTarget(projectRoot, shim.relPath, existingHash);\n return {\n id: shim.id,\n path: abs,\n hash: existingHash,\n status: 'skipped-drift',\n };\n }\n }\n\n if (fileExists && existing === rendered) {\n updateTarget(projectRoot, shim.relPath, newHash);\n return { id: shim.id, path: abs, hash: newHash, status: 'unchanged' };\n }\n\n ensureDir(abs);\n writeFileSync(abs, rendered, 'utf8');\n updateTarget(projectRoot, shim.relPath, newHash);\n return {\n id: shim.id,\n path: abs,\n hash: newHash,\n status: fileExists ? 'updated' : 'created',\n };\n }\n\n const managed = shim.render();\n const existingBlock = parseBlock(existing);\n\n if (existingBlock && hasDrift(existingBlock)) {\n if (onDrift === 'error') {\n throw new Error(\n `managed block in ${shim.relPath} was hand-edited; rerun with onDrift=overwrite to force`\n );\n }\n if (onDrift === 'skip') {\n updateTarget(projectRoot, shim.relPath, existingBlock.hash);\n return {\n id: shim.id,\n path: abs,\n hash: existingBlock.hash,\n status: 'skipped-drift',\n };\n }\n }\n\n if (existingBlock && existingBlock.content === managed && !hasDrift(existingBlock)) {\n updateTarget(projectRoot, shim.relPath, existingBlock.hash);\n return {\n id: shim.id,\n path: abs,\n hash: existingBlock.hash,\n status: 'unchanged',\n };\n }\n\n const { updated, hash } = writeBlock(existing, managed, MANAGED_BLOCK_VERSION);\n ensureDir(abs);\n writeFileSync(abs, updated, 'utf8');\n updateTarget(projectRoot, shim.relPath, hash);\n return {\n id: shim.id,\n path: abs,\n hash,\n status: existingBlock ? 'updated' : 'created',\n };\n}\n\nexport function writeAllShims(\n projectRoot: string,\n opts: WriteShimsOptions = {}\n): WriteShimResult[] {\n const results = ALL_SHIMS.map((shim) => writeShim(projectRoot, shim, opts));\n refreshDesignMdHash(projectRoot);\n return results;\n}\n\nexport function refreshDesignMdHash(projectRoot: string): string | null {\n const designMdPath = join(projectRoot, 'DESIGN.md');\n if (!existsSync(designMdPath)) return null;\n const hash = hashContent(readFileSync(designMdPath, 'utf8'));\n updateDesignMdHash(projectRoot, hash);\n return hash;\n}\n","import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { z } from 'zod';\n\nexport const SYNC_LOCK_VERSION = 1;\nexport const SYNC_LOCK_PATH = '.omd/sync.lock.json';\n\nconst TargetSchema = z.object({\n managed_hash: z.string(),\n last_synced: z.string(),\n});\n\nconst SyncLockSchema = z.object({\n version: z.number().int().positive(),\n design_md_hash: z.string(),\n targets: z.record(z.string(), TargetSchema),\n});\n\nexport type SyncTarget = z.infer<typeof TargetSchema>;\nexport type SyncLock = z.infer<typeof SyncLockSchema>;\n\nfunction lockPath(projectRoot: string): string {\n return join(projectRoot, SYNC_LOCK_PATH);\n}\n\nexport function readLock(projectRoot: string): SyncLock | null {\n const path = lockPath(projectRoot);\n if (!existsSync(path)) return null;\n const raw = readFileSync(path, 'utf8');\n const parsed = JSON.parse(raw);\n return SyncLockSchema.parse(parsed);\n}\n\nexport function writeLock(projectRoot: string, lock: SyncLock): void {\n const validated = SyncLockSchema.parse(lock);\n const path = lockPath(projectRoot);\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, JSON.stringify(validated, null, 2) + '\\n', 'utf8');\n}\n\nexport function createLock(designMdHash: string): SyncLock {\n return {\n version: SYNC_LOCK_VERSION,\n design_md_hash: designMdHash,\n targets: {},\n };\n}\n\nexport function updateTarget(\n projectRoot: string,\n target: string,\n managedHash: string\n): SyncLock {\n const existing = readLock(projectRoot) ?? createLock('');\n const next: SyncLock = {\n ...existing,\n targets: {\n ...existing.targets,\n [target]: {\n managed_hash: managedHash,\n last_synced: new Date().toISOString(),\n },\n },\n };\n writeLock(projectRoot, next);\n return next;\n}\n\nexport function updateDesignMdHash(\n projectRoot: string,\n designMdHash: string\n): SyncLock {\n const existing = readLock(projectRoot) ?? createLock(designMdHash);\n const next: SyncLock = { ...existing, design_md_hash: designMdHash };\n writeLock(projectRoot, next);\n return next;\n}\n"],"mappings":"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","names":["readFileSync","writeFileSync","mkdirSync","existsSync","dirname","join","join","existsSync","readFileSync","mkdirSync","dirname","writeFileSync","join","existsSync","readFileSync"]}