oh-my-design-cli 1.0.0 → 1.0.2

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.
@@ -1,8 +1,26 @@
1
1
  #!/usr/bin/env node
2
- // UserPromptSubmit hook — forced-eval skill activation (~84% reliability).
3
- // Reads .claude/skills/skill-rules.json + the user's prompt, finds matching
4
- // rules, appends a structured "SKILL ACTIVATION CHECK" block via the
5
- // `additionalContext` channel.
2
+ // UserPromptSubmit hook — DESIGN.md existence gate.
3
+ //
4
+ // Scope (2026-05-07, simplified from the prior keyword/regex forced-eval
5
+ // system): the hook now does the one thing description-based skill
6
+ // triggering cannot do — file-system state checks. If the project has no
7
+ // DESIGN.md and the user is asking for UI/design work, we surface a
8
+ // single warning recommending omd:init first. Everything else (which
9
+ // skill to invoke, when, in what language) is handled by Claude reading
10
+ // each SKILL.md description, the standard Anthropic mechanism.
11
+ //
12
+ // What was removed:
13
+ // - .claude/skills/skill-rules.json (per-skill keyword/regex pools)
14
+ // - the "OMD SKILL ACTIVATION CHECK" forced-injection block
15
+ // - per-skill required/suggested classification
16
+ // All those overlapped with SKILL.md descriptions and created a
17
+ // dual-source-of-truth maintenance burden.
18
+ //
19
+ // What remains:
20
+ // - DESIGN.md existence check
21
+ // - a small inline UI-cue keyword set (KO/EN/JA/ZH-TW) so we only fire
22
+ // the warning when the prompt is actually about UI — otherwise a
23
+ // casual "what's 2+2" wouldn't get spammed with init reminders
6
24
  //
7
25
  // Plain CommonJS so it works on any Node ≥18 without build deps.
8
26
 
@@ -12,62 +30,46 @@ const fs = require('node:fs');
12
30
  const path = require('node:path');
13
31
 
14
32
  const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
15
- const rulesPath = path.join(projectDir, '.claude', 'skills', 'skill-rules.json');
33
+
34
+ // Inline UI cue list (gate scope only). Tiny on purpose — this is a
35
+ // "is the user asking for UI work?" sniff test, not a full taxonomy.
36
+ // If you find yourself wanting to add 50 entries here, that's a sign
37
+ // the description on the relevant SKILL.md should grow instead.
38
+ const UI_CUES = [
39
+ // Korean
40
+ '디자인', '레이아웃', '화면', '컴포넌트', '버튼', '카드', '색', '색상',
41
+ '폰트', '스타일', '브랜드', '리팩토링', '랜딩', '페이지',
42
+ // English
43
+ 'design', 'layout', 'component', 'button', 'card', 'color', 'colour',
44
+ 'font', 'style', 'brand', 'refactor', 'landing', 'redesign', 'rework',
45
+ // Japanese
46
+ 'デザイン', 'レイアウト', 'コンポーネント', 'ボタン', 'スタイル',
47
+ 'カラー', 'フォント', 'ブランド', 'ランディング',
48
+ // Traditional Chinese
49
+ '設計', '佈局', '元件', '按鈕', '風格', '顏色', '字體', '品牌', '落地頁',
50
+ ];
16
51
 
17
52
  let prompt = '';
18
53
  process.stdin.setEncoding('utf8');
19
54
  process.stdin.on('data', (chunk) => (prompt += chunk));
20
55
  process.stdin.on('end', () => {
21
- if (!fs.existsSync(rulesPath)) {
22
- process.exit(0);
23
- }
24
- let rules;
25
- try {
26
- rules = JSON.parse(fs.readFileSync(rulesPath, 'utf8'));
27
- } catch (e) {
28
- process.exit(0);
29
- }
30
- const matches = [];
31
- const lower = prompt.toLowerCase();
32
- for (const [skill, def] of Object.entries(rules.skills || {})) {
33
- const t = def.promptTriggers || {};
34
- const all = [...(t.keywordsKr || []), ...(t.keywordsEn || [])];
35
- const kwHit = all.some((k) => lower.includes(k.toLowerCase()) || prompt.includes(k));
36
- let ipHit = false;
37
- for (const p of t.intentPatterns || []) {
38
- try {
39
- if (new RegExp(p, 'i').test(prompt)) {
40
- ipHit = true;
41
- break;
42
- }
43
- } catch {
44
- // bad regex — skip
45
- }
46
- }
47
- if (kwHit || ipHit) matches.push({ skill, def });
48
- }
49
- if (matches.length === 0) {
50
- process.exit(0);
51
- }
56
+ const designMdPath = path.join(projectDir, 'DESIGN.md');
57
+ if (fs.existsSync(designMdPath)) process.exit(0);
52
58
 
53
- const required = matches.filter((m) => m.def.enforcement === 'required');
54
- const suggested = matches.filter((m) => m.def.enforcement === 'suggest');
59
+ const lower = prompt.toLowerCase();
60
+ const looksLikeUiWork = UI_CUES.some(
61
+ (k) => lower.includes(k.toLowerCase()) || prompt.includes(k),
62
+ );
63
+ if (!looksLikeUiWork) process.exit(0);
55
64
 
56
65
  const lines = [
57
66
  '',
58
- 'OMD SKILL ACTIVATION CHECK',
67
+ 'OMD GATE',
68
+ '⚠️ DESIGN.md not found at project root.',
69
+ ' This prompt looks like UI / design work. Run the omd:init skill first',
70
+ ' to bootstrap DESIGN.md, then re-issue the request so it can be applied',
71
+ ' with brand context.',
72
+ '',
59
73
  ];
60
- if (required.length > 0) {
61
- lines.push('REQUIRED skills (use Skill tool BEFORE responding):');
62
- for (const m of required) lines.push(` → ${m.skill}`);
63
- }
64
- if (suggested.length > 0) {
65
- lines.push('SUGGESTED skills (consider invoking):');
66
- for (const m of suggested) lines.push(` → ${m.skill}`);
67
- }
68
- lines.push('Reasoning: state YES/NO with brief reason for each rule before doing the work.');
69
- lines.push('');
70
-
71
74
  process.stdout.write(JSON.stringify({ additionalContext: lines.join('\n') }));
72
75
  });
73
-
package/README.ja.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img src="logo-bg.png" width="480" alt="oh-my-design" />
2
+ <img src=".github/assets/logo-bg.png" width="480" alt="oh-my-design" />
3
3
  </p>
