oh-my-design-cli 0.1.3 → 1.0.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.
- package/.claude/hooks/post-edit-watch.cjs +99 -0
- package/.claude/hooks/session-end-foldin.cjs +96 -0
- package/.claude/hooks/session-state-loader.cjs +64 -0
- package/.claude/hooks/skill-activation.cjs +75 -0
- package/.claude/settings.json +55 -0
- package/AGENTS.md +111 -0
- package/README.ja.md +1 -1
- package/README.ko.md +1 -1
- package/README.md +76 -203
- package/README.zh-TW.md +1 -1
- package/agents/AGENT.md +53 -0
- package/agents/omd-3d-blender.md +269 -0
- package/agents/omd-a11y-auditor.md +97 -0
- package/agents/omd-asset-curator.md +260 -0
- package/agents/omd-critic.md +181 -0
- package/agents/omd-master.md +548 -0
- package/agents/omd-microcopy.md +63 -0
- package/agents/omd-persona-tester.md +118 -0
- package/agents/omd-ui-junior.md +129 -0
- package/agents/omd-ux-engineer.md +265 -0
- package/agents/omd-ux-researcher.md +62 -0
- package/agents/omd-ux-writer.md +181 -0
- package/data/opt-out-corpus.json +141 -0
- package/data/reference-fingerprints.json +1495 -0
- package/dist/bin/oh-my-design.js +3 -818
- package/dist/bin/oh-my-design.js.map +1 -1
- package/dist/install-skills-GQPTQF5S.js +420 -0
- package/dist/install-skills-GQPTQF5S.js.map +1 -0
- package/package.json +22 -21
- package/scripts/context.cjs +91 -0
- package/scripts/postinstall.cjs +54 -0
- package/skills/omd-apply/SKILL.md +64 -53
- package/skills/omd-harness/SKILL.md +271 -0
- package/skills/omd-init/SKILL.md +1 -1
- package/skills/omd-learn/SKILL.md +56 -36
- package/skills/omd-remember/SKILL.md +94 -16
- package/skills/omd-sync/SKILL.md +141 -17
- package/dist/chunk-6YNSV3VY.js +0 -35
- package/dist/chunk-6YNSV3VY.js.map +0 -1
- package/dist/chunk-MHFYGZSO.js +0 -337
- package/dist/chunk-MHFYGZSO.js.map +0 -1
- package/dist/chunk-N2JG6N4Q.js +0 -264
- package/dist/chunk-N2JG6N4Q.js.map +0 -1
- package/dist/chunk-OOQQEUGX.js +0 -46
- package/dist/chunk-OOQQEUGX.js.map +0 -1
- package/dist/chunk-OR5DHENY.js +0 -250
- package/dist/chunk-OR5DHENY.js.map +0 -1
- package/dist/customizer-CM76752R.js +0 -8
- package/dist/customizer-CM76752R.js.map +0 -1
- package/dist/index.d.ts +0 -559
- package/dist/index.js +0 -3113
- package/dist/index.js.map +0 -1
- package/dist/init-UMM4XIV5.js +0 -675
- package/dist/init-UMM4XIV5.js.map +0 -1
- package/dist/install-skills-CM6VXFZJ.js +0 -152
- package/dist/install-skills-CM6VXFZJ.js.map +0 -1
- package/dist/learn-33LHKEJA.js +0 -140
- package/dist/learn-33LHKEJA.js.map +0 -1
- package/dist/reference-YMNAOXJQ.js +0 -47
- package/dist/reference-YMNAOXJQ.js.map +0 -1
- package/dist/reference-parser-TM3CJPNE.js +0 -10
- package/dist/reference-parser-TM3CJPNE.js.map +0 -1
- package/dist/remember-UAFA5B2O.js +0 -78
- package/dist/remember-UAFA5B2O.js.map +0 -1
- package/dist/sync-FDYRKNFE.js +0 -417
- package/dist/sync-FDYRKNFE.js.map +0 -1
- package/dist/templates/templates/design-md.hbs +0 -44
- package/dist/templates/templates/partials/agent-prompt-guide.hbs +0 -28
- package/dist/templates/templates/partials/color-palette.hbs +0 -49
- package/dist/templates/templates/partials/component-stylings.hbs +0 -28
- package/dist/templates/templates/partials/depth-elevation.hbs +0 -31
- package/dist/templates/templates/partials/dos-donts.hbs +0 -13
- package/dist/templates/templates/partials/layout.hbs +0 -30
- package/dist/templates/templates/partials/responsive.hbs +0 -25
- package/dist/templates/templates/partials/shadcn-tokens.hbs +0 -64
- package/dist/templates/templates/partials/typography.hbs +0 -43
- package/dist/templates/templates/partials/visual-theme.hbs +0 -26
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../bin/oh-my-design.ts","../../src/cli/index.ts","../../src/cli/prompts.ts","../../src/core/preview-generator.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { readFileSync, existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { run } from '../src/cli/index.js';\n\nfunction readPackageVersion(): string {\n let cur = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 8; i++) {\n const pkg = join(cur, 'package.json');\n if (existsSync(pkg)) {\n try {\n return JSON.parse(readFileSync(pkg, 'utf8')).version ?? '0.0.0';\n } catch {\n return '0.0.0';\n }\n }\n const parent = dirname(cur);\n if (parent === cur) break;\n cur = parent;\n }\n return '0.0.0';\n}\n\nconst program = new Command();\n\nprogram\n .name('oh-my-design')\n .description('Interactive CLI to generate DESIGN.md files for AI coding agents')\n .version(readPackageVersion())\n .showSuggestionAfterError(true)\n .showHelpAfterError(true);\n\nprogram\n .command('generate')\n .description('Generate DESIGN.md and interactive preview')\n .option('--config <hash>', 'Apply a config hash from the web builder')\n .action(async (opts: { config?: string }) => {\n if (opts.config) {\n // Decode config hash and generate directly without prompts\n const { writeFileSync } = await import('fs');\n const { resolve } = await import('path');\n\n // Decode the hash — format: refId|primary|font|weight|radius|dark|components|stylePrefs\n // parts[7] (stylePrefs) was added in v2. Old hashes lack it and still decode.\n let b64 = opts.config.replace(/-/g, '+').replace(/_/g, '/');\n while (b64.length % 4) b64 += '=';\n const decoded = Buffer.from(b64, 'base64').toString('utf-8');\n const parts = decoded.split('|');\n\n const refId = parts[0] || 'vercel';\n const overrides = {\n primaryColor: parts[1] || undefined,\n fontFamily: parts[2] || undefined,\n headingWeight: parts[3] || undefined,\n borderRadius: parts[4] || undefined,\n darkMode: parts[5] === '1',\n };\n const components = parts[6] ? parts[6].split(',').filter(Boolean) : [];\n // stylePreferences are decoded but not yet applied here — the CLI's\n // customizer (src/core/customizer.ts) does not mirror the web's\n // stylePreferences inline-rewrite logic yet. Parsed here so old and\n // new hashes coexist without error; CLI integration is a follow-up.\n const stylePrefs: Record<string, string> = {};\n if (parts[7]) {\n for (const pair of parts[7].split(';')) {\n const [k, v] = pair.split('=');\n if (k && v) stylePrefs[k] = v;\n }\n }\n\n // Load reference and apply\n const { loadReference } = await import('../src/core/reference-parser.js');\n const { applyOverrides } = await import('../src/core/customizer.js');\n\n const ref = loadReference(refId);\n const mode = (overrides.primaryColor || overrides.fontFamily) ? 'customized' as const : 'as-is' as const;\n const { designMd, shadcnCss, previewData } = applyOverrides(ref, {\n primaryColor: overrides.primaryColor,\n fontFamily: overrides.fontFamily,\n headingWeight: overrides.headingWeight,\n borderRadius: overrides.borderRadius,\n darkMode: overrides.darkMode,\n }, mode, components);\n\n previewData.shadcnCss = shadcnCss;\n previewData.designMd = designMd;\n\n const outputDir = process.cwd();\n const mdPath = resolve(outputDir, 'DESIGN.md');\n writeFileSync(mdPath, designMd, 'utf-8');\n\n console.log(`\\x1b[32m✓\\x1b[0m DESIGN.md generated at ${mdPath}`);\n console.log(` Based on: ${ref.name}`);\n if (overrides.primaryColor) console.log(` Primary: ${overrides.primaryColor}`);\n if (overrides.fontFamily) console.log(` Font: ${overrides.fontFamily}`);\n if (overrides.headingWeight) console.log(` Weight: ${overrides.headingWeight}`);\n if (overrides.borderRadius) console.log(` Radius: ${overrides.borderRadius}`);\n if (overrides.darkMode) console.log(` Dark mode: included`);\n } else {\n await run();\n }\n });\n\nprogram\n .command('install-skills')\n .description('Install omd:* skill files into agent directories (.claude/, .codex/, .opencode/)')\n .option('--dir <path>', 'Project root (defaults to cwd)')\n .option('--agent <name...>', 'Restrict to specific agents (claude-code | codex | opencode)')\n .option('--force', 'Overwrite existing files even without the omd marker')\n .action(\n async (opts: { dir?: string; agent?: string[]; force?: boolean }) => {\n const { runInstallSkills } = await import('../src/cli/install-skills.js');\n const validAgents = ['claude-code', 'codex', 'opencode'] as const;\n type Agent = (typeof validAgents)[number];\n const agents = opts.agent\n ? (opts.agent.filter((a): a is Agent =>\n (validAgents as readonly string[]).includes(a)\n ) as Agent[])\n : undefined;\n const code = await runInstallSkills({\n dir: opts.dir,\n agents,\n force: opts.force,\n });\n if (code !== 0) process.exit(code);\n }\n );\n\nconst referenceCmd = program\n .command('reference')\n .description('Inspect bundled design references');\n\nreferenceCmd\n .command('list')\n .description('List all bundled reference ids')\n .action(async () => {\n const { runReferenceList } = await import('../src/cli/reference.js');\n process.exit(runReferenceList());\n });\n\nreferenceCmd\n .command('show <id>')\n .description('Print the full reference DESIGN.md to stdout')\n .action(async (id: string) => {\n const { runReferenceShow } = await import('../src/cli/reference.js');\n process.exit(runReferenceShow(id));\n });\n\nconst initCmd = program\n .command('init')\n .description('Bootstrap DESIGN.md from a reference + project description');\n\ninitCmd\n .command('recommend <description...>')\n .description('Recommend references matching a project description')\n .option('--top <n>', 'Number of recommendations', '5')\n .option('--json', 'Emit machine-readable JSON')\n .action(\n async (descParts: string[], opts: { top?: string; json?: boolean }) => {\n const { runInitRecommend } = await import('../src/cli/init.js');\n const code = runInitRecommend({\n description: descParts.join(' '),\n topK: opts.top ? Number(opts.top) : 5,\n json: opts.json,\n });\n if (code !== 0) process.exit(code);\n }\n );\n\ninitCmd\n .command('prepare')\n .description('Stage init context: rename existing DESIGN.md, compute delta_set, write .omd/init-context.json')\n .requiredOption('--ref <id>', 'Reference id (e.g. vercel, linear.app)')\n .requiredOption('--description <text>', 'Project description')\n .option('--dir <path>', 'Project root (defaults to cwd)')\n .option('--reason <text>', 'Reason for deprecation header', 'user-initiated omd init')\n .option('--json', 'Emit machine-readable JSON')\n .action(async (opts: { ref: string; description: string; dir?: string; reason?: string; json?: boolean }) => {\n const { runInitPrepare } = await import('../src/cli/init.js');\n const code = runInitPrepare(opts);\n if (code !== 0) process.exit(code);\n });\n\nprogram\n .command('learn')\n .description('List preferences and flip status (applied/rejected)')\n .option('--dir <path>', 'Project root (defaults to cwd)')\n .option('--all', 'Include all statuses (default: pending only)')\n .option('--status <s>', 'Filter by status: pending|applied|rejected|superseded')\n .option('--scope <s>', 'Filter by scope')\n .option('--mark-applied <id>', 'Mark entry as applied')\n .option('--mark-rejected <id>', 'Mark entry as rejected (requires --reason)')\n .option('--reason <text>', 'Reason for rejection')\n .option('--hash <value>', 'DESIGN.md hash to stamp on applied entry (defaults to current file)')\n .action(async (opts: Record<string, string | boolean>) => {\n const { runLearn } = await import('../src/cli/learn.js');\n const code = await runLearn(opts as never);\n if (code !== 0) process.exit(code);\n });\n\nprogram\n .command('remember <note...>')\n .description('Append a design preference/correction to .omd/preferences.md')\n .option('--dir <path>', 'Project root (defaults to cwd)')\n .option('--scope <scope>', 'Explicit scope (e.g. color, components.button)')\n .option('--agent <name>', 'Source agent (e.g. claude-code, codex)')\n .option('--context <text>', 'Source file/line context')\n .action(\n async (\n noteParts: string[],\n opts: {\n dir?: string;\n scope?: string;\n agent?: string;\n context?: string;\n }\n ) => {\n const { runRemember } = await import('../src/cli/remember.js');\n const code = await runRemember(noteParts.join(' '), opts as never);\n if (code !== 0) process.exit(code);\n }\n );\n\nprogram\n .command('sync')\n .description('Sync DESIGN.md shim files (CLAUDE.md, AGENTS.md, .cursor/rules/omd-design.mdc)')\n .option('--dir <path>', 'Project root (defaults to cwd)')\n .option('--force', 'Overwrite drift without prompting')\n .option('--check', 'Exit non-zero if any shim has drift; do not write')\n .action(async (opts: { dir?: string; force?: boolean; check?: boolean }) => {\n const { runSync } = await import('../src/cli/sync.js');\n const code = await runSync(opts);\n if (code !== 0) process.exit(code);\n });\n\nprogram\n .command('preview')\n .description('Open the preview HTML in the default browser')\n .action(async () => {\n const { existsSync } = await import('fs');\n const { resolve } = await import('path');\n const previewPath = resolve(process.cwd(), 'DESIGN.preview.html');\n\n if (!existsSync(previewPath)) {\n console.error('No DESIGN.preview.html found. Run `oh-my-design generate` first.');\n process.exit(1);\n }\n\n const { exec } = await import('child_process');\n const platform = process.platform;\n const cmd = platform === 'darwin' ? 'open' : platform === 'win32' ? 'start' : 'xdg-open';\n exec(`${cmd} \"${previewPath}\"`);\n console.log(`Opening ${previewPath}...`);\n });\n\nprogram.parse();\n","import * as p from '@clack/prompts';\nimport { writeFileSync } from 'fs';\nimport { resolve } from 'path';\nimport { runPrompts } from './prompts.js';\nimport { applyOverrides } from '../core/customizer.js';\nimport { generatePreviewHtml } from '../core/preview-generator.js';\n\nexport async function run() {\n const { reference, overrides, outputMode } = await runPrompts();\n\n const s = p.spinner();\n s.start(`Building design system based on ${reference.name}...`);\n\n // Apply overrides to reference DESIGN.md\n const { designMd, shadcnCss, previewData } = applyOverrides(reference, overrides, outputMode);\n\n // Fill in the preview data with generated content\n previewData.shadcnCss = shadcnCss;\n previewData.designMd = designMd;\n\n // Generate preview HTML\n const html = generatePreviewHtml(previewData);\n\n // Write files\n const outputDir = process.cwd();\n const mdPath = resolve(outputDir, 'DESIGN.md');\n const htmlPath = resolve(outputDir, 'DESIGN.preview.html');\n\n writeFileSync(mdPath, designMd, 'utf-8');\n writeFileSync(htmlPath, html, 'utf-8');\n\n s.stop('Done!');\n\n p.log.success(`DESIGN.md → ${mdPath}`);\n p.log.success(`Preview HTML → ${htmlPath}`);\n p.outro('Open DESIGN.preview.html in your browser to explore your design system.');\n}\n","import * as p from '@clack/prompts';\nimport pc from 'picocolors';\nimport { listReferences, loadReference } from '../core/reference-parser.js';\nimport type { ReferenceEntry } from '../core/reference-parser.js';\n\n// ── Types ────────────────────────────────────────────────────────\n\nexport interface CustomOverrides {\n primaryColor?: string; // override primary color hex\n headingWeight?: string; // override heading weight\n borderRadius?: string; // override base radius\n fontFamily?: string; // override primary font\n darkMode: boolean;\n additionalNotes?: string;\n}\n\nexport interface PromptResult {\n reference: ReferenceEntry;\n overrides: CustomOverrides;\n outputMode: 'as-is' | 'customized';\n}\n\n// ── Main ─────────────────────────────────────────────────────────\n\nexport async function runPrompts(): Promise<PromptResult> {\n p.intro(`${pc.bold('oh-my-design')} — Generate your DESIGN.md from real design systems`);\n\n // Step 1: List all references grouped by category\n const refs = listReferences();\n const categories = [...new Set(refs.map((r) => r.category))];\n\n // Step 1a: Pick category\n const category = await p.select({\n message: 'Pick a category:',\n options: categories.map((cat) => {\n const count = refs.filter((r) => r.category === cat).length;\n return { value: cat, label: `${cat}`, hint: `${count} references` };\n }),\n });\n if (p.isCancel(category)) { p.cancel('Cancelled.'); process.exit(0); }\n\n // Step 1b: Pick reference within category\n const categoryRefs = refs.filter((r) => r.category === category);\n const refId = await p.select({\n message: `Select a design system reference:`,\n options: categoryRefs.map((r) => ({\n value: r.id,\n label: `${r.name}`,\n hint: `${r.primaryColor}`,\n })),\n });\n if (p.isCancel(refId)) { p.cancel('Cancelled.'); process.exit(0); }\n\n const reference = loadReference(refId as string);\n\n p.log.info(`${pc.bold(reference.name)} loaded`);\n p.log.info(` Primary: ${pc.bold(reference.colors.primary)} ${reference.colors.primaryName}`);\n p.log.info(` Font: ${reference.typography.primary}`);\n p.log.info(` Radius: ${reference.radius}`);\n if (reference.mood) {\n p.log.info(` Mood: ${reference.mood.slice(0, 120)}...`);\n }\n\n // Step 2: Use as-is or customize?\n const mode = await p.select({\n message: 'How do you want to use this design system?',\n options: [\n { value: 'as-is', label: 'Use as-is', hint: 'Copy the reference DESIGN.md directly' },\n { value: 'customized', label: 'Customize', hint: 'Modify colors, fonts, radius, etc.' },\n ],\n });\n if (p.isCancel(mode)) { p.cancel('Cancelled.'); process.exit(0); }\n\n if (mode === 'as-is') {\n const darkMode = await p.confirm({\n message: 'Include dark mode notes?',\n initialValue: false,\n });\n if (p.isCancel(darkMode)) { p.cancel('Cancelled.'); process.exit(0); }\n\n return {\n reference,\n overrides: { darkMode },\n outputMode: 'as-is',\n };\n }\n\n // Step 3: Customization prompts\n const overrides = await p.group(\n {\n primaryColor: () =>\n p.text({\n message: `Primary color (current: ${reference.colors.primary}):`,\n placeholder: 'Press enter to keep, or type a hex like #6366f1',\n validate: (val) => {\n if (val && !/^#[0-9a-fA-F]{6}$/.test(val)) return 'Invalid hex color';\n },\n }),\n\n fontFamily: () =>\n p.select({\n message: `Primary font (current: ${reference.typography.primary}):`,\n options: [\n { value: '', label: `Keep \"${reference.typography.primary}\"`, hint: 'No change' },\n { value: 'Inter', label: 'Inter', hint: 'Clean, geometric sans-serif' },\n { value: 'system-ui', label: 'System UI', hint: 'Native OS fonts' },\n { value: 'JetBrains Mono', label: 'JetBrains Mono', hint: 'Monospace, technical' },\n { value: 'Geist', label: 'Geist', hint: 'Vercel\\'s modern sans' },\n ],\n }),\n\n headingWeight: () =>\n p.select({\n message: `Heading weight (current: ${reference.typography.headingWeight}):`,\n options: [\n { value: '', label: `Keep ${reference.typography.headingWeight}`, hint: 'No change' },\n { value: '300', label: '300 — Light', hint: 'Whisper authority (Stripe style)' },\n { value: '400', label: '400 — Regular' },\n { value: '500', label: '500 — Medium', hint: 'Balanced emphasis' },\n { value: '600', label: '600 — Semibold', hint: 'Strong headings' },\n { value: '700', label: '700 — Bold', hint: 'Maximum impact' },\n ],\n }),\n\n borderRadius: () =>\n p.select({\n message: `Border radius (current: ${reference.radius}):`,\n options: [\n { value: '', label: `Keep \"${reference.radius}\"`, hint: 'No change' },\n { value: '2px', label: 'Sharp (2px)', hint: 'Technical, precise' },\n { value: '4px', label: 'Tight (4px)', hint: 'Conservative' },\n { value: '6px', label: 'Moderate (6px)', hint: 'Balanced' },\n { value: '8px', label: 'Comfortable (8px)', hint: 'Friendly' },\n { value: '12px', label: 'Rounded (12px)', hint: 'Soft, approachable' },\n { value: '9999px', label: 'Pill', hint: 'Fully rounded' },\n ],\n }),\n\n darkMode: () =>\n p.confirm({\n message: 'Generate dark mode variant?',\n initialValue: false,\n }),\n\n additionalNotes: () =>\n p.text({\n message: 'Any additional customization notes? (optional)',\n placeholder: 'e.g. \"Use more warm tones\" or press enter to skip',\n }),\n },\n {\n onCancel: () => { p.cancel('Cancelled.'); process.exit(0); },\n },\n );\n\n return {\n reference,\n overrides: {\n primaryColor: overrides.primaryColor || undefined,\n fontFamily: overrides.fontFamily || undefined,\n headingWeight: overrides.headingWeight || undefined,\n borderRadius: overrides.borderRadius || undefined,\n darkMode: overrides.darkMode,\n additionalNotes: overrides.additionalNotes || undefined,\n },\n outputMode: 'customized',\n };\n}\n","import type { PreviewData } from './customizer.js';\nimport {\n generateColorScale,\n contrastForeground,\n hslString,\n hexToHsl,\n hslToHex,\n lighten,\n darken,\n generateChartColors,\n} from '../utils/color.js';\n\nexport function generatePreviewHtml(data: PreviewData): string {\n const {\n name, basedOn, primary, background, foreground, font, headingWeight,\n radius, colors, darkMode, shadcnCss,\n } = data;\n\n const isLightBg = isLight(background);\n const scale = generateColorScale(primary);\n const radiusPx = radius === '9999px' ? '24px' : radius;\n const borderColor = colors.border;\n const accent = colors.accent;\n const muted = colors.muted;\n const chart = colors.chart;\n\n // Generate dark mode vars for CSS\n const darkBg = hslToHex(hexToHsl(primary)[0], 15, 7);\n const darkFg = '#fafafa';\n const darkBorder = hslToHex(hexToHsl(primary)[0], 10, 18);\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>${esc(name)} — Design System Preview</title>\n<style>\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n :root {\n --bg: ${background};\n --fg: ${foreground};\n --primary: ${primary};\n --primary-fg: ${contrastForeground(primary)};\n --accent: ${accent};\n --muted: ${muted};\n --muted-fg: ${lighten(foreground, 40)};\n --border: ${borderColor};\n --destructive: #ef4444;\n --card: ${isLightBg ? '#ffffff' : lighten(background, 3)};\n --radius: ${radiusPx};\n }\n\n .dark {\n --bg: ${darkBg};\n --fg: ${darkFg};\n --card: ${lighten(darkBg, 3)};\n --muted: ${hslToHex(hexToHsl(primary)[0], 10, 15)};\n --muted-fg: ${darken(darkFg, 35)};\n --border: ${darkBorder};\n }\n\n body {\n font-family: \"${font}\", \"Inter\", system-ui, sans-serif;\n background: var(--bg);\n color: var(--fg);\n line-height: 1.5;\n transition: background 0.25s, color 0.25s;\n }\n\n .shell { max-width: 1100px; margin: 0 auto; padding: 40px 24px 80px; }\n\n /* ── Header ─────── */\n .header {\n display: flex; justify-content: space-between; align-items: center;\n padding-bottom: 24px; margin-bottom: 40px;\n border-bottom: 1px solid var(--border);\n }\n .header h1 {\n font-size: 2rem; font-weight: ${headingWeight};\n letter-spacing: -0.02em;\n }\n .header .sub {\n font-size: 0.875rem; color: var(--muted-fg); margin-top: 4px;\n }\n .controls { display: flex; gap: 8px; }\n .ctrl-btn {\n padding: 8px 16px; border-radius: var(--radius);\n font-size: 0.8125rem; font-family: inherit; cursor: pointer;\n transition: all 0.15s; border: 1px solid var(--border);\n background: var(--card); color: var(--fg);\n }\n .ctrl-btn:hover { opacity: 0.85; }\n .ctrl-btn.primary { background: var(--primary); color: var(--primary-fg); border-color: transparent; }\n\n /* ── Sections ───── */\n .section { margin-bottom: 48px; }\n .section-title {\n font-size: 1.375rem; font-weight: ${headingWeight};\n letter-spacing: -0.01em; margin-bottom: 16px;\n }\n .section-sub { font-size: 0.8125rem; color: var(--muted-fg); margin-bottom: 16px; }\n\n /* ── Color Scale ── */\n .scale-row {\n display: flex; border-radius: var(--radius); overflow: hidden;\n border: 1px solid var(--border); margin-bottom: 24px;\n }\n .scale-stop {\n flex: 1; padding: 28px 4px 8px; text-align: center;\n font-size: 10px; font-family: monospace; cursor: pointer;\n transition: transform 0.1s; position: relative;\n }\n .scale-stop:hover { z-index: 1; transform: scaleY(1.12); }\n .scale-stop .lbl { display: block; opacity: 0.7; margin-bottom: 2px; }\n .scale-stop .hex {\n background: rgba(0,0,0,0.2); color: #fff;\n padding: 1px 4px; border-radius: 3px; font-size: 9px;\n }\n\n /* ── Color Chips ── */\n .chip-grid {\n display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 12px;\n }\n .chip {\n border-radius: var(--radius); overflow: hidden;\n border: 1px solid var(--border); cursor: pointer;\n transition: transform 0.15s, box-shadow 0.15s;\n }\n .chip:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.1); }\n .chip .sw { height: 64px; display: flex; align-items: end; padding: 6px; }\n .chip .sw span {\n font-size: 10px; font-family: monospace;\n background: rgba(0,0,0,0.25); color: #fff;\n padding: 1px 5px; border-radius: 3px;\n }\n .chip .inf { padding: 8px; background: var(--card); }\n .chip .inf .n { font-size: 0.75rem; font-weight: 500; }\n .chip .inf .v { font-size: 10px; color: var(--muted-fg); font-family: monospace; }\n\n /* ── Typography ── */\n .type-rows { display: flex; flex-direction: column; gap: 8px; }\n .type-row {\n display: flex; align-items: baseline; gap: 16px;\n padding: 12px 16px; border-radius: var(--radius);\n background: var(--card); border: 1px solid var(--border);\n }\n .type-row .lbl {\n min-width: 48px; font-size: 0.75rem; color: var(--muted-fg);\n font-family: monospace; flex-shrink: 0;\n }\n .type-row .sample { flex: 1; }\n\n /* ── Components ── */\n .comp-card {\n padding: 24px; border-radius: var(--radius);\n background: var(--card); border: 1px solid var(--border);\n margin-bottom: 16px;\n }\n .comp-card h3 { font-size: 1rem; font-weight: 600; margin-bottom: 16px; }\n .comp-row { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-bottom: 12px; }\n\n .btn {\n display: inline-flex; align-items: center; justify-content: center;\n font-family: inherit; font-weight: 500; cursor: pointer;\n transition: all 0.15s; border: none; font-size: 0.8125rem;\n padding: 8px 16px; height: 36px; border-radius: var(--radius);\n }\n .btn-primary { background: var(--primary); color: var(--primary-fg); }\n .btn-secondary { background: var(--muted); color: var(--fg); }\n .btn-outline { background: transparent; color: var(--fg); border: 1px solid var(--border); }\n .btn-ghost { background: transparent; color: var(--fg); }\n .btn-destructive { background: var(--destructive); color: #fff; }\n .btn:hover { opacity: 0.9; }\n\n .badge {\n display: inline-flex; font-size: 0.6875rem; font-weight: 500;\n padding: 2px 10px; border-radius: 9999px;\n }\n .badge-primary { background: var(--primary); color: var(--primary-fg); }\n .badge-secondary { background: var(--muted); color: var(--fg); }\n .badge-outline { background: transparent; color: var(--fg); border: 1px solid var(--border); }\n\n .input-demo {\n width: 100%; max-width: 320px; font-family: inherit;\n background: var(--bg); color: var(--fg);\n border: 1px solid var(--border); border-radius: var(--radius);\n padding: 8px 12px; font-size: 0.8125rem; outline: none;\n transition: border-color 0.15s;\n }\n .input-demo:focus {\n border-color: var(--primary);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--primary) 20%, transparent);\n }\n\n .demo-table {\n width: 100%; border-collapse: collapse; font-size: 0.8125rem;\n }\n .demo-table th, .demo-table td {\n padding: 10px 12px; text-align: left;\n border-bottom: 1px solid var(--border);\n }\n .demo-table th {\n font-weight: 500; color: var(--muted-fg);\n font-size: 0.75rem; text-transform: uppercase;\n letter-spacing: 0.05em;\n }\n .demo-table tr:hover td { background: var(--muted); }\n\n .demo-card {\n padding: 20px; border-radius: var(--radius);\n background: var(--card); border: 1px solid var(--border);\n max-width: 340px; box-shadow: 0 1px 3px rgba(0,0,0,0.06);\n }\n .demo-card h4 { font-weight: 600; margin-bottom: 6px; }\n .demo-card p { font-size: 0.8125rem; color: var(--muted-fg); margin-bottom: 14px; }\n\n .demo-dialog {\n padding: 24px; border-radius: var(--radius);\n background: var(--card); box-shadow: 0 20px 40px rgba(0,0,0,0.15);\n max-width: 400px; border: 1px solid var(--border);\n }\n .demo-dialog h4 { font-weight: 600; margin-bottom: 8px; }\n .demo-dialog p { font-size: 0.8125rem; color: var(--muted-fg); margin-bottom: 16px; }\n .demo-dialog .acts { display: flex; justify-content: flex-end; gap: 8px; }\n\n .tabs {\n display: flex; background: var(--muted); border-radius: var(--radius); padding: 4px; gap: 2px;\n }\n .tab {\n padding: 6px 14px; border-radius: calc(var(--radius) - 2px);\n font-size: 0.8125rem; font-family: inherit; font-weight: 500;\n border: none; cursor: pointer; background: transparent;\n color: var(--muted-fg); transition: all 0.15s;\n }\n .tab.active {\n background: var(--bg); color: var(--fg);\n box-shadow: 0 1px 2px rgba(0,0,0,0.05);\n }\n .tab:hover:not(.active) { color: var(--fg); }\n\n /* ── CSS Block ──── */\n .css-block {\n position: relative; background: var(--card);\n border: 1px solid var(--border); border-radius: var(--radius);\n overflow: hidden;\n }\n .css-block pre {\n padding: 20px; overflow-x: auto; font-family: monospace;\n font-size: 11px; line-height: 1.6; color: var(--fg);\n }\n .css-block .copy-css { position: absolute; top: 8px; right: 8px; }\n\n /* ── Spacing ────── */\n .spacing-vis {\n display: flex; align-items: end; gap: 8px; padding: 24px;\n background: var(--card); border: 1px solid var(--border);\n border-radius: var(--radius);\n }\n .sp-bar { display: flex; flex-direction: column; align-items: center; gap: 4px; }\n .sp-bar .bar { background: var(--primary); border-radius: 2px; width: 28px; }\n .sp-bar .val { font-size: 10px; font-family: monospace; color: var(--muted-fg); }\n\n /* ── Radius ─────── */\n .radius-grid { display: flex; gap: 16px; flex-wrap: wrap; }\n .radius-demo {\n width: 72px; height: 72px; background: var(--primary);\n display: flex; align-items: center; justify-content: center;\n color: var(--primary-fg); font-size: 0.6875rem; font-family: monospace;\n }\n\n /* ── Toast ──────── */\n .toast-popup {\n position: fixed; bottom: 24px; right: 24px;\n padding: 10px 20px; background: var(--fg); color: var(--bg);\n border-radius: var(--radius); font-size: 0.8125rem;\n box-shadow: 0 8px 20px rgba(0,0,0,0.15);\n transform: translateY(100px); opacity: 0;\n transition: all 0.3s; z-index: 100;\n }\n .toast-popup.show { transform: translateY(0); opacity: 1; }\n\n @media (max-width: 768px) {\n .shell { padding: 16px 12px 48px; }\n .header { flex-direction: column; gap: 12px; align-items: flex-start; }\n .chip-grid { grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); }\n }\n</style>\n</head>\n<body>\n<div class=\"shell\">\n\n <div class=\"header\">\n <div>\n <h1>${esc(name)}</h1>\n <div class=\"sub\">Based on ${esc(basedOn)} · 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, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"');\n}\n\nfunction escHtml(s: string): string {\n return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');\n}\n"],"mappings":"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","names":["p","writeFileSync","resolve","loadReference","applyOverrides","existsSync"]}
|
|
1
|
+
{"version":3,"sources":["../../bin/oh-my-design.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { readFileSync, existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nfunction readPackageVersion(): string {\n let cur = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 8; i++) {\n const pkg = join(cur, 'package.json');\n if (existsSync(pkg)) {\n try {\n return JSON.parse(readFileSync(pkg, 'utf8')).version ?? '0.0.0';\n } catch {\n return '0.0.0';\n }\n }\n const parent = dirname(cur);\n if (parent === cur) break;\n cur = parent;\n }\n return '0.0.0';\n}\n\nconst program = new Command();\n\nprogram\n .name('oh-my-design')\n .description('Bootstrap oh-my-design skills + agents into your project. After install, talk to your agent in natural language — no other CLI commands.')\n .version(readPackageVersion())\n .showSuggestionAfterError(true)\n .showHelpAfterError(true);\n\nprogram\n .command('install-skills')\n .description('Install omd skill files + canonical agents into agent directories (.claude/, .codex/, .opencode/)')\n .option('--dir <path>', 'Project root (defaults to cwd)')\n .option('--agent <name...>', 'Restrict to specific agents (claude-code | codex | opencode)')\n .option('--force', 'Overwrite existing files even without the omd marker')\n .action(\n async (opts: { dir?: string; agent?: string[]; force?: boolean }) => {\n const { runInstallSkills } = await import('../src/cli/install-skills.js');\n const validAgents = ['claude-code', 'codex', 'opencode'] as const;\n type Agent = (typeof validAgents)[number];\n const agents = opts.agent\n ? (opts.agent.filter((a): a is Agent =>\n (validAgents as readonly string[]).includes(a)\n ) as Agent[])\n : undefined;\n const code = await runInstallSkills({\n dir: opts.dir,\n agents,\n force: opts.force,\n });\n if (code !== 0) process.exit(code);\n }\n );\n\nprogram.parse();\n"],"mappings":";;;AAAA,SAAS,eAAe;AACxB,SAAS,cAAc,kBAAkB;AACzC,SAAS,SAAS,YAAY;AAC9B,SAAS,qBAAqB;AAE9B,SAAS,qBAA6B;AACpC,MAAI,MAAM,QAAQ,cAAc,YAAY,GAAG,CAAC;AAChD,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,MAAM,KAAK,KAAK,cAAc;AACpC,QAAI,WAAW,GAAG,GAAG;AACnB,UAAI;AACF,eAAO,KAAK,MAAM,aAAa,KAAK,MAAM,CAAC,EAAE,WAAW;AAAA,MAC1D,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AACA,UAAM,SAAS,QAAQ,GAAG;AAC1B,QAAI,WAAW,IAAK;AACpB,UAAM;AAAA,EACR;AACA,SAAO;AACT;AAEA,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,cAAc,EACnB,YAAY,+IAA0I,EACtJ,QAAQ,mBAAmB,CAAC,EAC5B,yBAAyB,IAAI,EAC7B,mBAAmB,IAAI;AAE1B,QACG,QAAQ,gBAAgB,EACxB,YAAY,mGAAmG,EAC/G,OAAO,gBAAgB,gCAAgC,EACvD,OAAO,qBAAqB,8DAA8D,EAC1F,OAAO,WAAW,sDAAsD,EACxE;AAAA,EACC,OAAO,SAA8D;AACnE,UAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,+BAA8B;AACxE,UAAM,cAAc,CAAC,eAAe,SAAS,UAAU;AAEvD,UAAM,SAAS,KAAK,QACf,KAAK,MAAM;AAAA,MAAO,CAAC,MACjB,YAAkC,SAAS,CAAC;AAAA,IAC/C,IACA;AACJ,UAAM,OAAO,MAAM,iBAAiB;AAAA,MAClC,KAAK,KAAK;AAAA,MACV;AAAA,MACA,OAAO,KAAK;AAAA,IACd,CAAC;AACD,QAAI,SAAS,EAAG,SAAQ,KAAK,IAAI;AAAA,EACnC;AACF;AAEF,QAAQ,MAAM;","names":[]}
|
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli/install-skills.ts
|
|
4
|
+
import * as p from "@clack/prompts";
|
|
5
|
+
import pc from "picocolors";
|
|
6
|
+
import {
|
|
7
|
+
readFileSync,
|
|
8
|
+
readdirSync,
|
|
9
|
+
writeFileSync,
|
|
10
|
+
existsSync as existsSync2,
|
|
11
|
+
mkdirSync
|
|
12
|
+
} from "fs";
|
|
13
|
+
import { join as join2, dirname, relative } from "path";
|
|
14
|
+
import { fileURLToPath } from "url";
|
|
15
|
+
|
|
16
|
+
// src/core/agent-detect.ts
|
|
17
|
+
import { existsSync } from "fs";
|
|
18
|
+
import { join } from "path";
|
|
19
|
+
function detectInstalledAgents(projectRoot) {
|
|
20
|
+
return {
|
|
21
|
+
claudeCode: existsSync(join(projectRoot, ".claude")) || existsSync(join(projectRoot, "CLAUDE.md")),
|
|
22
|
+
codex: existsSync(join(projectRoot, ".codex")) || existsSync(join(projectRoot, "AGENTS.md")) || existsSync(join(projectRoot, "AGENTS.override.md")),
|
|
23
|
+
opencode: existsSync(join(projectRoot, ".opencode")) || existsSync(join(projectRoot, "opencode.json")) || existsSync(join(projectRoot, "opencode.jsonc")),
|
|
24
|
+
cursor: existsSync(join(projectRoot, ".cursor")) || existsSync(join(projectRoot, ".cursorrules"))
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// src/cli/install-skills.ts
|
|
29
|
+
function findPackageRoot() {
|
|
30
|
+
let cur = dirname(fileURLToPath(import.meta.url));
|
|
31
|
+
for (let i = 0; i < 8; i++) {
|
|
32
|
+
if (existsSync2(join2(cur, "skills"))) return cur;
|
|
33
|
+
const parent = dirname(cur);
|
|
34
|
+
if (parent === cur) break;
|
|
35
|
+
cur = parent;
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
function listShippedSkills(packageRoot) {
|
|
40
|
+
const skillsDir = join2(packageRoot, "skills");
|
|
41
|
+
if (!existsSync2(skillsDir)) return [];
|
|
42
|
+
return readdirSync(skillsDir).filter((name) => existsSync2(join2(skillsDir, name, "SKILL.md"))).sort();
|
|
43
|
+
}
|
|
44
|
+
function listCanonicalAgents(packageRoot) {
|
|
45
|
+
const dir = join2(packageRoot, "agents");
|
|
46
|
+
if (!existsSync2(dir)) return [];
|
|
47
|
+
return readdirSync(dir).filter((name) => name.startsWith("omd-") && name.endsWith(".md")).sort();
|
|
48
|
+
}
|
|
49
|
+
function parseCanonicalAgent(packageRoot, filename) {
|
|
50
|
+
const src = readFileSync(join2(packageRoot, "agents", filename), "utf8");
|
|
51
|
+
const match = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/.exec(src);
|
|
52
|
+
if (!match) {
|
|
53
|
+
throw new Error(`agents/${filename}: missing YAML frontmatter`);
|
|
54
|
+
}
|
|
55
|
+
const fm = match[1];
|
|
56
|
+
const body = match[2];
|
|
57
|
+
const grab = (key) => {
|
|
58
|
+
const re = new RegExp(`^${key}:\\s*(.+)$`, "m");
|
|
59
|
+
const m = re.exec(fm);
|
|
60
|
+
return m ? m[1].trim().replace(/^["']|["']$/g, "") : "";
|
|
61
|
+
};
|
|
62
|
+
return {
|
|
63
|
+
name: grab("name") || filename.replace(/\.md$/, ""),
|
|
64
|
+
description: grab("description"),
|
|
65
|
+
tools: grab("tools").split(",").map((t) => t.trim()).filter(Boolean),
|
|
66
|
+
model: grab("model") || "sonnet",
|
|
67
|
+
body
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function claudeToolsToCodex(tools) {
|
|
71
|
+
const m = {
|
|
72
|
+
Read: "read_file",
|
|
73
|
+
Write: "write_file",
|
|
74
|
+
Edit: "edit_file",
|
|
75
|
+
Bash: "shell",
|
|
76
|
+
Glob: "search",
|
|
77
|
+
Grep: "search",
|
|
78
|
+
WebFetch: "web_fetch",
|
|
79
|
+
WebSearch: "search",
|
|
80
|
+
Agent: "spawn_agent",
|
|
81
|
+
TaskCreate: "task",
|
|
82
|
+
TaskUpdate: "task",
|
|
83
|
+
TaskList: "task"
|
|
84
|
+
};
|
|
85
|
+
const out = /* @__PURE__ */ new Set();
|
|
86
|
+
for (const t of tools) out.add(m[t] ?? t.toLowerCase());
|
|
87
|
+
return [...out].sort();
|
|
88
|
+
}
|
|
89
|
+
function claudeModelToCodex(model) {
|
|
90
|
+
const m = {
|
|
91
|
+
haiku: "gpt-4.1-mini",
|
|
92
|
+
sonnet: "gpt-4.1",
|
|
93
|
+
opus: "gpt-4.1"
|
|
94
|
+
};
|
|
95
|
+
return m[model.toLowerCase()] ?? "gpt-4.1";
|
|
96
|
+
}
|
|
97
|
+
function renderClaudeAgent(a) {
|
|
98
|
+
const fm = [
|
|
99
|
+
"---",
|
|
100
|
+
`name: ${a.name}`,
|
|
101
|
+
`description: ${a.description}`,
|
|
102
|
+
`tools: ${a.tools.join(", ")}`,
|
|
103
|
+
`model: ${a.model}`,
|
|
104
|
+
`omd_managed: true`,
|
|
105
|
+
"---",
|
|
106
|
+
""
|
|
107
|
+
].join("\n");
|
|
108
|
+
return fm + a.body;
|
|
109
|
+
}
|
|
110
|
+
function renderCodexAgent(a) {
|
|
111
|
+
const tools = claudeToolsToCodex(a.tools);
|
|
112
|
+
const model = claudeModelToCodex(a.model);
|
|
113
|
+
const desc = a.description.replace(/"/g, '\\"');
|
|
114
|
+
return [
|
|
115
|
+
`[agent]`,
|
|
116
|
+
`name = "${a.name}"`,
|
|
117
|
+
`description = "${desc}"`,
|
|
118
|
+
`model = "${model}"`,
|
|
119
|
+
`max_threads = 1`,
|
|
120
|
+
`allowed_tools = [${tools.map((t) => `"${t}"`).join(", ")}]`,
|
|
121
|
+
"",
|
|
122
|
+
`instructions = """`,
|
|
123
|
+
`Source of truth: agents/${a.name}.md (canonical). The full role spec is`,
|
|
124
|
+
`mirrored to .claude/agents/${a.name}.md when installed for Claude Code.`,
|
|
125
|
+
`Follow that spec verbatim regardless of channel.`,
|
|
126
|
+
"",
|
|
127
|
+
`Codex notes:`,
|
|
128
|
+
`- Spawn sub-agents via spawn_agent with names matching .codex/agents/<name>.toml`,
|
|
129
|
+
`- Use shell to invoke CLI helpers (omd init prepare, omd remember, git apply, npx axe-core, npx lighthouse)`,
|
|
130
|
+
`- All artifacts go inside .omd/runs/run-<latest>/ (or skills/omd-lab-02-design-harness/runs/<lab-version>-...)`,
|
|
131
|
+
`"""`,
|
|
132
|
+
""
|
|
133
|
+
].join("\n");
|
|
134
|
+
}
|
|
135
|
+
function planForTarget(projectRoot, target) {
|
|
136
|
+
switch (target) {
|
|
137
|
+
case "claude-code":
|
|
138
|
+
return {
|
|
139
|
+
target,
|
|
140
|
+
destDir: join2(projectRoot, ".claude", "skills"),
|
|
141
|
+
layout: "folder"
|
|
142
|
+
};
|
|
143
|
+
case "codex":
|
|
144
|
+
return {
|
|
145
|
+
target,
|
|
146
|
+
destDir: join2(projectRoot, ".codex", "skills"),
|
|
147
|
+
layout: "folder"
|
|
148
|
+
};
|
|
149
|
+
case "opencode":
|
|
150
|
+
return {
|
|
151
|
+
target,
|
|
152
|
+
destDir: join2(projectRoot, ".opencode", "agents"),
|
|
153
|
+
layout: "flat"
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
var MANAGED_HEADER = "<!-- omd:installed-skill \u2014 managed by `omd install-skills`. Do not edit; rerun the command to refresh. -->";
|
|
158
|
+
function installOne(packageRoot, plan, skill, force) {
|
|
159
|
+
const src = readFileSync(
|
|
160
|
+
join2(packageRoot, "skills", skill, "SKILL.md"),
|
|
161
|
+
"utf8"
|
|
162
|
+
);
|
|
163
|
+
const managed = MANAGED_HEADER + "\n\n" + src;
|
|
164
|
+
const destPath = plan.layout === "folder" ? join2(plan.destDir, skill, "SKILL.md") : join2(plan.destDir, skill + ".md");
|
|
165
|
+
const exists = existsSync2(destPath);
|
|
166
|
+
const existing = exists ? readFileSync(destPath, "utf8") : "";
|
|
167
|
+
if (exists && existing === managed) {
|
|
168
|
+
return { target: plan.target, skill, destPath, status: "unchanged" };
|
|
169
|
+
}
|
|
170
|
+
if (exists && !existing.startsWith(MANAGED_HEADER) && !force) {
|
|
171
|
+
return { target: plan.target, skill, destPath, status: "skipped-drift" };
|
|
172
|
+
}
|
|
173
|
+
mkdirSync(dirname(destPath), { recursive: true });
|
|
174
|
+
writeFileSync(destPath, managed, "utf8");
|
|
175
|
+
return {
|
|
176
|
+
target: plan.target,
|
|
177
|
+
skill,
|
|
178
|
+
destPath,
|
|
179
|
+
status: exists ? "updated" : "created"
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function installHookFile(packageRoot, projectRoot, filename, force) {
|
|
183
|
+
const target = "claude-code";
|
|
184
|
+
const skillLabel = `hook:${filename}`;
|
|
185
|
+
const srcPath = join2(packageRoot, ".claude", "hooks", filename);
|
|
186
|
+
const destPath = join2(projectRoot, ".claude", "hooks", filename);
|
|
187
|
+
if (!existsSync2(srcPath)) {
|
|
188
|
+
return { target, skill: skillLabel, destPath, status: "skipped-drift" };
|
|
189
|
+
}
|
|
190
|
+
const src = readFileSync(srcPath, "utf8");
|
|
191
|
+
const exists = existsSync2(destPath);
|
|
192
|
+
const existing = exists ? readFileSync(destPath, "utf8") : "";
|
|
193
|
+
if (exists && existing === src) {
|
|
194
|
+
return { target, skill: skillLabel, destPath, status: "unchanged" };
|
|
195
|
+
}
|
|
196
|
+
if (exists && !force) {
|
|
197
|
+
return { target, skill: skillLabel, destPath, status: "skipped-drift" };
|
|
198
|
+
}
|
|
199
|
+
mkdirSync(dirname(destPath), { recursive: true });
|
|
200
|
+
writeFileSync(destPath, src);
|
|
201
|
+
return { target, skill: skillLabel, destPath, status: exists ? "updated" : "created" };
|
|
202
|
+
}
|
|
203
|
+
function installSettingsJson(packageRoot, projectRoot, force) {
|
|
204
|
+
const target = "claude-code";
|
|
205
|
+
const skillLabel = "settings:.claude/settings.json";
|
|
206
|
+
const srcPath = join2(packageRoot, ".claude", "settings.json");
|
|
207
|
+
const destPath = join2(projectRoot, ".claude", "settings.json");
|
|
208
|
+
if (!existsSync2(srcPath)) {
|
|
209
|
+
return { target, skill: skillLabel, destPath, status: "skipped-drift" };
|
|
210
|
+
}
|
|
211
|
+
const src = readFileSync(srcPath, "utf8");
|
|
212
|
+
const exists = existsSync2(destPath);
|
|
213
|
+
const existing = exists ? readFileSync(destPath, "utf8") : "";
|
|
214
|
+
if (exists && existing === src) {
|
|
215
|
+
return { target, skill: skillLabel, destPath, status: "unchanged" };
|
|
216
|
+
}
|
|
217
|
+
if (exists && !force) {
|
|
218
|
+
try {
|
|
219
|
+
const parsed = JSON.parse(existing);
|
|
220
|
+
if (typeof parsed._doc === "string" && parsed._doc.includes("OmD")) {
|
|
221
|
+
} else {
|
|
222
|
+
return { target, skill: skillLabel, destPath, status: "skipped-drift" };
|
|
223
|
+
}
|
|
224
|
+
} catch {
|
|
225
|
+
return { target, skill: skillLabel, destPath, status: "skipped-drift" };
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
mkdirSync(dirname(destPath), { recursive: true });
|
|
229
|
+
writeFileSync(destPath, src);
|
|
230
|
+
return { target, skill: skillLabel, destPath, status: exists ? "updated" : "created" };
|
|
231
|
+
}
|
|
232
|
+
function installDataFile(packageRoot, projectRoot, channelDir, dataFilename, force) {
|
|
233
|
+
const target = channelDir === ".claude" ? "claude-code" : "codex";
|
|
234
|
+
const skillLabel = `data:${dataFilename}`;
|
|
235
|
+
const srcPath = join2(packageRoot, "data", dataFilename);
|
|
236
|
+
const destPath = join2(projectRoot, channelDir, "data", dataFilename);
|
|
237
|
+
if (!existsSync2(srcPath)) {
|
|
238
|
+
return { target, skill: skillLabel, destPath, status: "skipped-drift" };
|
|
239
|
+
}
|
|
240
|
+
const src = readFileSync(srcPath, "utf8");
|
|
241
|
+
const exists = existsSync2(destPath);
|
|
242
|
+
const existing = exists ? readFileSync(destPath, "utf8") : "";
|
|
243
|
+
if (exists && existing === src) {
|
|
244
|
+
return { target, skill: skillLabel, destPath, status: "unchanged" };
|
|
245
|
+
}
|
|
246
|
+
if (exists && !force) {
|
|
247
|
+
return { target, skill: skillLabel, destPath, status: "skipped-drift" };
|
|
248
|
+
}
|
|
249
|
+
mkdirSync(dirname(destPath), { recursive: true });
|
|
250
|
+
writeFileSync(destPath, src, "utf8");
|
|
251
|
+
return {
|
|
252
|
+
target,
|
|
253
|
+
skill: skillLabel,
|
|
254
|
+
destPath,
|
|
255
|
+
status: exists ? "updated" : "created"
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
function installAgentFile(packageRoot, projectRoot, channel, filename, force) {
|
|
259
|
+
const target = channel === "claude" ? "claude-code" : "codex";
|
|
260
|
+
const skillLabel = `agent:${filename}`;
|
|
261
|
+
const parsed = parseCanonicalAgent(packageRoot, filename);
|
|
262
|
+
const rendered = channel === "claude" ? renderClaudeAgent(parsed) : renderCodexAgent(parsed);
|
|
263
|
+
const destFilename = channel === "claude" ? filename : filename.replace(/\.md$/, ".toml");
|
|
264
|
+
const destPath = join2(
|
|
265
|
+
projectRoot,
|
|
266
|
+
channel === "claude" ? ".claude" : ".codex",
|
|
267
|
+
"agents",
|
|
268
|
+
destFilename
|
|
269
|
+
);
|
|
270
|
+
const managed = channel === "claude" ? rendered : "# omd:installed-agent \u2014 generated from agents/" + filename + " by `omd install-skills`. Do not edit; rerun the command to refresh.\n\n" + rendered;
|
|
271
|
+
const exists = existsSync2(destPath);
|
|
272
|
+
const existing = exists ? readFileSync(destPath, "utf8") : "";
|
|
273
|
+
if (exists && existing === managed) {
|
|
274
|
+
return { target, skill: skillLabel, destPath, status: "unchanged" };
|
|
275
|
+
}
|
|
276
|
+
const isManaged = channel === "claude" ? /\nomd_managed:\s*true\b/.test(existing) : existing.startsWith("# omd:installed-agent");
|
|
277
|
+
if (exists && !isManaged && !force) {
|
|
278
|
+
return { target, skill: skillLabel, destPath, status: "skipped-drift" };
|
|
279
|
+
}
|
|
280
|
+
mkdirSync(dirname(destPath), { recursive: true });
|
|
281
|
+
writeFileSync(destPath, managed, "utf8");
|
|
282
|
+
return {
|
|
283
|
+
target,
|
|
284
|
+
skill: skillLabel,
|
|
285
|
+
destPath,
|
|
286
|
+
status: exists ? "updated" : "created"
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
var STATUS_LABEL = {
|
|
290
|
+
created: pc.green("created"),
|
|
291
|
+
updated: pc.cyan("updated"),
|
|
292
|
+
unchanged: pc.dim("unchanged"),
|
|
293
|
+
"skipped-drift": pc.yellow("skipped")
|
|
294
|
+
};
|
|
295
|
+
function autoDetectTargets(projectRoot) {
|
|
296
|
+
const presence = detectInstalledAgents(projectRoot);
|
|
297
|
+
const targets = [];
|
|
298
|
+
if (presence.claudeCode) targets.push("claude-code");
|
|
299
|
+
if (presence.codex) targets.push("codex");
|
|
300
|
+
if (presence.opencode) targets.push("opencode");
|
|
301
|
+
if (targets.length === 0) {
|
|
302
|
+
return ["claude-code", "codex", "opencode"];
|
|
303
|
+
}
|
|
304
|
+
return targets;
|
|
305
|
+
}
|
|
306
|
+
async function runInstallSkills(opts = {}) {
|
|
307
|
+
const projectRoot = opts.dir ?? process.cwd();
|
|
308
|
+
const packageRoot = findPackageRoot();
|
|
309
|
+
if (!packageRoot) {
|
|
310
|
+
console.error(pc.red("omd install-skills: package data not found"));
|
|
311
|
+
return 1;
|
|
312
|
+
}
|
|
313
|
+
const skills = listShippedSkills(packageRoot);
|
|
314
|
+
if (skills.length === 0) {
|
|
315
|
+
console.error(pc.red("omd install-skills: no skills found in package"));
|
|
316
|
+
return 1;
|
|
317
|
+
}
|
|
318
|
+
const targets = opts.agents ?? autoDetectTargets(projectRoot);
|
|
319
|
+
const plans = targets.map((t) => planForTarget(projectRoot, t));
|
|
320
|
+
const force = opts.force ?? false;
|
|
321
|
+
p.intro(
|
|
322
|
+
pc.bold("omd install-skills") + pc.dim(` (${relative(process.cwd(), projectRoot) || "."})`)
|
|
323
|
+
);
|
|
324
|
+
p.log.message(
|
|
325
|
+
pc.bold("Targets: ") + targets.map((t) => pc.cyan(t)).join(", ")
|
|
326
|
+
);
|
|
327
|
+
p.log.message(
|
|
328
|
+
pc.bold("Skills: ") + skills.map((s) => pc.cyan(s)).join(", ")
|
|
329
|
+
);
|
|
330
|
+
const results = [];
|
|
331
|
+
for (const plan of plans) {
|
|
332
|
+
for (const skill of skills) {
|
|
333
|
+
results.push(installOne(packageRoot, plan, skill, force));
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
const canonicalAgents = listCanonicalAgents(packageRoot);
|
|
337
|
+
for (const target of targets) {
|
|
338
|
+
if (target === "claude-code") {
|
|
339
|
+
for (const filename of canonicalAgents) {
|
|
340
|
+
results.push(installAgentFile(packageRoot, projectRoot, "claude", filename, force));
|
|
341
|
+
}
|
|
342
|
+
} else if (target === "codex") {
|
|
343
|
+
for (const filename of canonicalAgents) {
|
|
344
|
+
results.push(installAgentFile(packageRoot, projectRoot, "codex", filename, force));
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
const dataFiles = [
|
|
349
|
+
"reference-fingerprints.json",
|
|
350
|
+
"reference-tags.md",
|
|
351
|
+
"vocabulary.json",
|
|
352
|
+
"synonyms.json",
|
|
353
|
+
"opt-out-corpus.json"
|
|
354
|
+
];
|
|
355
|
+
for (const target of targets) {
|
|
356
|
+
if (target === "claude-code") {
|
|
357
|
+
for (const dataFile of dataFiles) {
|
|
358
|
+
results.push(installDataFile(packageRoot, projectRoot, ".claude", dataFile, force));
|
|
359
|
+
}
|
|
360
|
+
} else if (target === "codex") {
|
|
361
|
+
for (const dataFile of dataFiles) {
|
|
362
|
+
results.push(installDataFile(packageRoot, projectRoot, ".codex", dataFile, force));
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (targets.includes("claude-code")) {
|
|
367
|
+
for (const hookFile of [
|
|
368
|
+
"skill-activation.cjs",
|
|
369
|
+
"session-state-loader.cjs",
|
|
370
|
+
"post-edit-watch.cjs",
|
|
371
|
+
"session-end-foldin.cjs"
|
|
372
|
+
]) {
|
|
373
|
+
results.push(installHookFile(packageRoot, projectRoot, hookFile, force));
|
|
374
|
+
}
|
|
375
|
+
results.push(installSettingsJson(packageRoot, projectRoot, force));
|
|
376
|
+
}
|
|
377
|
+
p.log.message(pc.bold("\nResults:"));
|
|
378
|
+
for (const r of results) {
|
|
379
|
+
const rel = relative(projectRoot, r.destPath);
|
|
380
|
+
p.log.message(
|
|
381
|
+
` ${STATUS_LABEL[r.status]} ${pc.dim(r.target.padEnd(12))} ${rel}`
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
const driftCount = results.filter((r) => r.status === "skipped-drift").length;
|
|
385
|
+
const writtenCount = results.filter(
|
|
386
|
+
(r) => r.status === "created" || r.status === "updated"
|
|
387
|
+
).length;
|
|
388
|
+
if (driftCount > 0) {
|
|
389
|
+
p.outro(
|
|
390
|
+
pc.yellow(
|
|
391
|
+
`${writtenCount} written, ${driftCount} skipped (existing files lack the omd marker \u2014 rerun with --force to overwrite).`
|
|
392
|
+
)
|
|
393
|
+
);
|
|
394
|
+
return 0;
|
|
395
|
+
}
|
|
396
|
+
const nextSteps = [
|
|
397
|
+
`${pc.bold("Open Claude Code (or Codex). Just say what you want:")}`,
|
|
398
|
+
"",
|
|
399
|
+
` ${pc.dim('"\uD1A0\uC2A4 \uC2A4\uD0C0\uC77C \uAC00\uC871 \uC2DD\uB2E8 \uACF5\uC720 \uC571 \uBA54\uC778 \uD654\uBA74 \uB514\uC790\uC778\uD574\uC918"')}`,
|
|
400
|
+
` ${pc.dim('"Linear-clone B2B SaaS dashboard \uB9CC\uB4E4\uACE0 \uC2F6\uC5B4"')}`,
|
|
401
|
+
` ${pc.dim('"\uC774 \uCE74\uB4DC \uC880 \uB354 \uC138\uB828\uB418\uAC8C"')} ${pc.dim("# \uC791\uC5C5 \uC911 \uC790\uC5F0\uC5B4 \u2014 \uC790\uB3D9 hook \uB77C\uC6B0\uD305")}`,
|
|
402
|
+
"",
|
|
403
|
+
`${pc.bold("Hook\uC774 \uC790\uB3D9\uC73C\uB85C \uB77C\uC6B0\uD305")} ${pc.dim("\u2014 \uB514\uC790\uC778 \uC758\uB3C4 \uAC10\uC9C0\uD574\uC11C \uD558\uB124\uC2A4/\uC2A4\uD0AC \uD638\uCD9C. \uC2AC\uB798\uC2DC \uBA85\uB839 \uC548 \uCCD0\uB3C4 \uB428.")}`,
|
|
404
|
+
"",
|
|
405
|
+
`${pc.dim("Power user shortcut: ")}${pc.cyan("/omd-harness <task>")} ${pc.dim("\u2014 hook \uC6B0\uD68C, \uC989\uC2DC \uC9C4\uC785.")}`,
|
|
406
|
+
"",
|
|
407
|
+
`${pc.yellow("\u26A0 Already-running Claude Code session?")} ${pc.dim("Run `/agents` inside the session to reload \u2014 or quit (Cmd+Q on macOS) and relaunch. Without reload, hooks/agents do not load.")}`
|
|
408
|
+
].join("\n");
|
|
409
|
+
p.note(nextSteps, "Next");
|
|
410
|
+
p.outro(
|
|
411
|
+
pc.green(
|
|
412
|
+
`Done. 6 skills \xB7 11 sub-agents \xB7 4 hooks installed (${writtenCount} files).`
|
|
413
|
+
)
|
|
414
|
+
);
|
|
415
|
+
return 0;
|
|
416
|
+
}
|
|
417
|
+
export {
|
|
418
|
+
runInstallSkills
|
|
419
|
+
};
|
|
420
|
+
//# sourceMappingURL=install-skills-GQPTQF5S.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/install-skills.ts","../src/core/agent-detect.ts"],"sourcesContent":["import * as p from '@clack/prompts';\nimport pc from 'picocolors';\nimport {\n readFileSync,\n readdirSync,\n writeFileSync,\n existsSync,\n mkdirSync,\n} from 'node:fs';\nimport { join, dirname, relative } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { detectInstalledAgents } from '../core/agent-detect.js';\n\nexport type SkillTarget = 'claude-code' | 'codex' | 'opencode';\n\nexport interface InstallSkillsOptions {\n dir?: string;\n agents?: SkillTarget[];\n force?: boolean;\n}\n\ninterface InstallPlan {\n target: SkillTarget;\n destDir: string;\n layout: 'folder' | 'flat';\n}\n\nfunction findPackageRoot(): string | null {\n let cur = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 8; i++) {\n if (existsSync(join(cur, 'skills'))) return cur;\n const parent = dirname(cur);\n if (parent === cur) break;\n cur = parent;\n }\n return null;\n}\n\nfunction listShippedSkills(packageRoot: string): string[] {\n const skillsDir = join(packageRoot, 'skills');\n if (!existsSync(skillsDir)) return [];\n return readdirSync(skillsDir)\n .filter((name) => existsSync(join(skillsDir, name, 'SKILL.md')))\n .sort();\n}\n\n/**\n * Canonical agent definitions live at `agents/<name>.md` (markdown with YAML\n * frontmatter). Channel-specific files (.claude/agents/*.md, .codex/agents/*.toml)\n * are generated artifacts — never the source of truth.\n *\n * The package ships only `agents/` and the generator emits per-channel files\n * into the user's project on `omd install-skills`.\n */\nfunction listCanonicalAgents(packageRoot: string): string[] {\n const dir = join(packageRoot, 'agents');\n if (!existsSync(dir)) return [];\n return readdirSync(dir)\n .filter((name) => name.startsWith('omd-') && name.endsWith('.md'))\n .sort();\n}\n\ninterface ParsedAgent {\n name: string;\n description: string;\n tools: string[];\n model: string;\n body: string;\n}\n\n/** Parse `agents/<name>.md` YAML frontmatter + body into structured form. */\nfunction parseCanonicalAgent(packageRoot: string, filename: string): ParsedAgent {\n const src = readFileSync(join(packageRoot, 'agents', filename), 'utf8');\n const match = /^---\\n([\\s\\S]*?)\\n---\\n([\\s\\S]*)$/.exec(src);\n if (!match) {\n throw new Error(`agents/${filename}: missing YAML frontmatter`);\n }\n const fm = match[1];\n const body = match[2];\n const grab = (key: string): string => {\n const re = new RegExp(`^${key}:\\\\s*(.+)$`, 'm');\n const m = re.exec(fm);\n return m ? m[1].trim().replace(/^[\"']|[\"']$/g, '') : '';\n };\n return {\n name: grab('name') || filename.replace(/\\.md$/, ''),\n description: grab('description'),\n tools: grab('tools')\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean),\n model: grab('model') || 'sonnet',\n body,\n };\n}\n\n/** Map Claude tool names to Codex tool names (best-effort). */\nfunction claudeToolsToCodex(tools: string[]): string[] {\n const m: Record<string, string> = {\n Read: 'read_file',\n Write: 'write_file',\n Edit: 'edit_file',\n Bash: 'shell',\n Glob: 'search',\n Grep: 'search',\n WebFetch: 'web_fetch',\n WebSearch: 'search',\n Agent: 'spawn_agent',\n TaskCreate: 'task',\n TaskUpdate: 'task',\n TaskList: 'task',\n };\n const out = new Set<string>();\n for (const t of tools) out.add(m[t] ?? t.toLowerCase());\n return [...out].sort();\n}\n\n/** Map Claude model alias to Codex/OpenAI model id (best-effort). */\nfunction claudeModelToCodex(model: string): string {\n const m: Record<string, string> = {\n haiku: 'gpt-4.1-mini',\n sonnet: 'gpt-4.1',\n opus: 'gpt-4.1',\n };\n return m[model.toLowerCase()] ?? 'gpt-4.1';\n}\n\n/** Render a canonical agent as a Claude Code subagent file.\n * IMPORTANT: Claude Code's subagent parser requires YAML frontmatter (`---`)\n * as the FIRST line of the file. Any preceding content (HTML comments, blank\n * lines) breaks discovery. So we encode the managed-by-omd marker as a\n * custom frontmatter field (`omd_managed: true`) instead of an HTML comment.\n */\nfunction renderClaudeAgent(a: ParsedAgent): string {\n const fm = [\n '---',\n `name: ${a.name}`,\n `description: ${a.description}`,\n `tools: ${a.tools.join(', ')}`,\n `model: ${a.model}`,\n `omd_managed: true`,\n '---',\n '',\n ].join('\\n');\n return fm + a.body;\n}\n\n/** Render a canonical agent as a Codex TOML file (declarative pointer). */\nfunction renderCodexAgent(a: ParsedAgent): string {\n const tools = claudeToolsToCodex(a.tools);\n const model = claudeModelToCodex(a.model);\n const desc = a.description.replace(/\"/g, '\\\\\"');\n return [\n `[agent]`,\n `name = \"${a.name}\"`,\n `description = \"${desc}\"`,\n `model = \"${model}\"`,\n `max_threads = 1`,\n `allowed_tools = [${tools.map((t) => `\"${t}\"`).join(', ')}]`,\n '',\n `instructions = \"\"\"`,\n `Source of truth: agents/${a.name}.md (canonical). The full role spec is`,\n `mirrored to .claude/agents/${a.name}.md when installed for Claude Code.`,\n `Follow that spec verbatim regardless of channel.`,\n '',\n `Codex notes:`,\n `- Spawn sub-agents via spawn_agent with names matching .codex/agents/<name>.toml`,\n `- Use shell to invoke CLI helpers (omd init prepare, omd remember, git apply, npx axe-core, npx lighthouse)`,\n `- All artifacts go inside .omd/runs/run-<latest>/ (or skills/omd-lab-02-design-harness/runs/<lab-version>-...)`,\n `\"\"\"`,\n '',\n ].join('\\n');\n}\n\nfunction planForTarget(projectRoot: string, target: SkillTarget): InstallPlan {\n switch (target) {\n case 'claude-code':\n return {\n target,\n destDir: join(projectRoot, '.claude', 'skills'),\n layout: 'folder',\n };\n case 'codex':\n return {\n target,\n destDir: join(projectRoot, '.codex', 'skills'),\n layout: 'folder',\n };\n case 'opencode':\n return {\n target,\n destDir: join(projectRoot, '.opencode', 'agents'),\n layout: 'flat',\n };\n }\n}\n\nconst MANAGED_HEADER =\n '<!-- omd:installed-skill — managed by `omd install-skills`. Do not edit; rerun the command to refresh. -->';\n\ninterface InstallResult {\n target: SkillTarget;\n skill: string;\n destPath: string;\n status: 'created' | 'updated' | 'unchanged' | 'skipped-drift';\n}\n\nfunction installOne(\n packageRoot: string,\n plan: InstallPlan,\n skill: string,\n force: boolean\n): InstallResult {\n const src = readFileSync(\n join(packageRoot, 'skills', skill, 'SKILL.md'),\n 'utf8'\n );\n const managed = MANAGED_HEADER + '\\n\\n' + src;\n const destPath =\n plan.layout === 'folder'\n ? join(plan.destDir, skill, 'SKILL.md')\n : join(plan.destDir, skill + '.md');\n\n const exists = existsSync(destPath);\n const existing = exists ? readFileSync(destPath, 'utf8') : '';\n\n if (exists && existing === managed) {\n return { target: plan.target, skill, destPath, status: 'unchanged' };\n }\n\n if (exists && !existing.startsWith(MANAGED_HEADER) && !force) {\n return { target: plan.target, skill, destPath, status: 'skipped-drift' };\n }\n\n mkdirSync(dirname(destPath), { recursive: true });\n writeFileSync(destPath, managed, 'utf8');\n return {\n target: plan.target,\n skill,\n destPath,\n status: exists ? 'updated' : 'created',\n };\n}\n\n/** Install a hook script from package's .claude/hooks/ to project. */\nfunction installHookFile(\n packageRoot: string,\n projectRoot: string,\n filename: string,\n force: boolean\n): InstallResult {\n const target: SkillTarget = 'claude-code';\n const skillLabel = `hook:${filename}`;\n const srcPath = join(packageRoot, '.claude', 'hooks', filename);\n const destPath = join(projectRoot, '.claude', 'hooks', filename);\n\n if (!existsSync(srcPath)) {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n const src = readFileSync(srcPath, 'utf8');\n const exists = existsSync(destPath);\n const existing = exists ? readFileSync(destPath, 'utf8') : '';\n if (exists && existing === src) {\n return { target, skill: skillLabel, destPath, status: 'unchanged' };\n }\n if (exists && !force) {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n mkdirSync(dirname(destPath), { recursive: true });\n writeFileSync(destPath, src);\n return { target, skill: skillLabel, destPath, status: exists ? 'updated' : 'created' };\n}\n\n/**\n * Install / merge .claude/settings.json. We MERGE hooks (don't clobber user\n * customizations) by checking if the omd-managed `_doc` field is present.\n * Without --force, if a user-edited settings.json exists (no _doc field),\n * we skip with `skipped-drift`.\n */\nfunction installSettingsJson(\n packageRoot: string,\n projectRoot: string,\n force: boolean\n): InstallResult {\n const target: SkillTarget = 'claude-code';\n const skillLabel = 'settings:.claude/settings.json';\n const srcPath = join(packageRoot, '.claude', 'settings.json');\n const destPath = join(projectRoot, '.claude', 'settings.json');\n if (!existsSync(srcPath)) {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n const src = readFileSync(srcPath, 'utf8');\n const exists = existsSync(destPath);\n const existing = exists ? readFileSync(destPath, 'utf8') : '';\n if (exists && existing === src) {\n return { target, skill: skillLabel, destPath, status: 'unchanged' };\n }\n if (exists && !force) {\n // Check if it's the omd-managed version\n try {\n const parsed = JSON.parse(existing);\n if (typeof parsed._doc === 'string' && parsed._doc.includes('OmD')) {\n // managed — overwrite\n } else {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n } catch {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n }\n mkdirSync(dirname(destPath), { recursive: true });\n writeFileSync(destPath, src);\n return { target, skill: skillLabel, destPath, status: exists ? 'updated' : 'created' };\n}\n\n/**\n * Copy a read-only data asset (reference-fingerprints.json, vocabulary.json, …)\n * from the package's `data/` into the project's `.claude/data/` or `.codex/data/`.\n * The skill reads these at runtime — they replace the deprecated `omd init recommend` CLI.\n */\nfunction installDataFile(\n packageRoot: string,\n projectRoot: string,\n channelDir: string,\n dataFilename: string,\n force: boolean\n): InstallResult {\n const target: SkillTarget = channelDir === '.claude' ? 'claude-code' : 'codex';\n const skillLabel = `data:${dataFilename}`;\n\n const srcPath = join(packageRoot, 'data', dataFilename);\n const destPath = join(projectRoot, channelDir, 'data', dataFilename);\n\n if (!existsSync(srcPath)) {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n\n const src = readFileSync(srcPath, 'utf8');\n const exists = existsSync(destPath);\n const existing = exists ? readFileSync(destPath, 'utf8') : '';\n\n // Data files are pure copies — no managed header (would corrupt JSON).\n if (exists && existing === src) {\n return { target, skill: skillLabel, destPath, status: 'unchanged' };\n }\n if (exists && !force) {\n // Honor user customization unless --force\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n\n mkdirSync(dirname(destPath), { recursive: true });\n writeFileSync(destPath, src, 'utf8');\n return {\n target,\n skill: skillLabel,\n destPath,\n status: exists ? 'updated' : 'created',\n };\n}\n\n/**\n * Generate a per-channel agent file from the canonical `agents/<name>.md`.\n *\n * Channel = 'claude' → emits `.claude/agents/<name>.md` (markdown w/ frontmatter)\n * Channel = 'codex' → emits `.codex/agents/<name>.toml` (TOML pointer)\n */\nfunction installAgentFile(\n packageRoot: string,\n projectRoot: string,\n channel: 'claude' | 'codex',\n filename: string,\n force: boolean\n): InstallResult {\n const target: SkillTarget = channel === 'claude' ? 'claude-code' : 'codex';\n const skillLabel = `agent:${filename}`;\n\n const parsed = parseCanonicalAgent(packageRoot, filename);\n const rendered =\n channel === 'claude' ? renderClaudeAgent(parsed) : renderCodexAgent(parsed);\n\n const destFilename =\n channel === 'claude' ? filename : filename.replace(/\\.md$/, '.toml');\n const destPath = join(\n projectRoot,\n channel === 'claude' ? '.claude' : '.codex',\n 'agents',\n destFilename\n );\n\n // For Claude Code: managed marker is encoded as `omd_managed: true` INSIDE the\n // frontmatter (rendered above) — no HTML comment can precede `---` or the\n // subagent loader rejects the file.\n // For Codex: TOML allows leading comments, so `# omd:installed-agent ...` is fine.\n const managed =\n channel === 'claude'\n ? rendered\n : '# omd:installed-agent — generated from agents/' +\n filename +\n ' by `omd install-skills`. Do not edit; rerun the command to refresh.\\n\\n' +\n rendered;\n\n const exists = existsSync(destPath);\n const existing = exists ? readFileSync(destPath, 'utf8') : '';\n\n if (exists && existing === managed) {\n return { target, skill: skillLabel, destPath, status: 'unchanged' };\n }\n\n // Drift detection sentinels:\n // Claude — look for `omd_managed: true` line inside frontmatter\n // Codex — look for `# omd:installed-agent` comment\n const isManaged =\n channel === 'claude'\n ? /\\nomd_managed:\\s*true\\b/.test(existing)\n : existing.startsWith('# omd:installed-agent');\n\n if (exists && !isManaged && !force) {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n\n mkdirSync(dirname(destPath), { recursive: true });\n writeFileSync(destPath, managed, 'utf8');\n return {\n target,\n skill: skillLabel,\n destPath,\n status: exists ? 'updated' : 'created',\n };\n}\n\nconst STATUS_LABEL: Record<InstallResult['status'], string> = {\n created: pc.green('created'),\n updated: pc.cyan('updated'),\n unchanged: pc.dim('unchanged'),\n 'skipped-drift': pc.yellow('skipped'),\n};\n\nfunction autoDetectTargets(projectRoot: string): SkillTarget[] {\n const presence = detectInstalledAgents(projectRoot);\n const targets: SkillTarget[] = [];\n if (presence.claudeCode) targets.push('claude-code');\n if (presence.codex) targets.push('codex');\n if (presence.opencode) targets.push('opencode');\n // Cursor uses .mdc rules, not skills — installed via `omd sync`.\n if (targets.length === 0) {\n // Fallback: install for all three so user gets coverage even without\n // explicit signal. Idempotent so cost is low.\n return ['claude-code', 'codex', 'opencode'];\n }\n return targets;\n}\n\nexport async function runInstallSkills(\n opts: InstallSkillsOptions = {}\n): Promise<number> {\n const projectRoot = opts.dir ?? process.cwd();\n const packageRoot = findPackageRoot();\n if (!packageRoot) {\n console.error(pc.red('omd install-skills: package data not found'));\n return 1;\n }\n\n const skills = listShippedSkills(packageRoot);\n if (skills.length === 0) {\n console.error(pc.red('omd install-skills: no skills found in package'));\n return 1;\n }\n\n const targets = opts.agents ?? autoDetectTargets(projectRoot);\n const plans = targets.map((t) => planForTarget(projectRoot, t));\n const force = opts.force ?? false;\n\n p.intro(\n pc.bold('omd install-skills') +\n pc.dim(` (${relative(process.cwd(), projectRoot) || '.'})`)\n );\n\n p.log.message(\n pc.bold('Targets: ') +\n targets.map((t) => pc.cyan(t)).join(', ')\n );\n p.log.message(\n pc.bold('Skills: ') + skills.map((s) => pc.cyan(s)).join(', ')\n );\n\n const results: InstallResult[] = [];\n for (const plan of plans) {\n for (const skill of skills) {\n results.push(installOne(packageRoot, plan, skill, force));\n }\n }\n\n // Generate per-channel sub-agent definitions from the canonical `agents/`.\n // This is the v2 portable source-of-truth pattern (oh-my-agent style).\n const canonicalAgents = listCanonicalAgents(packageRoot);\n for (const target of targets) {\n if (target === 'claude-code') {\n for (const filename of canonicalAgents) {\n results.push(installAgentFile(packageRoot, projectRoot, 'claude', filename, force));\n }\n } else if (target === 'codex') {\n for (const filename of canonicalAgents) {\n results.push(installAgentFile(packageRoot, projectRoot, 'codex', filename, force));\n }\n }\n // OpenCode currently has no agent-definition channel — skills only.\n }\n\n // Ship the read-only data assets (reference fingerprints, controlled vocab,\n // human-readable tag matrix, opt-out corpus) into the project so skills + hooks\n // can run entirely on the host CLI's own model — no external API keys.\n const dataFiles = [\n 'reference-fingerprints.json',\n 'reference-tags.md',\n 'vocabulary.json',\n 'synonyms.json',\n 'opt-out-corpus.json',\n ];\n for (const target of targets) {\n if (target === 'claude-code') {\n for (const dataFile of dataFiles) {\n results.push(installDataFile(packageRoot, projectRoot, '.claude', dataFile, force));\n }\n } else if (target === 'codex') {\n for (const dataFile of dataFiles) {\n results.push(installDataFile(packageRoot, projectRoot, '.codex', dataFile, force));\n }\n }\n }\n\n // Install hooks (Claude Code only — Codex / OpenCode have separate hook systems)\n if (targets.includes('claude-code')) {\n for (const hookFile of [\n 'skill-activation.cjs',\n 'session-state-loader.cjs',\n 'post-edit-watch.cjs',\n 'session-end-foldin.cjs',\n ]) {\n results.push(installHookFile(packageRoot, projectRoot, hookFile, force));\n }\n // settings.json (with merge, never clobber user)\n results.push(installSettingsJson(packageRoot, projectRoot, force));\n }\n\n p.log.message(pc.bold('\\nResults:'));\n for (const r of results) {\n const rel = relative(projectRoot, r.destPath);\n p.log.message(\n ` ${STATUS_LABEL[r.status]} ${pc.dim(r.target.padEnd(12))} ${rel}`\n );\n }\n\n const driftCount = results.filter((r) => r.status === 'skipped-drift').length;\n const writtenCount = results.filter(\n (r) => r.status === 'created' || r.status === 'updated'\n ).length;\n\n if (driftCount > 0) {\n p.outro(\n pc.yellow(\n `${writtenCount} written, ${driftCount} skipped (existing files lack the omd marker — rerun with --force to overwrite).`\n )\n );\n return 0;\n }\n\n // Friendly next-step nudge after successful install\n const nextSteps = [\n `${pc.bold('Open Claude Code (or Codex). Just say what you want:')}`,\n '',\n ` ${pc.dim('\"토스 스타일 가족 식단 공유 앱 메인 화면 디자인해줘\"')}`,\n ` ${pc.dim('\"Linear-clone B2B SaaS dashboard 만들고 싶어\"')}`,\n ` ${pc.dim('\"이 카드 좀 더 세련되게\"')} ${pc.dim('# 작업 중 자연어 — 자동 hook 라우팅')}`,\n '',\n `${pc.bold('Hook이 자동으로 라우팅')} ${pc.dim('— 디자인 의도 감지해서 하네스/스킬 호출. 슬래시 명령 안 쳐도 됨.')}`,\n '',\n `${pc.dim('Power user shortcut: ')}${pc.cyan('/omd-harness <task>')} ${pc.dim('— hook 우회, 즉시 진입.')}`,\n '',\n `${pc.yellow('⚠ Already-running Claude Code session?')} ${pc.dim('Run `/agents` inside the session to reload — or quit (Cmd+Q on macOS) and relaunch. Without reload, hooks/agents do not load.')}`,\n ].join('\\n');\n p.note(nextSteps, 'Next');\n\n p.outro(\n pc.green(\n `Done. 6 skills · 11 sub-agents · 4 hooks installed (${writtenCount} files).`,\n ),\n );\n return 0;\n}\n\n","import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport type AgentId = 'claude-code' | 'codex' | 'opencode' | 'cursor' | 'unknown';\n\nexport function detectCallingAgent(): AgentId {\n const env = process.env;\n\n if (env.CLAUDECODE === '1' || env.CLAUDE_CODE === '1' || env.CLAUDE_CODE_TASK_ID) {\n return 'claude-code';\n }\n if (env.CODEX_SESSION_ID || env.CODEX || env.OPENAI_CODEX) {\n return 'codex';\n }\n if (env.OPENCODE || env.OPENCODE_SESSION) {\n return 'opencode';\n }\n if (env.CURSOR_SESSION_ID || env.CURSOR_AGENT) {\n return 'cursor';\n }\n\n return 'unknown';\n}\n\nexport interface AgentPresence {\n claudeCode: boolean;\n codex: boolean;\n opencode: boolean;\n cursor: boolean;\n}\n\nexport function detectInstalledAgents(projectRoot: string): AgentPresence {\n return {\n claudeCode:\n existsSync(join(projectRoot, '.claude')) ||\n existsSync(join(projectRoot, 'CLAUDE.md')),\n codex:\n existsSync(join(projectRoot, '.codex')) ||\n existsSync(join(projectRoot, 'AGENTS.md')) ||\n existsSync(join(projectRoot, 'AGENTS.override.md')),\n opencode:\n existsSync(join(projectRoot, '.opencode')) ||\n existsSync(join(projectRoot, 'opencode.json')) ||\n existsSync(join(projectRoot, 'opencode.jsonc')),\n cursor:\n existsSync(join(projectRoot, '.cursor')) ||\n existsSync(join(projectRoot, '.cursorrules')),\n };\n}\n"],"mappings":"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","names":["existsSync","join","existsSync","join"]}
|