oh-my-design-cli 0.1.3 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/.claude/hooks/post-edit-watch.cjs +99 -0
  2. package/.claude/hooks/session-end-foldin.cjs +96 -0
  3. package/.claude/hooks/session-state-loader.cjs +64 -0
  4. package/.claude/hooks/skill-activation.cjs +73 -0
  5. package/.claude/settings.json +55 -0
  6. package/.claude/skills/skill-rules.json +87 -0
  7. package/AGENTS.md +111 -0
  8. package/README.md +75 -202
  9. package/agents/AGENT.md +53 -0
  10. package/agents/omd-3d-blender.md +269 -0
  11. package/agents/omd-a11y-auditor.md +97 -0
  12. package/agents/omd-asset-curator.md +260 -0
  13. package/agents/omd-critic.md +181 -0
  14. package/agents/omd-master.md +548 -0
  15. package/agents/omd-microcopy.md +63 -0
  16. package/agents/omd-persona-tester.md +118 -0
  17. package/agents/omd-ui-junior.md +129 -0
  18. package/agents/omd-ux-engineer.md +265 -0
  19. package/agents/omd-ux-researcher.md +62 -0
  20. package/agents/omd-ux-writer.md +181 -0
  21. package/data/opt-out-corpus.json +141 -0
  22. package/data/reference-fingerprints.json +1495 -0
  23. package/dist/bin/oh-my-design.js +3 -818
  24. package/dist/bin/oh-my-design.js.map +1 -1
  25. package/dist/install-skills-SVIYKXOE.js +442 -0
  26. package/dist/install-skills-SVIYKXOE.js.map +1 -0
  27. package/package.json +23 -21
  28. package/scripts/context.cjs +91 -0
  29. package/scripts/postinstall.cjs +54 -0
  30. package/skills/omd-apply/SKILL.md +64 -53
  31. package/skills/omd-harness/SKILL.md +271 -0
  32. package/skills/omd-learn/SKILL.md +55 -35
  33. package/skills/omd-remember/SKILL.md +93 -15
  34. package/skills/omd-sync/SKILL.md +140 -16
  35. package/dist/chunk-6YNSV3VY.js +0 -35
  36. package/dist/chunk-6YNSV3VY.js.map +0 -1
  37. package/dist/chunk-MHFYGZSO.js +0 -337
  38. package/dist/chunk-MHFYGZSO.js.map +0 -1
  39. package/dist/chunk-N2JG6N4Q.js +0 -264
  40. package/dist/chunk-N2JG6N4Q.js.map +0 -1
  41. package/dist/chunk-OOQQEUGX.js +0 -46
  42. package/dist/chunk-OOQQEUGX.js.map +0 -1
  43. package/dist/chunk-OR5DHENY.js +0 -250
  44. package/dist/chunk-OR5DHENY.js.map +0 -1
  45. package/dist/customizer-CM76752R.js +0 -8
  46. package/dist/customizer-CM76752R.js.map +0 -1
  47. package/dist/index.d.ts +0 -559
  48. package/dist/index.js +0 -3113
  49. package/dist/index.js.map +0 -1
  50. package/dist/init-UMM4XIV5.js +0 -675
  51. package/dist/init-UMM4XIV5.js.map +0 -1
  52. package/dist/install-skills-CM6VXFZJ.js +0 -152
  53. package/dist/install-skills-CM6VXFZJ.js.map +0 -1
  54. package/dist/learn-33LHKEJA.js +0 -140
  55. package/dist/learn-33LHKEJA.js.map +0 -1
  56. package/dist/reference-YMNAOXJQ.js +0 -47
  57. package/dist/reference-YMNAOXJQ.js.map +0 -1
  58. package/dist/reference-parser-TM3CJPNE.js +0 -10
  59. package/dist/reference-parser-TM3CJPNE.js.map +0 -1
  60. package/dist/remember-UAFA5B2O.js +0 -78
  61. package/dist/remember-UAFA5B2O.js.map +0 -1
  62. package/dist/sync-FDYRKNFE.js +0 -417
  63. package/dist/sync-FDYRKNFE.js.map +0 -1
  64. package/dist/templates/templates/design-md.hbs +0 -44
  65. package/dist/templates/templates/partials/agent-prompt-guide.hbs +0 -28
  66. package/dist/templates/templates/partials/color-palette.hbs +0 -49
  67. package/dist/templates/templates/partials/component-stylings.hbs +0 -28
  68. package/dist/templates/templates/partials/depth-elevation.hbs +0 -31
  69. package/dist/templates/templates/partials/dos-donts.hbs +0 -13
  70. package/dist/templates/templates/partials/layout.hbs +0 -30
  71. package/dist/templates/templates/partials/responsive.hbs +0 -25
  72. package/dist/templates/templates/partials/shadcn-tokens.hbs +0 -64
  73. package/dist/templates/templates/partials/typography.hbs +0 -43
  74. package/dist/templates/templates/partials/visual-theme.hbs +0 -26
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env node
2
+ // PostToolUse hook — runs after Edit/Write on .tsx/.jsx/.css/.scss files.
3
+ // Detects if the change introduced a hex/spacing value that's NOT in
4
+ // DESIGN.md tokens, and surfaces a one-line suggestion to capture as preference.
5
+ //
6
+ // Hook input shape (Claude Code hook contract):
7
+ // stdin = JSON with toolName / args / etc.
8
+
9
+ 'use strict';
10
+
11
+ const fs = require('node:fs');
12
+ const path = require('node:path');
13
+
14
+ const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
15
+ const designMd = path.join(projectDir, 'DESIGN.md');
16
+
17
+ let input = '';
18
+ process.stdin.setEncoding('utf8');
19
+ process.stdin.on('data', (c) => (input += c));
20
+ process.stdin.on('end', () => {
21
+ let payload = {};
22
+ try {
23
+ payload = JSON.parse(input || '{}');
24
+ } catch {
25
+ process.exit(0);
26
+ }
27
+ const toolName = payload.toolName || payload.tool_name || '';
28
+ if (!['Edit', 'Write', 'MultiEdit'].includes(toolName)) {
29
+ process.exit(0);
30
+ }
31
+ const filePath = payload.toolInput?.file_path || payload.toolInput?.filePath || '';
32
+ if (!/\.(tsx|jsx|ts|js|css|scss)$/i.test(filePath)) {
33
+ process.exit(0);
34
+ }
35
+
36
+ const newText =
37
+ payload.toolInput?.content ||
38
+ payload.toolInput?.new_string ||
39
+ '';
40
+ if (!newText) process.exit(0);
41
+
42
+ // Normalize a hex to canonical 6-char lowercase form (#abc → #aabbcc)
43
+ function normHex(h) {
44
+ let s = h.toLowerCase();
45
+ if (s.length === 4) {
46
+ // #abc → #aabbcc
47
+ s = '#' + s[1] + s[1] + s[2] + s[2] + s[3] + s[3];
48
+ } else if (s.length === 9) {
49
+ // #aarrggbb (8-char includes alpha) — strip alpha for comparison
50
+ s = s.slice(0, 7);
51
+ }
52
+ return s;
53
+ }
54
+
55
+ // Extract hexes from new content
56
+ const rawHexes = newText.match(/#[0-9a-f]{3,8}\b/gi) || [];
57
+ const hexes = [...new Set(rawHexes.map(normHex))];
58
+ if (hexes.length === 0) process.exit(0);
59
+
60
+ // Read DESIGN.md hexes — also handle CSS vars and oklch (Tailwind v4) by
61
+ // signaling N/A when DESIGN.md is purely token-driven (no inline hex).
62
+ let designHexes = new Set();
63
+ let designUsesTokenSystem = false;
64
+ if (fs.existsSync(designMd)) {
65
+ try {
66
+ const text = fs.readFileSync(designMd, 'utf8');
67
+ const lower = text.toLowerCase();
68
+ for (const h of (lower.match(/#[0-9a-f]{3,8}\b/g) || [])) {
69
+ designHexes.add(normHex(h));
70
+ }
71
+ // If DESIGN.md mostly uses oklch() / CSS vars / token names, hex-only
72
+ // comparison is unreliable — skip warning to avoid noise.
73
+ const hexCount = designHexes.size;
74
+ const oklchCount = (lower.match(/oklch\(/g) || []).length;
75
+ const cssVarCount = (lower.match(/var\(--/g) || []).length;
76
+ if ((oklchCount + cssVarCount) > hexCount * 2) {
77
+ designUsesTokenSystem = true;
78
+ }
79
+ } catch {
80
+ // ignore
81
+ }
82
+ }
83
+
84
+ if (designUsesTokenSystem) process.exit(0); // skip — too noisy
85
+ const introduced = hexes.filter((h) => !designHexes.has(h));
86
+ if (introduced.length === 0) process.exit(0);
87
+
88
+ const lines = [
89
+ '',
90
+ 'OMD WATCH:',
91
+ `방금 ${path.basename(filePath)} 에 DESIGN.md에 없는 색이 들어갔어요: ${introduced.slice(0, 3).join(', ')}`,
92
+ '의도된 거면 \`omd remember "<설명>" --context "' + filePath + '"\` 으로 preference 캡처 추천.',
93
+ '',
94
+ ];
95
+
96
+ // Hook contract: write to additionalContext via JSON stdout
97
+ process.stdout.write(JSON.stringify({ additionalContext: lines.join('\n') }));
98
+ });
99
+
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+ // Stop hook — at session end, run fold-in algorithm on .omd/preferences.md.
3
+ // If proposals exceed threshold, append a note to .omd/timeline.md so the
4
+ // next SessionStart hook surfaces it as "fold-in proposals ready: N".
5
+
6
+ 'use strict';
7
+
8
+ const fs = require('node:fs');
9
+ const path = require('node:path');
10
+
11
+ const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
12
+ const preferencesMd = path.join(projectDir, '.omd', 'preferences.md');
13
+ const timelineMd = path.join(projectDir, '.omd', 'timeline.md');
14
+ const configJson = path.join(projectDir, '.omd', 'config.json');
15
+
16
+ if (!fs.existsSync(preferencesMd)) process.exit(0);
17
+
18
+ let config = {
19
+ fold_in_score_threshold: 60,
20
+ recurrence_window_days: 7,
21
+ };
22
+ try {
23
+ if (fs.existsSync(configJson)) {
24
+ config = { ...config, ...JSON.parse(fs.readFileSync(configJson, 'utf8')) };
25
+ }
26
+ } catch {
27
+ // ignore
28
+ }
29
+
30
+ let prefText;
31
+ try {
32
+ prefText = fs.readFileSync(preferencesMd, 'utf8');
33
+ } catch {
34
+ process.exit(0);
35
+ }
36
+
37
+ // Parse pending entries (very rough)
38
+ const blocks = prefText.split(/^### /m).slice(1);
39
+ const now = Date.now();
40
+ const windowMs = config.recurrence_window_days * 24 * 3600 * 1000;
41
+ const byScope = new Map();
42
+
43
+ for (const block of blocks) {
44
+ const tsMatch = /^- ts:\s*(.+)$/m.exec(block);
45
+ const scopeMatch = /^- scope:\s*(.+)$/m.exec(block);
46
+ const statusMatch = /^- status:\s*(\w+)$/m.exec(block);
47
+ const importanceMatch = /^- importance:\s*([1-5])$/m.exec(block);
48
+ if ((statusMatch?.[1] || 'pending') !== 'pending') continue;
49
+ if (!tsMatch || !scopeMatch) continue;
50
+ const ts = new Date(tsMatch[1]).getTime();
51
+ if (Number.isNaN(ts) || now - ts > windowMs) continue;
52
+ const scope = scopeMatch[1];
53
+ const importance = parseInt(importanceMatch?.[1] || '3', 10);
54
+ const list = byScope.get(scope) || [];
55
+ list.push({ ts, importance });
56
+ byScope.set(scope, list);
57
+ }
58
+
59
+ const proposals = [];
60
+ for (const [scope, entries] of byScope.entries()) {
61
+ if (entries.length < 3) continue;
62
+ const importanceAvg = entries.reduce((s, e) => s + e.importance, 0) / entries.length;
63
+ // Hard floor matches src/core/memory.ts — keep these in sync.
64
+ if (importanceAvg < 2.5) continue;
65
+ const lastTs = entries.reduce((m, e) => Math.max(m, e.ts), 0);
66
+ const daysSince = (now - lastTs) / (24 * 3600 * 1000);
67
+ const recency = Math.exp(-daysSince / 7);
68
+ const score = entries.length * importanceAvg * recency * 10;
69
+ if (score >= config.fold_in_score_threshold) {
70
+ proposals.push({ scope, count: entries.length, score: Math.round(score) });
71
+ }
72
+ }
73
+
74
+ if (proposals.length === 0) process.exit(0);
75
+
76
+ // Append to timeline.md
77
+ const ts = new Date().toISOString();
78
+ const block =
79
+ `## ${ts} — fold_in_proposal\n\n` +
80
+ `${proposals.length} fold-in proposals ready (top: ${proposals[0].scope}, score ${proposals[0].score}).\n\n` +
81
+ '```json\n' +
82
+ JSON.stringify(proposals.slice(0, 5), null, 2) +
83
+ '\n```\n\n';
84
+
85
+ if (!fs.existsSync(path.dirname(timelineMd))) {
86
+ fs.mkdirSync(path.dirname(timelineMd), { recursive: true });
87
+ }
88
+ if (!fs.existsSync(timelineMd)) {
89
+ fs.writeFileSync(timelineMd, '# OMD TIMELINE — per-session journal\n\n' + block);
90
+ } else {
91
+ fs.appendFileSync(timelineMd, block);
92
+ }
93
+
94
+ // Optional: print one-line confirmation to stdout
95
+ process.stdout.write(JSON.stringify({}) || '');
96
+
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env node
2
+ // SessionStart hook — load .omd/state.md (and a recent timeline tail) into the
3
+ // session as additionalContext. If state.md is missing or stale, recompute
4
+ // best-effort from preferences.md + timeline.md.
5
+
6
+ 'use strict';
7
+
8
+ const fs = require('node:fs');
9
+ const path = require('node:path');
10
+
11
+ const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
12
+ const stateMd = path.join(projectDir, '.omd', 'state.md');
13
+ const timelineMd = path.join(projectDir, '.omd', 'timeline.md');
14
+ const preferencesMd = path.join(projectDir, '.omd', 'preferences.md');
15
+
16
+ function safeRead(p) {
17
+ try {
18
+ return fs.readFileSync(p, 'utf8');
19
+ } catch {
20
+ return null;
21
+ }
22
+ }
23
+
24
+ const lines = [];
25
+
26
+ if (fs.existsSync(stateMd)) {
27
+ const content = safeRead(stateMd);
28
+ if (content) {
29
+ lines.push('## OMD ENVIRONMENT STATE');
30
+ lines.push('');
31
+ lines.push(content.split('\n').slice(0, 60).join('\n')); // cap injection size
32
+ lines.push('');
33
+ }
34
+ } else if (fs.existsSync(preferencesMd)) {
35
+ // Best-effort fallback — count pending entries
36
+ const text = safeRead(preferencesMd) || '';
37
+ const pendingCount = (text.match(/^- status:\s*pending\b/gm) || []).length;
38
+ if (pendingCount > 0) {
39
+ lines.push('## OMD ENVIRONMENT STATE');
40
+ lines.push('');
41
+ lines.push(`Pending preferences: ${pendingCount}${pendingCount >= 7 ? ' — consider /omd-learn review' : ''}`);
42
+ lines.push('');
43
+ }
44
+ }
45
+
46
+ if (fs.existsSync(timelineMd)) {
47
+ const text = safeRead(timelineMd) || '';
48
+ const blocks = text.split(/^## /m).slice(1).slice(-3);
49
+ if (blocks.length > 0) {
50
+ lines.push('## RECENT TIMELINE (last 3)');
51
+ lines.push('');
52
+ for (const b of blocks) {
53
+ const firstLine = b.split('\n')[0];
54
+ const summary = (b.split('\n').slice(2).find((l) => l.trim()) || '').trim();
55
+ lines.push(`- ${firstLine.trim()} — ${summary}`);
56
+ }
57
+ lines.push('');
58
+ }
59
+ }
60
+
61
+ if (lines.length > 0) {
62
+ process.stdout.write(JSON.stringify({ additionalContext: lines.join('\n') }));
63
+ }
64
+
@@ -0,0 +1,73 @@
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.
6
+ //
7
+ // Plain CommonJS so it works on any Node ≥18 without build deps.
8
+
9
+ 'use strict';
10
+
11
+ const fs = require('node:fs');
12
+ const path = require('node:path');
13
+
14
+ const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
15
+ const rulesPath = path.join(projectDir, '.claude', 'skills', 'skill-rules.json');
16
+
17
+ let prompt = '';
18
+ process.stdin.setEncoding('utf8');
19
+ process.stdin.on('data', (chunk) => (prompt += chunk));
20
+ 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
+ }
52
+
53
+ const required = matches.filter((m) => m.def.enforcement === 'required');
54
+ const suggested = matches.filter((m) => m.def.enforcement === 'suggest');
55
+
56
+ const lines = [
57
+ '',
58
+ 'OMD SKILL ACTIVATION CHECK',
59
+ ];
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
+ process.stdout.write(JSON.stringify({ additionalContext: lines.join('\n') }));
72
+ });
73
+
@@ -0,0 +1,55 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/claude-code-settings.json",
3
+ "_doc": "OmD project hooks — auto-installed by `omd install-skills`. Re-run with --force to refresh.",
4
+ "hooks": {
5
+ "UserPromptSubmit": [
6
+ {
7
+ "matcher": "",
8
+ "hooks": [
9
+ {
10
+ "type": "command",
11
+ "command": "node ${CLAUDE_PROJECT_DIR}/.claude/hooks/skill-activation.cjs",
12
+ "timeout": 3000
13
+ }
14
+ ]
15
+ }
16
+ ],
17
+ "SessionStart": [
18
+ {
19
+ "matcher": "",
20
+ "hooks": [
21
+ {
22
+ "type": "command",
23
+ "command": "node ${CLAUDE_PROJECT_DIR}/.claude/hooks/session-state-loader.cjs",
24
+ "timeout": 3000
25
+ }
26
+ ]
27
+ }
28
+ ],
29
+ "PostToolUse": [
30
+ {
31
+ "matcher": "Edit|Write|MultiEdit",
32
+ "hooks": [
33
+ {
34
+ "type": "command",
35
+ "command": "node ${CLAUDE_PROJECT_DIR}/.claude/hooks/post-edit-watch.cjs",
36
+ "timeout": 2000
37
+ }
38
+ ]
39
+ }
40
+ ],
41
+ "Stop": [
42
+ {
43
+ "matcher": "",
44
+ "hooks": [
45
+ {
46
+ "type": "command",
47
+ "command": "node ${CLAUDE_PROJECT_DIR}/.claude/hooks/session-end-foldin.cjs",
48
+ "timeout": 5000
49
+ }
50
+ ]
51
+ }
52
+ ]
53
+ }
54
+ }
55
+
@@ -0,0 +1,87 @@
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
+ }
package/AGENTS.md ADDED
@@ -0,0 +1,111 @@
1
+ # AGENTS.md — oh-my-design
2
+
3
+ This is the agent-instruction shim for OpenAI Codex / generic AGENTS.md-aware CLIs working inside this repository.
4
+
5
+ oh-my-design itself uses Claude Code skills + subagents for its design harness. This file is the parity layer for Codex users.
6
+
7
+ ## Repository quick map
8
+
9
+ - `src/` — TypeScript source for the `omd` CLI (`bin/oh-my-design.ts` is the entrypoint).
10
+ - `references/` — 67 real-company DESIGN.md files used as bundled references.
11
+ - `skills/omd-*` — Claude Code / Codex / OpenCode skill files (installed into target projects via `omd install-skills`).
12
+ - `.claude/agents/` — Subagent definitions for the design harness (Claude Code).
13
+ - `.codex/agents/` — Mirror TOML definitions for the design harness (Codex).
14
+ - `spec/omd-v0.1.md` — OmD spec (15-section DESIGN.md format).
15
+ - `research/harness-design/` — Design harness research + integration design.
16
+ - `skills/omd-lab-02-design-harness/` — Lab #02 versioned harness experiments.
17
+
18
+ ## Build / test / lint
19
+
20
+ - Build: `npm run build` (tsup → `dist/`)
21
+ - Type-check: `npm run lint` (`tsc --noEmit`)
22
+ - Unit tests: `npm test` (vitest)
23
+ - The CLI runs via `node dist/bin/oh-my-design.js …` after build.
24
+
25
+ ## Design harness mode
26
+
27
+ User-facing entry: **`/omd-harness <task>`** in Claude Code or Codex CLI.
28
+
29
+ The harness runs entirely inside the host CLI session — no external API keys, no separate process. The skill at `.codex/skills/omd-harness/` (or `.claude/skills/omd-harness/` for Claude Code) handles bootstrapping and orchestration handoff.
30
+
31
+ ### Entrypoint
32
+
33
+ ```
34
+ /omd-harness <도메인 + 톤/스타일 + 핵심 화면>
35
+ ```
36
+
37
+ Examples:
38
+ - `/omd-harness 토스 스타일로 가족용 식단 공유 앱 메인 화면`
39
+ - `/omd-harness Linear-clone B2B SaaS dashboard — 6 widgets, dark first`
40
+ - `/omd-harness 결제 완료 화면 — 성공/실패/부분성공 3 states`
41
+
42
+ For Lab #02 versioned runs, append `--lab v1` (or v2, v3 …) inside the slash command.
43
+
44
+ The skill internally bootstraps the run dir via a hidden `omd harness` helper (slug consistency, standardized subdirs, INDEX.md append). This helper is NOT a user surface — always use `/omd-harness`.
45
+
46
+ ### Codex-specific harness flow
47
+
48
+ When this AGENTS.md is loaded and a fresh `.omd/runs/run-<latest>/` exists:
49
+
50
+ 1. **Activate the orchestrator persona.** Read `.codex/agents/omd-master.toml` and adopt that role. The full behavioral spec lives at `.claude/agents/omd-master.md` — follow it verbatim regardless of channel.
51
+
52
+ 2. **Run the 10 phases in order** (see `.claude/agents/omd-master.md` for full details):
53
+ - Phase 1 — Discovery (5-8 questions, one batch)
54
+ - Phase 1.5 — Asset Brief (spawn omd-asset-curator)
55
+ - Phase 2 — UX Research (parallel × 2-3 omd-ux-researcher)
56
+ - Phase 3 — IA / Journey (master itself) → **user checkpoint #1**
57
+ - Phase 4 — Wireframe (spawn omd-ui-junior)
58
+ - Phase 5 — System / DESIGN.md.patch (master + omd init prepare) → **user checkpoint #2**
59
+ - Phase 6 — Components (spawn omd-ui-junior)
60
+ - Phase 6.5 — Asset Sourcing (spawn omd-asset-curator)
61
+ - Phase 7 — Microcopy (spawn omd-microcopy)
62
+ - Phase 8 — Validation (spawn omd-a11y-auditor → cross-family jury → spawn omd-persona-tester × 4) → **user checkpoint #3**
63
+ - Iteration loop (cap 3) — spawn omd-critic between iterations
64
+ - Phase 9 — Handoff (zip packaging for v0/Cursor/Subframe)
65
+
66
+ 3. **Spawn sub-agents** via Codex's spawn-agent mechanism with names matching `.codex/agents/<name>.toml`:
67
+ - `omd-ux-researcher`
68
+ - `omd-asset-curator`
69
+ - `omd-ui-junior`
70
+ - `omd-microcopy`
71
+ - `omd-a11y-auditor`
72
+ - `omd-persona-tester` (×4 in parallel for Phase 8)
73
+ - `omd-critic` (iteration > 1)
74
+
75
+ 4. **All artifacts go inside the run dir.** Never write outside it except for `DESIGN.md` (Phase 5, with user checkpoint approval) and `.omd/preferences.md` (via `omd remember`).
76
+
77
+ ### User checkpoints (mandatory, do not auto-skip)
78
+
79
+ - Checkpoint #1 (Phase 3 end) — show `journey.mmd`, halt for user reply.
80
+ - Checkpoint #2 (Phase 5 end) — show `DESIGN.md.patch`, halt.
81
+ - Checkpoint #3 (Phase 8 end) — show validation summary, halt.
82
+
83
+ A non-mandatory informational checkpoint #0 follows Phase 1.5 (asset self/fallback/skip choice). All four are gated; do not bypass.
84
+
85
+ ## OmD apply (UI work outside the harness)
86
+
87
+ For ad-hoc UI work (component changes, microcopy edits, color tweaks) not running through the full harness:
88
+
89
+ 1. Read project-root `DESIGN.md` in full at the start of the turn.
90
+ 2. Read `.omd/preferences.md` `pending` entries — these override DESIGN.md until folded in.
91
+ 3. Apply changes citing only DESIGN.md tokens (never invent).
92
+ 4. If the user corrects your design choice, run `omd remember "<one-sentence summary>" --context "<file>"` before ending the turn.
93
+
94
+ This mirrors the `omd:apply` Claude Code skill behavior.
95
+
96
+ ## Hard rules (apply across channels)
97
+
98
+ - Never fabricate DESIGN.md §11-13 (Brand Narrative / Principles / Personas) facts. Use `[FILL IN]` placeholders if user-provided facts are absent.
99
+ - Never introduce a token absent from DESIGN.md without going through Phase 5 (system extension with checkpoint #2 approval).
100
+ - Never download from Pinterest. Pinterest URLs are listed for the user to download manually.
101
+ - Never emit SUS / NPS / "satisfaction score" from synthetic personas. Use task_success / steps_vs_optimal / friction_count / heuristic_violations / abandonment instead.
102
+ - Never auto-skip user checkpoints.
103
+ - Never delete a run directory. They are permanent learning artifacts.
104
+
105
+ ## Pre-existing OmD shims (managed by `omd sync`)
106
+
107
+ Below this line, `omd sync` may add a managed block with shim content for Claude Code / Cursor compat. Do not edit between the markers.
108
+
109
+ <!-- omd:start version=1 -->
110
+ <!-- omd:end -->
111
+