oh-my-design-cli 0.1.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 (317) hide show
  1. package/LICENSE +21 -0
  2. package/README.ja.md +145 -0
  3. package/README.ko.md +245 -0
  4. package/README.md +245 -0
  5. package/README.zh-TW.md +145 -0
  6. package/data/reference-tags.md +104 -0
  7. package/data/synonyms.json +79 -0
  8. package/data/vocabulary.json +516 -0
  9. package/dist/bin/oh-my-design.js +858 -0
  10. package/dist/bin/oh-my-design.js.map +1 -0
  11. package/dist/chunk-6YNSV3VY.js +35 -0
  12. package/dist/chunk-6YNSV3VY.js.map +1 -0
  13. package/dist/chunk-MHFYGZSO.js +337 -0
  14. package/dist/chunk-MHFYGZSO.js.map +1 -0
  15. package/dist/chunk-N2JG6N4Q.js +264 -0
  16. package/dist/chunk-N2JG6N4Q.js.map +1 -0
  17. package/dist/chunk-OOQQEUGX.js +46 -0
  18. package/dist/chunk-OOQQEUGX.js.map +1 -0
  19. package/dist/chunk-OR5DHENY.js +250 -0
  20. package/dist/chunk-OR5DHENY.js.map +1 -0
  21. package/dist/customizer-CM76752R.js +8 -0
  22. package/dist/customizer-CM76752R.js.map +1 -0
  23. package/dist/index.d.ts +559 -0
  24. package/dist/index.js +3113 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/init-STACB7E5.js +635 -0
  27. package/dist/init-STACB7E5.js.map +1 -0
  28. package/dist/install-skills-CM6VXFZJ.js +152 -0
  29. package/dist/install-skills-CM6VXFZJ.js.map +1 -0
  30. package/dist/learn-33LHKEJA.js +140 -0
  31. package/dist/learn-33LHKEJA.js.map +1 -0
  32. package/dist/reference-YMNAOXJQ.js +47 -0
  33. package/dist/reference-YMNAOXJQ.js.map +1 -0
  34. package/dist/reference-parser-TM3CJPNE.js +10 -0
  35. package/dist/reference-parser-TM3CJPNE.js.map +1 -0
  36. package/dist/remember-UAFA5B2O.js +78 -0
  37. package/dist/remember-UAFA5B2O.js.map +1 -0
  38. package/dist/sync-P7X4S2DK.js +404 -0
  39. package/dist/sync-P7X4S2DK.js.map +1 -0
  40. package/dist/templates/templates/design-md.hbs +44 -0
  41. package/dist/templates/templates/partials/agent-prompt-guide.hbs +28 -0
  42. package/dist/templates/templates/partials/color-palette.hbs +49 -0
  43. package/dist/templates/templates/partials/component-stylings.hbs +28 -0
  44. package/dist/templates/templates/partials/depth-elevation.hbs +31 -0
  45. package/dist/templates/templates/partials/dos-donts.hbs +13 -0
  46. package/dist/templates/templates/partials/layout.hbs +30 -0
  47. package/dist/templates/templates/partials/responsive.hbs +25 -0
  48. package/dist/templates/templates/partials/shadcn-tokens.hbs +64 -0
  49. package/dist/templates/templates/partials/typography.hbs +43 -0
  50. package/dist/templates/templates/partials/visual-theme.hbs +26 -0
  51. package/package.json +68 -0
  52. package/references/Claude-Design-Sys-Prompt.txt +421 -0
  53. package/references/airbnb/DESIGN.md +427 -0
  54. package/references/airbnb/README.md +23 -0
  55. package/references/airbnb/preview-dark.html +234 -0
  56. package/references/airbnb/preview.html +233 -0
  57. package/references/airtable/DESIGN.md +107 -0
  58. package/references/airtable/README.md +23 -0
  59. package/references/airtable/preview-dark.html +165 -0
  60. package/references/airtable/preview.html +164 -0
  61. package/references/apple/DESIGN.md +496 -0
  62. package/references/apple/README.md +24 -0
  63. package/references/apple/preview-dark.html +420 -0
  64. package/references/apple/preview.html +414 -0
  65. package/references/baemin/DESIGN.md +260 -0
  66. package/references/baemin/README.md +19 -0
  67. package/references/bmw/DESIGN.md +180 -0
  68. package/references/bmw/README.md +23 -0
  69. package/references/bmw/preview-dark.html +211 -0
  70. package/references/bmw/preview.html +210 -0
  71. package/references/cal/DESIGN.md +259 -0
  72. package/references/cal/README.md +23 -0
  73. package/references/cal/preview-dark.html +449 -0
  74. package/references/cal/preview.html +575 -0
  75. package/references/claude/DESIGN.md +455 -0
  76. package/references/claude/README.md +24 -0
  77. package/references/claude/preview-dark.html +803 -0
  78. package/references/claude/preview.html +826 -0
  79. package/references/clay/DESIGN.md +304 -0
  80. package/references/clay/README.md +23 -0
  81. package/references/clay/preview-dark.html +316 -0
  82. package/references/clay/preview.html +315 -0
  83. package/references/clickhouse/DESIGN.md +281 -0
  84. package/references/clickhouse/README.md +24 -0
  85. package/references/clickhouse/preview-dark.html +834 -0
  86. package/references/clickhouse/preview.html +786 -0
  87. package/references/cohere/DESIGN.md +266 -0
  88. package/references/cohere/README.md +24 -0
  89. package/references/cohere/preview-dark.html +803 -0
  90. package/references/cohere/preview.html +807 -0
  91. package/references/coinbase/DESIGN.md +129 -0
  92. package/references/coinbase/README.md +23 -0
  93. package/references/coinbase/preview-dark.html +164 -0
  94. package/references/coinbase/preview.html +163 -0
  95. package/references/composio/DESIGN.md +307 -0
  96. package/references/composio/README.md +24 -0
  97. package/references/composio/preview-dark.html +958 -0
  98. package/references/composio/preview.html +933 -0
  99. package/references/cursor/DESIGN.md +322 -0
  100. package/references/cursor/README.md +24 -0
  101. package/references/cursor/preview-dark.html +393 -0
  102. package/references/cursor/preview.html +383 -0
  103. package/references/dcard/DESIGN.md +302 -0
  104. package/references/dcard/README.md +12 -0
  105. package/references/dcard/_research/forum-1440px.png +0 -0
  106. package/references/dcard/_research.md +77 -0
  107. package/references/elevenlabs/DESIGN.md +265 -0
  108. package/references/elevenlabs/README.md +23 -0
  109. package/references/elevenlabs/preview-dark.html +252 -0
  110. package/references/elevenlabs/preview.html +251 -0
  111. package/references/expo/DESIGN.md +281 -0
  112. package/references/expo/README.md +24 -0
  113. package/references/expo/preview-dark.html +533 -0
  114. package/references/expo/preview.html +533 -0
  115. package/references/ferrari/DESIGN.md +314 -0
  116. package/references/ferrari/README.md +23 -0
  117. package/references/ferrari/preview-dark.html +1162 -0
  118. package/references/ferrari/preview.html +1122 -0
  119. package/references/figma/DESIGN.md +399 -0
  120. package/references/figma/README.md +24 -0
  121. package/references/figma/preview-dark.html +822 -0
  122. package/references/figma/preview.html +832 -0
  123. package/references/framer/DESIGN.md +246 -0
  124. package/references/framer/README.md +23 -0
  125. package/references/framer/preview-dark.html +902 -0
  126. package/references/framer/preview.html +883 -0
  127. package/references/freee/DESIGN.md +308 -0
  128. package/references/freee/README.md +12 -0
  129. package/references/freee/_research/vibes-storybook-1440px.png +0 -0
  130. package/references/freee/_research.md +77 -0
  131. package/references/hashicorp/DESIGN.md +278 -0
  132. package/references/hashicorp/README.md +24 -0
  133. package/references/hashicorp/preview-dark.html +1202 -0
  134. package/references/hashicorp/preview.html +1193 -0
  135. package/references/ibm/DESIGN.md +332 -0
  136. package/references/ibm/README.md +24 -0
  137. package/references/ibm/preview-dark.html +443 -0
  138. package/references/ibm/preview.html +428 -0
  139. package/references/intercom/DESIGN.md +146 -0
  140. package/references/intercom/README.md +23 -0
  141. package/references/intercom/preview-dark.html +185 -0
  142. package/references/intercom/preview.html +184 -0
  143. package/references/kakao/DESIGN.md +261 -0
  144. package/references/kakao/README.md +18 -0
  145. package/references/karrot/DESIGN.md +252 -0
  146. package/references/karrot/README.md +18 -0
  147. package/references/kraken/DESIGN.md +146 -0
  148. package/references/kraken/README.md +23 -0
  149. package/references/kraken/preview-dark.html +169 -0
  150. package/references/kraken/preview.html +168 -0
  151. package/references/lamborghini/DESIGN.md +288 -0
  152. package/references/lamborghini/README.md +23 -0
  153. package/references/lamborghini/preview-dark.html +303 -0
  154. package/references/lamborghini/preview.html +381 -0
  155. package/references/line/DESIGN.md +375 -0
  156. package/references/line/README.md +12 -0
  157. package/references/line/_research/home-1440px.png +0 -0
  158. package/references/line/_research.md +65 -0
  159. package/references/linear.app/DESIGN.md +526 -0
  160. package/references/linear.app/README.md +24 -0
  161. package/references/linear.app/preview-dark.html +383 -0
  162. package/references/linear.app/preview.html +373 -0
  163. package/references/lovable/DESIGN.md +298 -0
  164. package/references/lovable/README.md +24 -0
  165. package/references/lovable/preview-dark.html +349 -0
  166. package/references/lovable/preview.html +348 -0
  167. package/references/mercari/DESIGN.md +294 -0
  168. package/references/mercari/README.md +12 -0
  169. package/references/mercari/_research/home-1440px.png +0 -0
  170. package/references/mercari/_research.md +77 -0
  171. package/references/minimax/DESIGN.md +257 -0
  172. package/references/minimax/README.md +24 -0
  173. package/references/minimax/preview-dark.html +1262 -0
  174. package/references/minimax/preview.html +1248 -0
  175. package/references/mintlify/DESIGN.md +341 -0
  176. package/references/mintlify/README.md +24 -0
  177. package/references/mintlify/preview-dark.html +409 -0
  178. package/references/mintlify/preview.html +398 -0
  179. package/references/miro/DESIGN.md +108 -0
  180. package/references/miro/README.md +23 -0
  181. package/references/miro/preview-dark.html +174 -0
  182. package/references/miro/preview.html +173 -0
  183. package/references/mistral.ai/DESIGN.md +261 -0
  184. package/references/mistral.ai/README.md +24 -0
  185. package/references/mistral.ai/preview-dark.html +806 -0
  186. package/references/mistral.ai/preview.html +805 -0
  187. package/references/mongodb/DESIGN.md +266 -0
  188. package/references/mongodb/README.md +23 -0
  189. package/references/mongodb/preview-dark.html +260 -0
  190. package/references/mongodb/preview.html +259 -0
  191. package/references/notion/DESIGN.md +492 -0
  192. package/references/notion/README.md +24 -0
  193. package/references/notion/preview-dark.html +372 -0
  194. package/references/notion/preview.html +364 -0
  195. package/references/nvidia/DESIGN.md +308 -0
  196. package/references/nvidia/README.md +24 -0
  197. package/references/nvidia/preview-dark.html +374 -0
  198. package/references/nvidia/preview.html +366 -0
  199. package/references/ollama/DESIGN.md +267 -0
  200. package/references/ollama/README.md +24 -0
  201. package/references/ollama/preview-dark.html +678 -0
  202. package/references/ollama/preview.html +678 -0
  203. package/references/opencode.ai/DESIGN.md +295 -0
  204. package/references/opencode.ai/README.md +24 -0
  205. package/references/opencode.ai/preview-dark.html +366 -0
  206. package/references/opencode.ai/preview.html +357 -0
  207. package/references/pinkoi/DESIGN.md +309 -0
  208. package/references/pinkoi/README.md +12 -0
  209. package/references/pinkoi/_research/browse-1440px.png +0 -0
  210. package/references/pinkoi/_research.md +115 -0
  211. package/references/pinterest/DESIGN.md +230 -0
  212. package/references/pinterest/README.md +23 -0
  213. package/references/pinterest/preview-dark.html +233 -0
  214. package/references/pinterest/preview.html +232 -0
  215. package/references/posthog/DESIGN.md +256 -0
  216. package/references/posthog/README.md +23 -0
  217. package/references/posthog/preview-dark.html +699 -0
  218. package/references/posthog/preview.html +749 -0
  219. package/references/raycast/DESIGN.md +268 -0
  220. package/references/raycast/README.md +23 -0
  221. package/references/raycast/preview-dark.html +606 -0
  222. package/references/raycast/preview.html +688 -0
  223. package/references/renault/DESIGN.md +311 -0
  224. package/references/renault/README.md +23 -0
  225. package/references/renault/preview-dark.html +406 -0
  226. package/references/renault/preview.html +606 -0
  227. package/references/replicate/DESIGN.md +261 -0
  228. package/references/replicate/README.md +24 -0
  229. package/references/replicate/preview-dark.html +828 -0
  230. package/references/replicate/preview.html +831 -0
  231. package/references/resend/DESIGN.md +303 -0
  232. package/references/resend/README.md +23 -0
  233. package/references/resend/preview-dark.html +355 -0
  234. package/references/resend/preview.html +354 -0
  235. package/references/revolut/DESIGN.md +185 -0
  236. package/references/revolut/README.md +23 -0
  237. package/references/revolut/preview-dark.html +234 -0
  238. package/references/revolut/preview.html +233 -0
  239. package/references/runwayml/DESIGN.md +244 -0
  240. package/references/runwayml/README.md +24 -0
  241. package/references/runwayml/preview-dark.html +664 -0
  242. package/references/runwayml/preview.html +665 -0
  243. package/references/sanity/DESIGN.md +357 -0
  244. package/references/sanity/README.md +24 -0
  245. package/references/sanity/preview-dark.html +990 -0
  246. package/references/sanity/preview.html +1135 -0
  247. package/references/sentry/DESIGN.md +262 -0
  248. package/references/sentry/README.md +24 -0
  249. package/references/sentry/preview-dark.html +626 -0
  250. package/references/sentry/preview.html +951 -0
  251. package/references/spacex/DESIGN.md +205 -0
  252. package/references/spacex/README.md +23 -0
  253. package/references/spacex/preview-dark.html +221 -0
  254. package/references/spacex/preview.html +220 -0
  255. package/references/spotify/DESIGN.md +246 -0
  256. package/references/spotify/README.md +23 -0
  257. package/references/spotify/preview-dark.html +231 -0
  258. package/references/spotify/preview.html +230 -0
  259. package/references/stripe/DESIGN.md +473 -0
  260. package/references/stripe/README.md +24 -0
  261. package/references/stripe/preview-dark.html +428 -0
  262. package/references/stripe/preview.html +419 -0
  263. package/references/supabase/DESIGN.md +255 -0
  264. package/references/supabase/README.md +24 -0
  265. package/references/supabase/preview-dark.html +977 -0
  266. package/references/supabase/preview.html +955 -0
  267. package/references/superhuman/DESIGN.md +252 -0
  268. package/references/superhuman/README.md +23 -0
  269. package/references/superhuman/preview-dark.html +973 -0
  270. package/references/superhuman/preview.html +951 -0
  271. package/references/tesla/DESIGN.md +286 -0
  272. package/references/tesla/README.md +23 -0
  273. package/references/tesla/preview-dark.html +947 -0
  274. package/references/tesla/preview.html +925 -0
  275. package/references/together.ai/DESIGN.md +263 -0
  276. package/references/together.ai/README.md +24 -0
  277. package/references/together.ai/preview-dark.html +892 -0
  278. package/references/together.ai/preview.html +897 -0
  279. package/references/toss/DESIGN.md +387 -0
  280. package/references/toss/README.md +19 -0
  281. package/references/uber/DESIGN.md +295 -0
  282. package/references/uber/README.md +24 -0
  283. package/references/uber/preview-dark.html +1120 -0
  284. package/references/uber/preview.html +1119 -0
  285. package/references/vercel/DESIGN.md +456 -0
  286. package/references/vercel/README.md +24 -0
  287. package/references/vercel/preview-dark.html +368 -0
  288. package/references/vercel/preview.html +367 -0
  289. package/references/voltagent/DESIGN.md +323 -0
  290. package/references/voltagent/README.md +24 -0
  291. package/references/voltagent/preview-dark.html +487 -0
  292. package/references/voltagent/preview.html +766 -0
  293. package/references/warp/DESIGN.md +253 -0
  294. package/references/warp/README.md +23 -0
  295. package/references/warp/preview-dark.html +500 -0
  296. package/references/warp/preview.html +533 -0
  297. package/references/webflow/DESIGN.md +109 -0
  298. package/references/webflow/README.md +23 -0
  299. package/references/webflow/preview-dark.html +147 -0
  300. package/references/webflow/preview.html +146 -0
  301. package/references/wise/DESIGN.md +173 -0
  302. package/references/wise/README.md +23 -0
  303. package/references/wise/preview-dark.html +230 -0
  304. package/references/wise/preview.html +229 -0
  305. package/references/x.ai/DESIGN.md +267 -0
  306. package/references/x.ai/README.md +24 -0
  307. package/references/x.ai/preview-dark.html +356 -0
  308. package/references/x.ai/preview.html +407 -0
  309. package/references/zapier/DESIGN.md +328 -0
  310. package/references/zapier/README.md +24 -0
  311. package/references/zapier/preview-dark.html +380 -0
  312. package/references/zapier/preview.html +372 -0
  313. package/skills/omd-apply/SKILL.md +85 -0
  314. package/skills/omd-init/SKILL.md +167 -0
  315. package/skills/omd-learn/SKILL.md +81 -0
  316. package/skills/omd-remember/SKILL.md +44 -0
  317. package/skills/omd-sync/SKILL.md +38 -0