4
4
 
5
5
  <h1 align="center">oh-my-design</h1>
package/README.ko.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img src="logo-bg.png" width="480" alt="oh-my-design" />
2
+ <img src=".github/assets/logo-bg.png" width="480" alt="oh-my-design" />
3
3
  </p>
4
4
 
5
5
  <h1 align="center">oh-my-design</h1>
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img src="logo-bg.png" width="480" alt="oh-my-design" />
2
+ <img src=".github/assets/logo-bg.png" width="480" alt="oh-my-design" />
3
3
  </p>
4
4
 
5
5
  <h1 align="center">oh-my-design</h1>
package/README.zh-TW.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img src="logo-bg.png" width="480" alt="oh-my-design" />
2
+ <img src=".github/assets/logo-bg.png" width="480" alt="oh-my-design" />
3
3
  </p>
4
4
 
5
5
  <h1 align="center">oh-my-design</h1>
@@ -26,7 +26,7 @@ var program = new Command();
26
26
  program.name("oh-my-design").description("Bootstrap oh-my-design skills + agents into your project. After install, talk to your agent in natural language \u2014 no other CLI commands.").version(readPackageVersion()).showSuggestionAfterError(true).showHelpAfterError(true);
27
27
  program.command("install-skills").description("Install omd skill files + canonical agents into agent directories (.claude/, .codex/, .opencode/)").option("--dir <path>", "Project root (defaults to cwd)").option("--agent <name...>", "Restrict to specific agents (claude-code | codex | opencode)").option("--force", "Overwrite existing files even without the omd marker").action(
28
28
  async (opts) => {
29
- const { runInstallSkills } = await import("../install-skills-SVIYKXOE.js");
29
+ const { runInstallSkills } = await import("../install-skills-MVXVXYAY.js");
30
30
  const validAgents = ["claude-code", "codex", "opencode"];
31
31
  const agents = opts.agent ? opts.agent.filter(
32
32
  (a) => validAgents.includes(a)
@@ -200,27 +200,6 @@ function installHookFile(packageRoot, projectRoot, filename, force) {
200
200
  writeFileSync(destPath, src);
201
201
  return { target, skill: skillLabel, destPath, status: exists ? "updated" : "created" };
202
202
  }
203
- function installSkillRules(packageRoot, projectRoot, force) {
204
- const target = "claude-code";
205
- const skillLabel = "rules:skill-rules.json";
206
- const srcPath = join2(packageRoot, ".claude", "skills", "skill-rules.json");
207
- const destPath = join2(projectRoot, ".claude", "skills", "skill-rules.json");
208
- if (!existsSync2(srcPath)) {
209
- return { target, skill: skillLabel, destPath, status: "skipped-drift" };
210
- }
211
- const src = readFileSync(srcPath, "utf8");
212
- const exists = existsSync2(destPath);
213
- const existing = exists ? readFileSync(destPath, "utf8") : "";
214
- if (exists && existing === src) {
215
- return { target, skill: skillLabel, destPath, status: "unchanged" };
216
- }
217
- if (exists && !force) {
218
- return { target, skill: skillLabel, destPath, status: "skipped-drift" };
219
- }
220
- mkdirSync(dirname(destPath), { recursive: true });
221
- writeFileSync(destPath, src);
222
- return { target, skill: skillLabel, destPath, status: exists ? "updated" : "created" };
223
- }
224
203
  function installSettingsJson(packageRoot, projectRoot, force) {
225
204
  const target = "claude-code";
226
205
  const skillLabel = "settings:.claude/settings.json";
@@ -393,7 +372,6 @@ async function runInstallSkills(opts = {}) {
393
372
  ]) {
394
373
  results.push(installHookFile(packageRoot, projectRoot, hookFile, force));
395
374
  }
396
- results.push(installSkillRules(packageRoot, projectRoot, force));
397
375
  results.push(installSettingsJson(packageRoot, projectRoot, force));
398
376
  }
399
377
  p.log.message(pc.bold("\nResults:"));
@@ -420,11 +398,11 @@ async function runInstallSkills(opts = {}) {
420
398
  "",
421
399
  ` ${pc.dim('"\uD1A0\uC2A4 \uC2A4\uD0C0\uC77C \uAC00\uC871 \uC2DD\uB2E8 \uACF5\uC720 \uC571 \uBA54\uC778 \uD654\uBA74 \uB514\uC790\uC778\uD574\uC918"')}`,
422
400
  ` ${pc.dim('"Linear-clone B2B SaaS dashboard \uB9CC\uB4E4\uACE0 \uC2F6\uC5B4"')}`,
423
- ` ${pc.dim('"\uC774 \uCE74\uB4DC \uC880 \uB354 \uC138\uB828\uB418\uAC8C"')} ${pc.dim("# \uC791\uC5C5 \uC911 \uC790\uC5F0\uC5B4 \u2014 \uC790\uB3D9 hook \uB77C\uC6B0\uD305")}`,
401
+ ` ${pc.dim('"\uC774 \uCE74\uB4DC \uC880 \uB354 \uC138\uB828\uB418\uAC8C"')} ${pc.dim("# \uC791\uC5C5 \uC911 \uC790\uC5F0\uC5B4 \u2014 \uC790\uB3D9 \uB77C\uC6B0\uD305")}`,
424
402
  "",
425
- `${pc.bold("Hook\uC774 \uC790\uB3D9\uC73C\uB85C \uB77C\uC6B0\uD305")} ${pc.dim("\u2014 \uB514\uC790\uC778 \uC758\uB3C4 \uAC10\uC9C0\uD574\uC11C \uD558\uB124\uC2A4/\uC2A4\uD0AC \uD638\uCD9C. \uC2AC\uB798\uC2DC \uBA85\uB839 \uC548 \uCCD0\uB3C4 \uB428.")}`,
403
+ `${pc.bold("Claude\uAC00 description \uB9E4\uCE6D\uC73C\uB85C \uC790\uB3D9 \uB77C\uC6B0\uD305")} ${pc.dim("\u2014 \uC2AC\uB798\uC2DC \uBA85\uB839 \uC548 \uCCD0\uB3C4 \uB428. Hook\uC740 DESIGN.md \uBD80\uC7AC \uC2DC omd:init \uC548\uB0B4\uB9CC.")}`,
426
404
  "",
427
- `${pc.dim("Power user shortcut: ")}${pc.cyan("/omd-harness <task>")} ${pc.dim("\u2014 hook \uC6B0\uD68C, \uC989\uC2DC \uC9C4\uC785.")}`,
405
+ `${pc.dim("Power user shortcut: ")}${pc.cyan("/omd-harness <task>")} ${pc.dim("\u2014 \uC989\uC2DC \uC9C4\uC785.")}`,
428
406
  "",
429
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.")}`
430
408
  ].join("\n");
@@ -439,4 +417,4 @@ async function runInstallSkills(opts = {}) {
439
417
  export {
440
418
  runInstallSkills
441
419
  };
442
- //# sourceMappingURL=install-skills-SVIYKXOE.js.map
420
+ //# sourceMappingURL=install-skills-MVXVXYAY.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('# 작업 중 자연어 — 자동 라우팅')}`,\n '',\n `${pc.bold('Claude가 description 매칭으로 자동 라우팅')} ${pc.dim('— 슬래시 명령 안 쳐도 됨. Hook은 DESIGN.md 부재 시 omd:init 안내만.')}`,\n '',\n `${pc.dim('Power user shortcut: ')}${pc.cyan('/omd-harness <task>')} ${pc.dim('— 즉시 진입.')}`,\n '',\n `${pc.yellow('⚠ Already-running Claude Code session?')} ${pc.dim('Run `/agents` inside the session to reload — or quit (Cmd+Q on macOS) and relaunch. Without reload, hooks/agents do not load.')}`,\n ].join('\\n');\n p.note(nextSteps, 'Next');\n\n p.outro(\n pc.green(\n `Done. 6 skills · 11 sub-agents · 4 hooks installed (${writtenCount} files).`,\n ),\n );\n return 0;\n}\n\n","import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport type AgentId = 'claude-code' | 'codex' | 'opencode' | 'cursor' | 'unknown';\n\nexport function detectCallingAgent(): AgentId {\n const env = process.env;\n\n if (env.CLAUDECODE === '1' || env.CLAUDE_CODE === '1' || env.CLAUDE_CODE_TASK_ID) {\n return 'claude-code';\n }\n if (env.CODEX_SESSION_ID || env.CODEX || env.OPENAI_CODEX) {\n return 'codex';\n }\n if (env.OPENCODE || env.OPENCODE_SESSION) {\n return 'opencode';\n }\n if (env.CURSOR_SESSION_ID || env.CURSOR_AGENT) {\n return 'cursor';\n }\n\n return 'unknown';\n}\n\nexport interface AgentPresence {\n claudeCode: boolean;\n codex: boolean;\n opencode: boolean;\n cursor: boolean;\n}\n\nexport function detectInstalledAgents(projectRoot: string): AgentPresence {\n return {\n claudeCode:\n existsSync(join(projectRoot, '.claude')) ||\n existsSync(join(projectRoot, 'CLAUDE.md')),\n codex:\n existsSync(join(projectRoot, '.codex')) ||\n existsSync(join(projectRoot, 'AGENTS.md')) ||\n existsSync(join(projectRoot, 'AGENTS.override.md')),\n opencode:\n existsSync(join(projectRoot, '.opencode')) ||\n existsSync(join(projectRoot, 'opencode.json')) ||\n existsSync(join(projectRoot, 'opencode.jsonc')),\n cursor:\n existsSync(join(projectRoot, '.cursor')) ||\n existsSync(join(projectRoot, '.cursorrules')),\n };\n}\n"],"mappings":"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","names":["existsSync","join","existsSync","join"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-design-cli",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Bootstrap oh-my-design skills + agents into your project. After install, talk to your AI coding agent in natural language — no other CLI commands.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -19,7 +19,6 @@
19
19
  "data",
20
20
  "references/**/DESIGN.md",
21
21
  ".claude/hooks/*.cjs",
22
- ".claude/skills/skill-rules.json",
23
22
  ".claude/settings.json",
24
23
  "AGENTS.md",
25
24
  "scripts/postinstall.cjs",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: omd:apply
3
- description: "프로젝트의 DESIGN.md를 UI/스타일링/마이크로카피 작업의 authoritative brand context로 적용. 작은 변경(컴포넌트 1개 수정, 단일 텍스트 교체 등)은 인라인 처리하고, 화면 단위 신규 디자인·에셋 생성·접근성 감사·페르소나 검증 복합 작업은 적합한 서브에이전트로 즉시 dispatch. 사용자 교정 발생 omd:remember 스킬을 트리거합니다."
3
+ description: "프로젝트 DESIGN.md를 UI/시각 작업의 brand context로 적용. 컴포넌트·색상·폰트·레이아웃 수정 같은 구체적 요청과 톤·분위기 표현 KR '좀 따뜻하게', EN 'make it warmer/cooler', 日本語「もう少し暖かく」, 繁體中文「更溫暖一點」 모두에 트리거. DESIGN.md 부재 omd:init 우선. 화면 전체 신규 디자인은 omd:harness, 교정 기록은 omd:remember."
4
4
  ---
5
5
 
6
6
  # omd:apply — Brand Context Injection + Dispatch Router
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: omd:harness
3
- description: "디자인 하네스 진입점. 사용자가 '디자인 하네스 돌려줘', '시니어 디자이너처럼 ~ 만들어줘', '/omd-harness <task>'호출하면 run 디렉토리를 부트스트랩하고 omd-master 오케스트레이터를 spawn해서 10-phase 파이프라인(Discovery / Asset / Research / IA / Wireframe / System / Components / AssetSourcing / Microcopy / Validation / Handoff)을 실행, 10 specialist를 dispatch해서 brief + DESIGN.md + wireframes + components + assets + persona-feedback 패키지를 emit합니다."
3
+ description: "화면 전체나 신규 surface처음부터 디자인할 때의 진입점 DiscoveryWireframeComponentsMicrocopyValidation 파이프라인을 omd-master 오케스트레이터로 실행. '랜딩 처음부터', 'production-ready', 'wireframe to production', 「一からデザイン」, 「從頭設計」류의 요청에 트리거. 단일 컴포넌트 수정은 omd:apply."
4
4
  ---
5
5
 
6
6
  # omd:harness — Design Harness Entry
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: omd:init
3
- description: "프로젝트 루트에 DESIGN.md를 생성합니다. 사용자가 프로젝트를 묘사하면 67개 레퍼런스 중 가장 맞는 것을 추천하고, 사용자가 고른 레퍼런스의 톤&매너를 preserve하면서 프로젝트 맥락 delta만 반영한 variation을 생성합니다. '디자인 시스템 만들어줘', 'DESIGN.md 세팅', 'brand 초기화', 'omd init' 같은 요청에 트리거됩니다. CLAUDE.md / AGENTS.md / Cursor rule shim도 함께 설치."
3
+ description: "프로젝트 루트에 DESIGN.md를 부트스트랩 67개 실제 기업 레퍼런스 중 컨텍스트 매칭으로 추천하고 선택된 레퍼런스의 톤&매너를 보존한 variation을 생성. DESIGN.md 부재 상태에서의 UI 작업 또는 '디자인 시스템 세팅', 'set up our design system', 「デザインシステムを作って」, 「建立設計系統」류의 요청에 트리거. CLAUDE.md / AGENTS.md / Cursor rule shim도 함께 설치."
4
4
  ---
5
5
 
6
6
  # omd:init — DESIGN.md Bootstrap
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: omd:learn
3
- description: "누적된 preference 교정사항을 DESIGN.md에 반영합니다. '프리퍼런스 정리해줘', 'DESIGN.md 업데이트', 'preference 반영', 'fold preferences', 'learn from corrections' 같은 요청에 트리거됩니다."
3
+ description: ".omd/preferences.md의 status:pending 항목을 DESIGN.md에 정식 merge하고 status를 applied로 플립. '프리퍼런스 정리해줘', 'fold preferences', 'apply all corrections', 「好みをDESIGN.mdに反映」, 「套用偏好」류의 요청에 트리거. 단발성 교정 기록은 omd:remember."
4
4
  ---
5
5
 
6
6
  # omd:learn — Preference Fold into DESIGN.md
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: omd:remember
3
- description: "디자인 선호/교정을 .omd/preferences.md append합니다. 사용자가 '기억해', '앞으로는 ~해', '우리는 ~하지 않아', 'remember that' 같은 발화를 하거나 디자인 원칙을 명시할트리거됩니다."
3
+ description: "사용자의 디자인 선호·교정을 .omd/preferences.md에 기록. '이거 기억해줘', '앞으로는 이렇게', 'remember this', 'going forward never X', 「覚えておいて」, 「記住這個」류의 발화 또는 omd:apply가 교정을 감지했을 트리거. 기록된 내용은 omd:learn으로 DESIGN.md에 정식 반영."
4
4
  ---
5
5
 
6
6
  # omd:remember — Preference Logger
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: omd:sync
3
- description: "DESIGN.md shim 파일들(CLAUDE.md / AGENTS.md / .cursor/rules/omd-design.mdc)을 관리합니다. 'shim 업데이트', 'AGENTS.md 동기화', 'CLAUDE.md drift 확인' 같은 요청에 트리거됩니다."
3
+ description: "DESIGN.md 변경분을 CLAUDE.md / AGENTS.md / .cursor/rules/omd-design.mdc shim 3종에 전파. 'shim 갱신', 'drift 확인', 'ship', 'publish', 「shimを更新」, 「同步 CLAUDE.md」류의 요청에 트리거. DESIGN.md가 수정됐는데 shim이 오래됐을 때 자동 감지해 제안하기도 함."
4
4
  ---
5
5
 
6
6
  # omd:sync — Shim Maintenance
@@ -1,87 +0,0 @@
1
- {
2
- "version": 1,
3
- "_doc": "OmD skill activation rules — read by .claude/hooks/skill-activation.cjs (UserPromptSubmit hook). Forced-eval pattern (~84% reliability vs ~50% naked description matching). 1.0.0: omd-add-reference removed (no SKILL.md), omd-apply triggers tightened to push complex work into subagent dispatch.",
4
- "skills": {
5
- "omd-apply": {
6
- "enforcement": "required",
7
- "priority": "critical",
8
- "promptTriggers": {
9
- "keywordsKr": ["스타일", "색", "버튼", "카드", "여백", "폰트", "라운드", "그림자", "톤", "카피", "에러 메시지", "empty state", "loading", "컴포넌트", "아이콘", "차트", "그래프", "SVG", "일러스트", "에셋"],
10
- "keywordsEn": ["style", "color", "button", "card", "spacing", "font", "rounded", "shadow", "tone", "copy", "error message", "empty state", "loading", "component", "icon", "chart", "graph", "svg", "illustration", "asset"],
11
- "intentPatterns": [
12
- "(this|these|이|그|저).*?(warmer|cooler|rounder|tighter|softer|더|좀|덜)",
13
- "edit.*\\.(tsx|jsx|css|scss|vue|svelte)",
14
- "(만들|make|create|generate).*?(아이콘|icon|차트|chart|일러스트|illustration|svg)"
15
- ]
16
- }
17
- },
18
- "omd-remember": {
19
- "enforcement": "suggest",
20
- "priority": "medium",
21
- "promptTriggers": {
22
- "keywordsKr": ["기억해", "앞으로", "우리는"],
23
- "keywordsEn": ["remember", "going forward", "we never", "we always"],
24
- "intentPatterns": [
25
- "(원래|never|항상|absolutely).*?(쓰지|don't|do not)",
26
- "rule of thumb"
27
- ]
28
- }
29
- },
30
- "omd-learn": {
31
- "enforcement": "suggest",
32
- "priority": "medium",
33
- "promptTriggers": {
34
- "keywordsKr": ["preference 정리", "프리퍼런스", "교정 반영", "DESIGN.md 업데이트", "fold"],
35
- "keywordsEn": ["fold preferences", "apply preferences", "fold in", "learn from corrections"],
36
- "intentPatterns": ["preference.*?(정리|반영|fold|apply)"]
37
- }
38
- },
39
- "omd-sync": {
40
- "enforcement": "required",
41
- "priority": "high",
42
- "promptTriggers": {
43
- "keywordsKr": ["배포", "퍼블리시", "릴리즈", "동기화", "shim", "sync", "shim 갱신", "drift 확인"],
44
- "keywordsEn": ["ship", "deploy", "publish", "release", "sync", "shim", "drift check"],
45
- "intentPatterns": ["^/?(ship|deploy|publish|sync)\\b"]
46
- }
47
- },
48
- "omd-3d-blender": {
49
- "enforcement": "required",
50
- "priority": "medium",
51
- "promptTriggers": {
52
- "keywordsKr": ["블렌더", "Blender", "3D", "렌더", "렌더링", "GLB", "모델링", "3차원"],
53
- "keywordsEn": ["blender", "3d render", "3d mockup", "glb export", "isometric", "product render", "3d model"],
54
- "intentPatterns": [
55
- "(blender|블렌더).*?(설치|install|사용|연결|쓰)",
56
- "(3d|3D|3차원).*?(mockup|목업|render|렌더|모델|model)",
57
- "(hero|메인).*?(render|렌더|3d|3차원)"
58
- ]
59
- }
60
- },
61
- "omd-init": {
62
- "enforcement": "suggest",
63
- "priority": "low",
64
- "promptTriggers": {
65
- "keywordsKr": ["DESIGN.md", "디자인 시스템", "초기화", "세팅", "레퍼런스", "참고"],
66
- "keywordsEn": ["design system", "init", "bootstrap", "reference"],
67
- "intentPatterns": ["DESIGN\\.md.*?(create|만들|초기화|generate|emit)"]
68
- }
69
- },
70
- "omd-harness": {
71
- "enforcement": "required",
72
- "priority": "high",
73
- "promptTriggers": {
74
- "keywordsKr": ["하네스", "알아서", "전체", "처음부터", "메인 화면", "랜딩", "랜딩페이지", "와이어프레임", "디자인 시작", "디자인 짜줘", "화면 디자인", "전반", "다듬", "정비", "톤 정비", "개선", "프로덕션화", "프로덕션", "실제로 만들", "실배포", "프로젝트화"],
75
- "keywordsEn": ["harness", "from scratch", "full design", "design the entire", "wireframe", "design from", "landing", "landing page", "polish", "improve", "tone", "productionize", "production-ready", "ship it"],
76
- "intentPatterns": [
77
- "design.*?(from scratch|처음부터|전체)",
78
- "(full|전체).*?(design|디자인)",
79
- "(메인|main|first|landing|home|랜딩).*?(화면|screen|page|페이지|디자인|design|만들|build|개선|improve|다듬|정비|polish)",
80
- "(프로덕션|production).*?(화|ize|만들|시키|구조|준비|진행)",
81
- "(실제로|properly|production-ready).*?(만들|ship|구조)",
82
- "(개선|improve|다듬|정비|polish|brush).*?(page|페이지|랜딩|landing|화면|screen|전반|전체|whole)"
83
- ]
84
- }
85
- }
86
- }
87
- }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cli/install-skills.ts","../src/core/agent-detect.ts"],"sourcesContent":["import * as p from '@clack/prompts';\nimport pc from 'picocolors';\nimport {\n readFileSync,\n readdirSync,\n writeFileSync,\n existsSync,\n mkdirSync,\n} from 'node:fs';\nimport { join, dirname, relative } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { detectInstalledAgents } from '../core/agent-detect.js';\n\nexport type SkillTarget = 'claude-code' | 'codex' | 'opencode';\n\nexport interface InstallSkillsOptions {\n dir?: string;\n agents?: SkillTarget[];\n force?: boolean;\n}\n\ninterface InstallPlan {\n target: SkillTarget;\n destDir: string;\n layout: 'folder' | 'flat';\n}\n\nfunction findPackageRoot(): string | null {\n let cur = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 8; i++) {\n if (existsSync(join(cur, 'skills'))) return cur;\n const parent = dirname(cur);\n if (parent === cur) break;\n cur = parent;\n }\n return null;\n}\n\nfunction listShippedSkills(packageRoot: string): string[] {\n const skillsDir = join(packageRoot, 'skills');\n if (!existsSync(skillsDir)) return [];\n return readdirSync(skillsDir)\n .filter((name) => existsSync(join(skillsDir, name, 'SKILL.md')))\n .sort();\n}\n\n/**\n * Canonical agent definitions live at `agents/<name>.md` (markdown with YAML\n * frontmatter). Channel-specific files (.claude/agents/*.md, .codex/agents/*.toml)\n * are generated artifacts — never the source of truth.\n *\n * The package ships only `agents/` and the generator emits per-channel files\n * into the user's project on `omd install-skills`.\n */\nfunction listCanonicalAgents(packageRoot: string): string[] {\n const dir = join(packageRoot, 'agents');\n if (!existsSync(dir)) return [];\n return readdirSync(dir)\n .filter((name) => name.startsWith('omd-') && name.endsWith('.md'))\n .sort();\n}\n\ninterface ParsedAgent {\n name: string;\n description: string;\n tools: string[];\n model: string;\n body: string;\n}\n\n/** Parse `agents/<name>.md` YAML frontmatter + body into structured form. */\nfunction parseCanonicalAgent(packageRoot: string, filename: string): ParsedAgent {\n const src = readFileSync(join(packageRoot, 'agents', filename), 'utf8');\n const match = /^---\\n([\\s\\S]*?)\\n---\\n([\\s\\S]*)$/.exec(src);\n if (!match) {\n throw new Error(`agents/${filename}: missing YAML frontmatter`);\n }\n const fm = match[1];\n const body = match[2];\n const grab = (key: string): string => {\n const re = new RegExp(`^${key}:\\\\s*(.+)$`, 'm');\n const m = re.exec(fm);\n return m ? m[1].trim().replace(/^[\"']|[\"']$/g, '') : '';\n };\n return {\n name: grab('name') || filename.replace(/\\.md$/, ''),\n description: grab('description'),\n tools: grab('tools')\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean),\n model: grab('model') || 'sonnet',\n body,\n };\n}\n\n/** Map Claude tool names to Codex tool names (best-effort). */\nfunction claudeToolsToCodex(tools: string[]): string[] {\n const m: Record<string, string> = {\n Read: 'read_file',\n Write: 'write_file',\n Edit: 'edit_file',\n Bash: 'shell',\n Glob: 'search',\n Grep: 'search',\n WebFetch: 'web_fetch',\n WebSearch: 'search',\n Agent: 'spawn_agent',\n TaskCreate: 'task',\n TaskUpdate: 'task',\n TaskList: 'task',\n };\n const out = new Set<string>();\n for (const t of tools) out.add(m[t] ?? t.toLowerCase());\n return [...out].sort();\n}\n\n/** Map Claude model alias to Codex/OpenAI model id (best-effort). */\nfunction claudeModelToCodex(model: string): string {\n const m: Record<string, string> = {\n haiku: 'gpt-4.1-mini',\n sonnet: 'gpt-4.1',\n opus: 'gpt-4.1',\n };\n return m[model.toLowerCase()] ?? 'gpt-4.1';\n}\n\n/** Render a canonical agent as a Claude Code subagent file.\n * IMPORTANT: Claude Code's subagent parser requires YAML frontmatter (`---`)\n * as the FIRST line of the file. Any preceding content (HTML comments, blank\n * lines) breaks discovery. So we encode the managed-by-omd marker as a\n * custom frontmatter field (`omd_managed: true`) instead of an HTML comment.\n */\nfunction renderClaudeAgent(a: ParsedAgent): string {\n const fm = [\n '---',\n `name: ${a.name}`,\n `description: ${a.description}`,\n `tools: ${a.tools.join(', ')}`,\n `model: ${a.model}`,\n `omd_managed: true`,\n '---',\n '',\n ].join('\\n');\n return fm + a.body;\n}\n\n/** Render a canonical agent as a Codex TOML file (declarative pointer). */\nfunction renderCodexAgent(a: ParsedAgent): string {\n const tools = claudeToolsToCodex(a.tools);\n const model = claudeModelToCodex(a.model);\n const desc = a.description.replace(/\"/g, '\\\\\"');\n return [\n `[agent]`,\n `name = \"${a.name}\"`,\n `description = \"${desc}\"`,\n `model = \"${model}\"`,\n `max_threads = 1`,\n `allowed_tools = [${tools.map((t) => `\"${t}\"`).join(', ')}]`,\n '',\n `instructions = \"\"\"`,\n `Source of truth: agents/${a.name}.md (canonical). The full role spec is`,\n `mirrored to .claude/agents/${a.name}.md when installed for Claude Code.`,\n `Follow that spec verbatim regardless of channel.`,\n '',\n `Codex notes:`,\n `- Spawn sub-agents via spawn_agent with names matching .codex/agents/<name>.toml`,\n `- Use shell to invoke CLI helpers (omd init prepare, omd remember, git apply, npx axe-core, npx lighthouse)`,\n `- All artifacts go inside .omd/runs/run-<latest>/ (or skills/omd-lab-02-design-harness/runs/<lab-version>-...)`,\n `\"\"\"`,\n '',\n ].join('\\n');\n}\n\nfunction planForTarget(projectRoot: string, target: SkillTarget): InstallPlan {\n switch (target) {\n case 'claude-code':\n return {\n target,\n destDir: join(projectRoot, '.claude', 'skills'),\n layout: 'folder',\n };\n case 'codex':\n return {\n target,\n destDir: join(projectRoot, '.codex', 'skills'),\n layout: 'folder',\n };\n case 'opencode':\n return {\n target,\n destDir: join(projectRoot, '.opencode', 'agents'),\n layout: 'flat',\n };\n }\n}\n\nconst MANAGED_HEADER =\n '<!-- omd:installed-skill — managed by `omd install-skills`. Do not edit; rerun the command to refresh. -->';\n\ninterface InstallResult {\n target: SkillTarget;\n skill: string;\n destPath: string;\n status: 'created' | 'updated' | 'unchanged' | 'skipped-drift';\n}\n\nfunction installOne(\n packageRoot: string,\n plan: InstallPlan,\n skill: string,\n force: boolean\n): InstallResult {\n const src = readFileSync(\n join(packageRoot, 'skills', skill, 'SKILL.md'),\n 'utf8'\n );\n const managed = MANAGED_HEADER + '\\n\\n' + src;\n const destPath =\n plan.layout === 'folder'\n ? join(plan.destDir, skill, 'SKILL.md')\n : join(plan.destDir, skill + '.md');\n\n const exists = existsSync(destPath);\n const existing = exists ? readFileSync(destPath, 'utf8') : '';\n\n if (exists && existing === managed) {\n return { target: plan.target, skill, destPath, status: 'unchanged' };\n }\n\n if (exists && !existing.startsWith(MANAGED_HEADER) && !force) {\n return { target: plan.target, skill, destPath, status: 'skipped-drift' };\n }\n\n mkdirSync(dirname(destPath), { recursive: true });\n writeFileSync(destPath, managed, 'utf8');\n return {\n target: plan.target,\n skill,\n destPath,\n status: exists ? 'updated' : 'created',\n };\n}\n\n/** Install a hook script from package's .claude/hooks/ to project. */\nfunction installHookFile(\n packageRoot: string,\n projectRoot: string,\n filename: string,\n force: boolean\n): InstallResult {\n const target: SkillTarget = 'claude-code';\n const skillLabel = `hook:${filename}`;\n const srcPath = join(packageRoot, '.claude', 'hooks', filename);\n const destPath = join(projectRoot, '.claude', 'hooks', filename);\n\n if (!existsSync(srcPath)) {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n const src = readFileSync(srcPath, 'utf8');\n const exists = existsSync(destPath);\n const existing = exists ? readFileSync(destPath, 'utf8') : '';\n if (exists && existing === src) {\n return { target, skill: skillLabel, destPath, status: 'unchanged' };\n }\n if (exists && !force) {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n mkdirSync(dirname(destPath), { recursive: true });\n writeFileSync(destPath, src);\n return { target, skill: skillLabel, destPath, status: exists ? 'updated' : 'created' };\n}\n\n/** Install skill-rules.json from package's .claude/skills/. */\nfunction installSkillRules(\n packageRoot: string,\n projectRoot: string,\n force: boolean\n): InstallResult {\n const target: SkillTarget = 'claude-code';\n const skillLabel = 'rules:skill-rules.json';\n const srcPath = join(packageRoot, '.claude', 'skills', 'skill-rules.json');\n const destPath = join(projectRoot, '.claude', 'skills', 'skill-rules.json');\n if (!existsSync(srcPath)) {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n const src = readFileSync(srcPath, 'utf8');\n const exists = existsSync(destPath);\n const existing = exists ? readFileSync(destPath, 'utf8') : '';\n if (exists && existing === src) {\n return { target, skill: skillLabel, destPath, status: 'unchanged' };\n }\n if (exists && !force) {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n mkdirSync(dirname(destPath), { recursive: true });\n writeFileSync(destPath, src);\n return { target, skill: skillLabel, destPath, status: exists ? 'updated' : 'created' };\n}\n\n/**\n * Install / merge .claude/settings.json. We MERGE hooks (don't clobber user\n * customizations) by checking if the omd-managed `_doc` field is present.\n * Without --force, if a user-edited settings.json exists (no _doc field),\n * we skip with `skipped-drift`.\n */\nfunction installSettingsJson(\n packageRoot: string,\n projectRoot: string,\n force: boolean\n): InstallResult {\n const target: SkillTarget = 'claude-code';\n const skillLabel = 'settings:.claude/settings.json';\n const srcPath = join(packageRoot, '.claude', 'settings.json');\n const destPath = join(projectRoot, '.claude', 'settings.json');\n if (!existsSync(srcPath)) {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n const src = readFileSync(srcPath, 'utf8');\n const exists = existsSync(destPath);\n const existing = exists ? readFileSync(destPath, 'utf8') : '';\n if (exists && existing === src) {\n return { target, skill: skillLabel, destPath, status: 'unchanged' };\n }\n if (exists && !force) {\n // Check if it's the omd-managed version\n try {\n const parsed = JSON.parse(existing);\n if (typeof parsed._doc === 'string' && parsed._doc.includes('OmD')) {\n // managed — overwrite\n } else {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n } catch {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n }\n mkdirSync(dirname(destPath), { recursive: true });\n writeFileSync(destPath, src);\n return { target, skill: skillLabel, destPath, status: exists ? 'updated' : 'created' };\n}\n\n/**\n * Copy a read-only data asset (reference-fingerprints.json, vocabulary.json, …)\n * from the package's `data/` into the project's `.claude/data/` or `.codex/data/`.\n * The skill reads these at runtime — they replace the deprecated `omd init recommend` CLI.\n */\nfunction installDataFile(\n packageRoot: string,\n projectRoot: string,\n channelDir: string,\n dataFilename: string,\n force: boolean\n): InstallResult {\n const target: SkillTarget = channelDir === '.claude' ? 'claude-code' : 'codex';\n const skillLabel = `data:${dataFilename}`;\n\n const srcPath = join(packageRoot, 'data', dataFilename);\n const destPath = join(projectRoot, channelDir, 'data', dataFilename);\n\n if (!existsSync(srcPath)) {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n\n const src = readFileSync(srcPath, 'utf8');\n const exists = existsSync(destPath);\n const existing = exists ? readFileSync(destPath, 'utf8') : '';\n\n // Data files are pure copies — no managed header (would corrupt JSON).\n if (exists && existing === src) {\n return { target, skill: skillLabel, destPath, status: 'unchanged' };\n }\n if (exists && !force) {\n // Honor user customization unless --force\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n\n mkdirSync(dirname(destPath), { recursive: true });\n writeFileSync(destPath, src, 'utf8');\n return {\n target,\n skill: skillLabel,\n destPath,\n status: exists ? 'updated' : 'created',\n };\n}\n\n/**\n * Generate a per-channel agent file from the canonical `agents/<name>.md`.\n *\n * Channel = 'claude' → emits `.claude/agents/<name>.md` (markdown w/ frontmatter)\n * Channel = 'codex' → emits `.codex/agents/<name>.toml` (TOML pointer)\n */\nfunction installAgentFile(\n packageRoot: string,\n projectRoot: string,\n channel: 'claude' | 'codex',\n filename: string,\n force: boolean\n): InstallResult {\n const target: SkillTarget = channel === 'claude' ? 'claude-code' : 'codex';\n const skillLabel = `agent:${filename}`;\n\n const parsed = parseCanonicalAgent(packageRoot, filename);\n const rendered =\n channel === 'claude' ? renderClaudeAgent(parsed) : renderCodexAgent(parsed);\n\n const destFilename =\n channel === 'claude' ? filename : filename.replace(/\\.md$/, '.toml');\n const destPath = join(\n projectRoot,\n channel === 'claude' ? '.claude' : '.codex',\n 'agents',\n destFilename\n );\n\n // For Claude Code: managed marker is encoded as `omd_managed: true` INSIDE the\n // frontmatter (rendered above) — no HTML comment can precede `---` or the\n // subagent loader rejects the file.\n // For Codex: TOML allows leading comments, so `# omd:installed-agent ...` is fine.\n const managed =\n channel === 'claude'\n ? rendered\n : '# omd:installed-agent — generated from agents/' +\n filename +\n ' by `omd install-skills`. Do not edit; rerun the command to refresh.\\n\\n' +\n rendered;\n\n const exists = existsSync(destPath);\n const existing = exists ? readFileSync(destPath, 'utf8') : '';\n\n if (exists && existing === managed) {\n return { target, skill: skillLabel, destPath, status: 'unchanged' };\n }\n\n // Drift detection sentinels:\n // Claude — look for `omd_managed: true` line inside frontmatter\n // Codex — look for `# omd:installed-agent` comment\n const isManaged =\n channel === 'claude'\n ? /\\nomd_managed:\\s*true\\b/.test(existing)\n : existing.startsWith('# omd:installed-agent');\n\n if (exists && !isManaged && !force) {\n return { target, skill: skillLabel, destPath, status: 'skipped-drift' };\n }\n\n mkdirSync(dirname(destPath), { recursive: true });\n writeFileSync(destPath, managed, 'utf8');\n return {\n target,\n skill: skillLabel,\n destPath,\n status: exists ? 'updated' : 'created',\n };\n}\n\nconst STATUS_LABEL: Record<InstallResult['status'], string> = {\n created: pc.green('created'),\n updated: pc.cyan('updated'),\n unchanged: pc.dim('unchanged'),\n 'skipped-drift': pc.yellow('skipped'),\n};\n\nfunction autoDetectTargets(projectRoot: string): SkillTarget[] {\n const presence = detectInstalledAgents(projectRoot);\n const targets: SkillTarget[] = [];\n if (presence.claudeCode) targets.push('claude-code');\n if (presence.codex) targets.push('codex');\n if (presence.opencode) targets.push('opencode');\n // Cursor uses .mdc rules, not skills — installed via `omd sync`.\n if (targets.length === 0) {\n // Fallback: install for all three so user gets coverage even without\n // explicit signal. Idempotent so cost is low.\n return ['claude-code', 'codex', 'opencode'];\n }\n return targets;\n}\n\nexport async function runInstallSkills(\n opts: InstallSkillsOptions = {}\n): Promise<number> {\n const projectRoot = opts.dir ?? process.cwd();\n const packageRoot = findPackageRoot();\n if (!packageRoot) {\n console.error(pc.red('omd install-skills: package data not found'));\n return 1;\n }\n\n const skills = listShippedSkills(packageRoot);\n if (skills.length === 0) {\n console.error(pc.red('omd install-skills: no skills found in package'));\n return 1;\n }\n\n const targets = opts.agents ?? autoDetectTargets(projectRoot);\n const plans = targets.map((t) => planForTarget(projectRoot, t));\n const force = opts.force ?? false;\n\n p.intro(\n pc.bold('omd install-skills') +\n pc.dim(` (${relative(process.cwd(), projectRoot) || '.'})`)\n );\n\n p.log.message(\n pc.bold('Targets: ') +\n targets.map((t) => pc.cyan(t)).join(', ')\n );\n p.log.message(\n pc.bold('Skills: ') + skills.map((s) => pc.cyan(s)).join(', ')\n );\n\n const results: InstallResult[] = [];\n for (const plan of plans) {\n for (const skill of skills) {\n results.push(installOne(packageRoot, plan, skill, force));\n }\n }\n\n // Generate per-channel sub-agent definitions from the canonical `agents/`.\n // This is the v2 portable source-of-truth pattern (oh-my-agent style).\n const canonicalAgents = listCanonicalAgents(packageRoot);\n for (const target of targets) {\n if (target === 'claude-code') {\n for (const filename of canonicalAgents) {\n results.push(installAgentFile(packageRoot, projectRoot, 'claude', filename, force));\n }\n } else if (target === 'codex') {\n for (const filename of canonicalAgents) {\n results.push(installAgentFile(packageRoot, projectRoot, 'codex', filename, force));\n }\n }\n // OpenCode currently has no agent-definition channel — skills only.\n }\n\n // Ship the read-only data assets (reference fingerprints, controlled vocab,\n // human-readable tag matrix, opt-out corpus) into the project so skills + hooks\n // can run entirely on the host CLI's own model — no external API keys.\n const dataFiles = [\n 'reference-fingerprints.json',\n 'reference-tags.md',\n 'vocabulary.json',\n 'synonyms.json',\n 'opt-out-corpus.json',\n ];\n for (const target of targets) {\n if (target === 'claude-code') {\n for (const dataFile of dataFiles) {\n results.push(installDataFile(packageRoot, projectRoot, '.claude', dataFile, force));\n }\n } else if (target === 'codex') {\n for (const dataFile of dataFiles) {\n results.push(installDataFile(packageRoot, projectRoot, '.codex', dataFile, force));\n }\n }\n }\n\n // Install hooks (Claude Code only — Codex / OpenCode have separate hook systems)\n if (targets.includes('claude-code')) {\n for (const hookFile of [\n 'skill-activation.cjs',\n 'session-state-loader.cjs',\n 'post-edit-watch.cjs',\n 'session-end-foldin.cjs',\n ]) {\n results.push(installHookFile(packageRoot, projectRoot, hookFile, force));\n }\n // skill-rules.json\n results.push(installSkillRules(packageRoot, projectRoot, force));\n // settings.json (with merge, never clobber user)\n results.push(installSettingsJson(packageRoot, projectRoot, force));\n }\n\n p.log.message(pc.bold('\\nResults:'));\n for (const r of results) {\n const rel = relative(projectRoot, r.destPath);\n p.log.message(\n ` ${STATUS_LABEL[r.status]} ${pc.dim(r.target.padEnd(12))} ${rel}`\n );\n }\n\n const driftCount = results.filter((r) => r.status === 'skipped-drift').length;\n const writtenCount = results.filter(\n (r) => r.status === 'created' || r.status === 'updated'\n ).length;\n\n if (driftCount > 0) {\n p.outro(\n pc.yellow(\n `${writtenCount} written, ${driftCount} skipped (existing files lack the omd marker — rerun with --force to overwrite).`\n )\n );\n return 0;\n }\n\n // Friendly next-step nudge after successful install\n const nextSteps = [\n `${pc.bold('Open Claude Code (or Codex). Just say what you want:')}`,\n '',\n ` ${pc.dim('\"토스 스타일 가족 식단 공유 앱 메인 화면 디자인해줘\"')}`,\n ` ${pc.dim('\"Linear-clone B2B SaaS dashboard 만들고 싶어\"')}`,\n ` ${pc.dim('\"이 카드 좀 더 세련되게\"')} ${pc.dim('# 작업 중 자연어 — 자동 hook 라우팅')}`,\n '',\n `${pc.bold('Hook이 자동으로 라우팅')} ${pc.dim('— 디자인 의도 감지해서 하네스/스킬 호출. 슬래시 명령 안 쳐도 됨.')}`,\n '',\n `${pc.dim('Power user shortcut: ')}${pc.cyan('/omd-harness <task>')} ${pc.dim('— hook 우회, 즉시 진입.')}`,\n '',\n `${pc.yellow('⚠ Already-running Claude Code session?')} ${pc.dim('Run `/agents` inside the session to reload — or quit (Cmd+Q on macOS) and relaunch. Without reload, hooks/agents do not load.')}`,\n ].join('\\n');\n p.note(nextSteps, 'Next');\n\n p.outro(\n pc.green(\n `Done. 6 skills · 11 sub-agents · 4 hooks installed (${writtenCount} files).`,\n ),\n );\n return 0;\n}\n\n","import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport type AgentId = 'claude-code' | 'codex' | 'opencode' | 'cursor' | 'unknown';\n\nexport function detectCallingAgent(): AgentId {\n const env = process.env;\n\n if (env.CLAUDECODE === '1' || env.CLAUDE_CODE === '1' || env.CLAUDE_CODE_TASK_ID) {\n return 'claude-code';\n }\n if (env.CODEX_SESSION_ID || env.CODEX || env.OPENAI_CODEX) {\n return 'codex';\n }\n if (env.OPENCODE || env.OPENCODE_SESSION) {\n return 'opencode';\n }\n if (env.CURSOR_SESSION_ID || env.CURSOR_AGENT) {\n return 'cursor';\n }\n\n return 'unknown';\n}\n\nexport interface AgentPresence {\n claudeCode: boolean;\n codex: boolean;\n opencode: boolean;\n cursor: boolean;\n}\n\nexport function detectInstalledAgents(projectRoot: string): AgentPresence {\n return {\n claudeCode:\n existsSync(join(projectRoot, '.claude')) ||\n existsSync(join(projectRoot, 'CLAUDE.md')),\n codex:\n existsSync(join(projectRoot, '.codex')) ||\n existsSync(join(projectRoot, 'AGENTS.md')) ||\n existsSync(join(projectRoot, 'AGENTS.override.md')),\n opencode:\n existsSync(join(projectRoot, '.opencode')) ||\n existsSync(join(projectRoot, 'opencode.json')) ||\n existsSync(join(projectRoot, 'opencode.jsonc')),\n cursor:\n existsSync(join(projectRoot, '.cursor')) ||\n existsSync(join(projectRoot, '.cursorrules')),\n };\n}\n"],"mappings":"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","names":["existsSync","join","existsSync","join"]}