@@ -0,0 +1,635 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli/init.ts
4
+ import * as p from "@clack/prompts";
5
+ import pc from "picocolors";
6
+ import {
7
+ writeFileSync as writeFileSync2,
8
+ readFileSync as readFileSync4,
9
+ mkdirSync as mkdirSync2,
10
+ existsSync as existsSync2
11
+ } from "fs";
12
+ import { join as join4, relative, dirname as dirname4 } from "path";
13
+ import { fileURLToPath as fileURLToPath3 } from "url";
14
+
15
+ // src/core/vocabulary.ts
16
+ import { readFileSync } from "fs";
17
+ import { fileURLToPath } from "url";
18
+ import { dirname, join } from "path";
19
+ var cachedVocab = null;
20
+ var cachedSynonyms = null;
21
+ function dataDir() {
22
+ const here = dirname(fileURLToPath(import.meta.url));
23
+ const candidates = [
24
+ join(here, "..", "data"),
25
+ join(here, "..", "..", "data")
26
+ ];
27
+ for (const c of candidates) {
28
+ try {
29
+ readFileSync(join(c, "vocabulary.json"), "utf8");
30
+ return c;
31
+ } catch {
32
+ }
33
+ }
34
+ throw new Error("data/vocabulary.json not found relative to " + here);
35
+ }
36
+ function loadVocabulary() {
37
+ if (cachedVocab) return cachedVocab;
38
+ const path = join(dataDir(), "vocabulary.json");
39
+ cachedVocab = JSON.parse(readFileSync(path, "utf8"));
40
+ return cachedVocab;
41
+ }
42
+ function loadSynonyms() {
43
+ if (cachedSynonyms) return cachedSynonyms;
44
+ const path = join(dataDir(), "synonyms.json");
45
+ cachedSynonyms = JSON.parse(readFileSync(path, "utf8"));
46
+ return cachedSynonyms;
47
+ }
48
+ var MODIFIER_RE = /\b(primarily|mostly|slightly|very|not)\s+([a-z][a-z-]*)/gi;
49
+ function tokenize(description) {
50
+ return description.toLowerCase().split(/[^a-z-]+/).filter(Boolean);
51
+ }
52
+ function extractKeywords(description) {
53
+ const vocab = loadVocabulary();
54
+ const { map: synonyms } = loadSynonyms();
55
+ const lower = description.toLowerCase();
56
+ const modifierOverrides = /* @__PURE__ */ new Map();
57
+ let match;
58
+ const modRe = new RegExp(MODIFIER_RE.source, MODIFIER_RE.flags);
59
+ while ((match = modRe.exec(lower)) !== null) {
60
+ const modName = match[1];
61
+ const target = match[2];
62
+ const value = vocab.modifiers[modName];
63
+ if (value !== void 0) modifierOverrides.set(target, value);
64
+ }
65
+ const tokens = tokenize(description);
66
+ const seen = /* @__PURE__ */ new Set();
67
+ const results = [];
68
+ for (const token of tokens) {
69
+ if (seen.has(token)) continue;
70
+ seen.add(token);
71
+ if (vocab.keywords[token]) {
72
+ results.push({
73
+ keyword: token,
74
+ modifier: modifierOverrides.get(token) ?? 1,
75
+ matchedAs: "direct"
76
+ });
77
+ continue;
78
+ }
79
+ const syn = synonyms[token];
80
+ if (syn && vocab.keywords[syn]) {
81
+ results.push({
82
+ keyword: syn,
83
+ modifier: modifierOverrides.get(token) ?? 1,
84
+ matchedAs: "synonym",
85
+ synonymSource: token
86
+ });
87
+ }
88
+ }
89
+ return results;
90
+ }
91
+ function resolveConflicts(keywords) {
92
+ const vocab = loadVocabulary();
93
+ const kept = [];
94
+ const dropped = [];
95
+ for (const kw of keywords) {
96
+ const spec = vocab.keywords[kw.keyword];
97
+ if (!spec) continue;
98
+ const conflictingHere = keywords.filter(
99
+ (other) => other.keyword !== kw.keyword && spec.conflicts_with.includes(other.keyword)
100
+ );
101
+ if (conflictingHere.length === 0) {
102
+ kept.push(kw);
103
+ continue;
104
+ }
105
+ const strictlyHigherThanAll = conflictingHere.every(
106
+ (c) => kw.modifier > c.modifier + 1e-9
107
+ );
108
+ if (strictlyHigherThanAll) {
109
+ kept.push(kw);
110
+ continue;
111
+ }
112
+ dropped.push({
113
+ keyword: kw.keyword,
114
+ reason: `conflicts with ${conflictingHere.map((c) => c.keyword).join(",")}`
115
+ });
116
+ }
117
+ const warnings = [];
118
+ const keptSet = new Set(kept.map((k) => k.keyword));
119
+ const warned = /* @__PURE__ */ new Set();
120
+ for (const kw of keywords) {
121
+ const spec = vocab.keywords[kw.keyword];
122
+ if (!spec) continue;
123
+ const conflictingHere = keywords.filter(
124
+ (other) => other.keyword !== kw.keyword && spec.conflicts_with.includes(other.keyword)
125
+ );
126
+ if (conflictingHere.length === 0) continue;
127
+ const groupKeys = [kw.keyword, ...conflictingHere.map((c) => c.keyword)];
128
+ const groupHasWinner = groupKeys.some((k) => keptSet.has(k));
129
+ if (groupHasWinner) continue;
130
+ const pairKey = [...new Set(groupKeys)].sort().join(",");
131
+ if (warned.has(pairKey)) continue;
132
+ warned.add(pairKey);
133
+ warnings.push(
134
+ `${kw.keyword} \u2194 ${conflictingHere.map((c) => c.keyword).join(",")}: use "primarily <kw>" or "very <kw>" to pick a winner`
135
+ );
136
+ }
137
+ return { kept, dropped, warnings };
138
+ }
139
+ function clamp(value, lo, hi) {
140
+ return Math.max(lo, Math.min(hi, value));
141
+ }
142
+ function snap(value, spec) {
143
+ if (spec.type === "int") return Math.round(value);
144
+ if (spec.type === "enum") {
145
+ const [lo, hi] = spec.domain;
146
+ return Math.abs(value - lo) < Math.abs(value - hi) ? lo : hi;
147
+ }
148
+ return Number(value.toFixed(3));
149
+ }
150
+ function buildDeltaSet(description) {
151
+ const vocab = loadVocabulary();
152
+ const matched = extractKeywords(description);
153
+ const { kept, dropped, warnings } = resolveConflicts(matched);
154
+ const axes = {};
155
+ const voiceHintSet = /* @__PURE__ */ new Set();
156
+ for (const kw of kept) {
157
+ const spec = vocab.keywords[kw.keyword];
158
+ const multiplier = kw.modifier;
159
+ for (const hint of spec.voice_hints) voiceHintSet.add(hint);
160
+ for (const [axisName, axisDelta] of Object.entries(spec.axes)) {
161
+ const bucket = axes[axisName] ?? {
162
+ value: 0,
163
+ rangeUnion: [axisDelta.range[0], axisDelta.range[1]],
164
+ sources: []
165
+ };
166
+ bucket.value += axisDelta.delta * multiplier;
167
+ bucket.rangeUnion = [
168
+ Math.min(bucket.rangeUnion[0], axisDelta.range[0]),
169
+ Math.max(bucket.rangeUnion[1], axisDelta.range[1])
170
+ ];
171
+ bucket.sources.push(kw.keyword);
172
+ axes[axisName] = bucket;
173
+ }
174
+ }
175
+ for (const [axisName, bucket] of Object.entries(axes)) {
176
+ const registry = vocab.axis_registry[axisName];
177
+ if (!registry) continue;
178
+ let v = clamp(bucket.value, bucket.rangeUnion[0], bucket.rangeUnion[1]);
179
+ v = clamp(v, registry.domain[0], registry.domain[1]);
180
+ bucket.value = snap(v, registry);
181
+ bucket.sources.sort();
182
+ }
183
+ return {
184
+ axes,
185
+ voiceHints: [...voiceHintSet],
186
+ unresolved: [],
187
+ warnings,
188
+ droppedKeywords: dropped,
189
+ matchedKeywords: kept
190
+ };
191
+ }
192
+
193
+ // src/core/recommend.ts
194
+ import { readFileSync as readFileSync2 } from "fs";
195
+ import { fileURLToPath as fileURLToPath2 } from "url";
196
+ import { dirname as dirname2, join as join2 } from "path";
197
+ var CATEGORY_HINTS = {
198
+ Consumer: [
199
+ "marketplace",
200
+ "shopping",
201
+ "ecommerce",
202
+ "consumer",
203
+ "b2c",
204
+ "retail",
205
+ "subscription",
206
+ "family",
207
+ "families",
208
+ "meal",
209
+ "meals",
210
+ "meal-kit",
211
+ "food",
212
+ "travel",
213
+ "social",
214
+ "community",
215
+ "buyer",
216
+ "seller",
217
+ "parents",
218
+ "kids",
219
+ "lifestyle",
220
+ "recipe",
221
+ "recipes"
222
+ ],
223
+ Fintech: [
224
+ "fintech",
225
+ "banking",
226
+ "bank",
227
+ "payment",
228
+ "payments",
229
+ "crypto",
230
+ "trading",
231
+ "wallet",
232
+ "invest",
233
+ "investing",
234
+ "money",
235
+ "finance",
236
+ "financial",
237
+ "lending",
238
+ "remittance",
239
+ "tax"
240
+ ],
241
+ "Developer Tools": [
242
+ "developer",
243
+ "devtool",
244
+ "devtools",
245
+ "dev-tool",
246
+ "deploy",
247
+ "deployment",
248
+ "build",
249
+ "ci",
250
+ "cd",
251
+ "cli",
252
+ "sdk",
253
+ "editor",
254
+ "ide",
255
+ "engineering",
256
+ "compiler",
257
+ "runtime"
258
+ ],
259
+ AI: [
260
+ "ai",
261
+ "ml",
262
+ "llm",
263
+ "agent",
264
+ "agents",
265
+ "model",
266
+ "models",
267
+ "inference",
268
+ "gpt",
269
+ "chatbot",
270
+ "rag",
271
+ "embedding",
272
+ "embeddings",
273
+ "mcp"
274
+ ],
275
+ "Design Tools": [
276
+ "design",
277
+ "design-tool",
278
+ "whiteboard",
279
+ "prototype",
280
+ "prototyping",
281
+ "wireframe",
282
+ "wireframes",
283
+ "mockup",
284
+ "mockups",
285
+ "figma-like",
286
+ "illustration",
287
+ "canvas"
288
+ ],
289
+ Productivity: [
290
+ "saas",
291
+ "workspace",
292
+ "team",
293
+ "teams",
294
+ "project-management",
295
+ "enterprise",
296
+ "b2b",
297
+ "crm",
298
+ "docs",
299
+ "wiki",
300
+ "collaboration",
301
+ "kanban",
302
+ "scheduling",
303
+ "meetings"
304
+ ],
305
+ Backend: [
306
+ "backend",
307
+ "database",
308
+ "db",
309
+ "api",
310
+ "apis",
311
+ "observability",
312
+ "monitoring",
313
+ "logging",
314
+ "analytics",
315
+ "pipeline",
316
+ "data-pipeline",
317
+ "streaming",
318
+ "queue",
319
+ "cache"
320
+ ],
321
+ Automotive: [
322
+ "car",
323
+ "cars",
324
+ "vehicle",
325
+ "vehicles",
326
+ "auto",
327
+ "automotive",
328
+ "driving",
329
+ "ev",
330
+ "electric-vehicle"
331
+ ],
332
+ Marketing: [
333
+ "marketing",
334
+ "seo",
335
+ "campaign",
336
+ "campaigns",
337
+ "newsletter",
338
+ "email-marketing",
339
+ "attribution"
340
+ ]
341
+ };
342
+ function matchedCategoriesFor(queryTokens, queryStems) {
343
+ const out = /* @__PURE__ */ new Set();
344
+ for (const [category, hints] of Object.entries(CATEGORY_HINTS)) {
345
+ for (const hint of hints) {
346
+ if (queryTokens.has(hint) || queryStems.has(stem(hint))) {
347
+ out.add(category);
348
+ break;
349
+ }
350
+ }
351
+ }
352
+ return out;
353
+ }
354
+ var cachedTags = null;
355
+ function stem(s) {
356
+ let out = s;
357
+ for (const suffix of ["ing", "ed", "ly", "es", "s"]) {
358
+ if (out.length - suffix.length >= 3 && out.endsWith(suffix)) {
359
+ out = out.slice(0, -suffix.length);
360
+ break;
361
+ }
362
+ }
363
+ return out;
364
+ }
365
+ function tagsFilePath() {
366
+ const here = dirname2(fileURLToPath2(import.meta.url));
367
+ const candidates = [
368
+ join2(here, "..", "data", "reference-tags.md"),
369
+ join2(here, "..", "..", "data", "reference-tags.md")
370
+ ];
371
+ for (const c of candidates) {
372
+ try {
373
+ readFileSync2(c, "utf8");
374
+ return c;
375
+ } catch {
376
+ }
377
+ }
378
+ throw new Error("data/reference-tags.md not found");
379
+ }
380
+ var ROW_RE = /^\|\s*([a-z0-9._-]+)\s*\|\s*([^|]*?)\s*\|\s*([^|]*?)\s*\|\s*([^|]*?)\s*\|$/i;
381
+ function loadReferenceTags() {
382
+ if (cachedTags) return cachedTags;
383
+ const raw = readFileSync2(tagsFilePath(), "utf8");
384
+ const rows = [];
385
+ for (const line of raw.split("\n")) {
386
+ const m = ROW_RE.exec(line);
387
+ if (!m) continue;
388
+ const [, id, color, category, keywordsRaw] = m;
389
+ if (id === "id") continue;
390
+ if (id.startsWith("---")) continue;
391
+ rows.push({
392
+ id: id.trim(),
393
+ color: color.trim(),
394
+ category: category.trim(),
395
+ keywords: keywordsRaw.split(",").map((k) => k.trim().toLowerCase()).filter(Boolean)
396
+ });
397
+ }
398
+ cachedTags = rows;
399
+ return rows;
400
+ }
401
+ function recommend(description, opts = {}) {
402
+ const topK = opts.topK ?? 5;
403
+ const diversityByCategory = opts.diversityByCategory ?? true;
404
+ const tags = loadReferenceTags();
405
+ const rawTokens = [
406
+ ...tokenize(description),
407
+ ...description.toLowerCase().split(/[^a-z0-9-]+/).filter(Boolean)
408
+ ];
409
+ const queryTokens = new Set(rawTokens);
410
+ const queryStems = new Set(rawTokens.map(stem));
411
+ const matchedCategories = matchedCategoriesFor(queryTokens, queryStems);
412
+ const tagMatchByRef = tags.map(
413
+ (t) => t.keywords.filter(
414
+ (kw) => queryTokens.has(kw) || queryStems.has(stem(kw))
415
+ )
416
+ );
417
+ const totalTagMatches = tagMatchByRef.reduce((a, m) => a + m.length, 0);
418
+ const scored = tags.map((t, i) => {
419
+ const matched = tagMatchByRef[i];
420
+ const tagScore = matched.length;
421
+ const categoryHit = matchedCategories.has(t.category);
422
+ const categoryBonus = categoryHit && (tagScore > 0 || totalTagMatches === 0) ? 0.5 : 0;
423
+ const score = tagScore + categoryBonus;
424
+ const ratio = matched.length / Math.max(1, t.keywords.length);
425
+ return {
426
+ id: t.id,
427
+ category: t.category,
428
+ color: t.color,
429
+ keywords: t.keywords,
430
+ score,
431
+ matchedKeywords: matched,
432
+ matchedCategories: categoryHit ? [t.category] : [],
433
+ _ratio: ratio
434
+ };
435
+ });
436
+ scored.sort(
437
+ (a, b) => b.score - a.score || b._ratio - a._ratio || a.id.localeCompare(b.id)
438
+ );
439
+ const stripRatio = (s) => s.map(({ _ratio, ...rest }) => {
440
+ void _ratio;
441
+ return rest;
442
+ });
443
+ if (!diversityByCategory) return stripRatio(scored.slice(0, topK));
444
+ const picked = stripRatio(scored).slice(0, 0);
445
+ const pickedSet = /* @__PURE__ */ new Set();
446
+ const usedCategories = /* @__PURE__ */ new Set();
447
+ const allHits = stripRatio(scored);
448
+ for (const hit of allHits) {
449
+ if (picked.length >= topK) break;
450
+ if (usedCategories.has(hit.category)) continue;
451
+ picked.push(hit);
452
+ pickedSet.add(hit.id);
453
+ usedCategories.add(hit.category);
454
+ }
455
+ for (const hit of allHits) {
456
+ if (picked.length >= topK) break;
457
+ if (pickedSet.has(hit.id)) continue;
458
+ picked.push(hit);
459
+ pickedSet.add(hit.id);
460
+ }
461
+ return picked;
462
+ }
463
+
464
+ // src/core/init-deprecate.ts
465
+ import {
466
+ existsSync,
467
+ readFileSync as readFileSync3,
468
+ writeFileSync,
469
+ unlinkSync,
470
+ mkdirSync
471
+ } from "fs";
472
+ import { dirname as dirname3, join as join3 } from "path";
473
+ function deprecationHeader(opts) {
474
+ const now = (opts.now ?? /* @__PURE__ */ new Date()).toISOString();
475
+ const lines = [
476
+ "<!--",
477
+ "omd:deprecated",
478
+ ` deprecated_at: ${now}`
479
+ ];
480
+ if (opts.previousReference)
481
+ lines.push(` previous_reference: ${opts.previousReference}`);
482
+ lines.push(` new_reference: ${opts.newReference}`);
483
+ if (opts.preferencesReplayed !== void 0)
484
+ lines.push(` preferences_replayed: ${opts.preferencesReplayed}`);
485
+ if (opts.preferencesOrphaned !== void 0)
486
+ lines.push(` preferences_orphaned: ${opts.preferencesOrphaned}`);
487
+ if (opts.orphanFile) lines.push(` orphan_file: ${opts.orphanFile}`);
488
+ lines.push(` reason: ${opts.reason}`);
489
+ lines.push("-->", "", "");
490
+ return lines.join("\n");
491
+ }
492
+ function deprecateDesignMd(opts) {
493
+ const from = join3(opts.projectRoot, "DESIGN.md");
494
+ const baseTo = join3(opts.projectRoot, "DESIGN_DEPRECATED.md");
495
+ if (!existsSync(from)) {
496
+ return { renamed: false, from, to: baseTo };
497
+ }
498
+ let target = baseTo;
499
+ if (existsSync(baseTo)) {
500
+ const ts = (opts.now ?? /* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
501
+ target = join3(opts.projectRoot, `DESIGN_DEPRECATED.${ts}.md`);
502
+ }
503
+ mkdirSync(dirname3(target), { recursive: true });
504
+ const prior = readFileSync3(from, "utf8");
505
+ writeFileSync(target, deprecationHeader(opts) + prior, "utf8");
506
+ unlinkSync(from);
507
+ return { renamed: true, from, to: target };
508
+ }
509
+
510
+ // src/cli/init.ts
511
+ function runInitRecommend(opts) {
512
+ const hits = recommend(opts.description, { topK: opts.topK ?? 5 });
513
+ const delta = buildDeltaSet(opts.description);
514
+ if (opts.json) {
515
+ process.stdout.write(
516
+ JSON.stringify(
517
+ {
518
+ description: opts.description,
519
+ recommendations: hits,
520
+ delta_set: delta
521
+ },
522
+ null,
523
+ 2
524
+ )
525
+ );
526
+ process.stdout.write("\n");
527
+ return 0;
528
+ }
529
+ p.intro(pc.bold("omd init \u2014 recommend"));
530
+ p.log.message(pc.dim(`Query: "${opts.description}"
531
+ `));
532
+ if (delta.matchedKeywords.length > 0) {
533
+ p.log.message(
534
+ pc.bold("Matched keywords: ") + delta.matchedKeywords.map((k) => pc.cyan(k.keyword) + pc.dim(` (${k.modifier.toFixed(2)})`)).join(", ")
535
+ );
536
+ }
537
+ if (delta.warnings.length > 0) {
538
+ for (const w of delta.warnings) p.log.warn(w);
539
+ }
540
+ p.log.message(pc.bold("\nTop references:"));
541
+ for (const [i, hit] of hits.entries()) {
542
+ const scoreStr = pc.dim(`[${hit.score.toFixed(2)}]`);
543
+ const matched = hit.matchedKeywords.length > 0 ? pc.green(hit.matchedKeywords.join(", ")) : pc.dim("(no direct tag match)");
544
+ p.log.message(
545
+ ` ${i + 1}. ${pc.bold(hit.id.padEnd(14))} ${scoreStr} ${pc.dim(hit.category.padEnd(14))} ${matched}`
546
+ );
547
+ }
548
+ p.outro(
549
+ pc.dim('Next: `omd init prepare --ref <id> --description "..."` to stage.')
550
+ );
551
+ return 0;
552
+ }
553
+ function runInitPrepare(opts) {
554
+ const projectRoot = opts.dir ?? process.cwd();
555
+ const relRoot = relative(process.cwd(), projectRoot) || ".";
556
+ const refPath = findReferencePath(opts.ref);
557
+ if (!refPath) {
558
+ console.error(pc.red(`omd init prepare: reference not found: ${opts.ref}`));
559
+ return 1;
560
+ }
561
+ const referenceMd = readFileSync4(refPath, "utf8");
562
+ const delta = buildDeltaSet(opts.description);
563
+ const deprecate = deprecateDesignMd({
564
+ projectRoot,
565
+ newReference: opts.ref,
566
+ reason: opts.reason ?? "user-initiated omd init"
567
+ });
568
+ const contextPath = join4(projectRoot, ".omd", "init-context.json");
569
+ mkdirSync2(join4(projectRoot, ".omd"), { recursive: true });
570
+ const context = {
571
+ schema: "omd.init-context/v1",
572
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
573
+ reference_id: opts.ref,
574
+ description: opts.description,
575
+ delta_set: delta,
576
+ deprecated_from: deprecate.renamed ? deprecate.to : null
577
+ };
578
+ writeFileSync2(contextPath, JSON.stringify(context, null, 2) + "\n", "utf8");
579
+ if (opts.json) {
580
+ process.stdout.write(
581
+ JSON.stringify(
582
+ {
583
+ project_root: projectRoot,
584
+ reference_path: refPath,
585
+ context_path: contextPath,
586
+ deprecated_from: deprecate.renamed ? deprecate.to : null,
587
+ reference_md: referenceMd,
588
+ delta_set: delta
589
+ },
590
+ null,
591
+ 2
592
+ )
593
+ );
594
+ process.stdout.write("\n");
595
+ return 0;
596
+ }
597
+ p.intro(pc.bold("omd init \u2014 prepare") + pc.dim(` (${relRoot})`));
598
+ p.log.message(`Reference: ${pc.cyan(opts.ref)}`);
599
+ p.log.message(`Description: ${pc.dim(opts.description)}`);
600
+ if (deprecate.renamed) {
601
+ p.log.warn(
602
+ `Existing DESIGN.md renamed \u2192 ${relative(projectRoot, deprecate.to)}`
603
+ );
604
+ }
605
+ p.log.success(
606
+ `Context staged \u2192 ${relative(projectRoot, contextPath)}`
607
+ );
608
+ p.outro(
609
+ pc.dim(
610
+ "Next: have your agent (Claude Code / Codex / OpenCode) run the `omd:init` skill to generate DESIGN.md from this context."
611
+ )
612
+ );
613
+ return 0;
614
+ }
615
+ function findReferencePath(refId) {
616
+ const root = findRepoRoot();
617
+ if (!root) return null;
618
+ const path = join4(root, "references", refId, "DESIGN.md");
619
+ return existsSync2(path) ? path : null;
620
+ }
621
+ function findRepoRoot() {
622
+ let cur = dirname4(fileURLToPath3(import.meta.url));
623
+ for (let i = 0; i < 8; i++) {
624
+ if (existsSync2(join4(cur, "references"))) return cur;
625
+ const parent = dirname4(cur);
626
+ if (parent === cur) break;
627
+ cur = parent;
628
+ }
629
+ return null;
630
+ }
631
+ export {
632
+ runInitPrepare,
633
+ runInitRecommend
634
+ };
635
+ //# sourceMappingURL=init-STACB7E5.js.map