oh-my-design-cli 1.8.7 → 1.9.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 (247) hide show
  1. package/.claude/hooks/lib/preferences-parser.cjs +1 -0
  2. package/.claude/hooks/lib/preferences-writer.cjs +1 -0
  3. package/.claude/hooks/post-edit-watch.cjs +1 -0
  4. package/.claude/hooks/session-end-foldin.cjs +1 -0
  5. package/.claude/hooks/session-state-loader.cjs +1 -0
  6. package/.claude/hooks/skill-activation.cjs +1 -0
  7. package/.claude/settings.json +10 -1
  8. package/.github/assets/logo-bg.png +0 -0
  9. package/AGENTS.md +20 -1
  10. package/CHANGELOG.md +482 -0
  11. package/MIGRATION.md +103 -0
  12. package/README.ja.md +56 -19
  13. package/README.ko.md +47 -30
  14. package/README.md +48 -30
  15. package/README.zh-TW.md +59 -22
  16. package/agents/AGENT.md +8 -7
  17. package/agents/omd-codex-image.md +1 -1
  18. package/agents/omd-critic.md +6 -7
  19. package/agents/omd-designer-review.md +1 -1
  20. package/agents/omd-final-qa.md +1 -1
  21. package/agents/omd-humanizer.md +38 -0
  22. package/agents/omd-kr-writer.md +3 -4
  23. package/agents/omd-locale-adapter.md +12 -16
  24. package/agents/omd-master.md +24 -19
  25. package/agents/omd-microcopy.md +1 -1
  26. package/agents/omd-orchestrator.md +7 -8
  27. package/agents/omd-slop-auditor.md +31 -0
  28. package/agents/omd-ux-researcher.md +1 -1
  29. package/data/reference-fingerprints.json +1995 -38
  30. package/dist/bin/oh-my-design.js +16 -8
  31. package/dist/bin/oh-my-design.js.map +1 -1
  32. package/dist/chunk-UJIHCEYF.js +99 -0
  33. package/dist/chunk-UJIHCEYF.js.map +1 -0
  34. package/dist/doctor-LDWSFM3I.js +763 -0
  35. package/dist/doctor-LDWSFM3I.js.map +1 -0
  36. package/dist/install-skills-ZZFNJ2II.js +1120 -0
  37. package/dist/install-skills-ZZFNJ2II.js.map +1 -0
  38. package/docs/CLI_QUICKSTART.md +157 -0
  39. package/package.json +25 -4
  40. package/scripts/postinstall.cjs +5 -4
  41. package/skills/claude-design/SKILL.md +38 -14
  42. package/skills/claude-design/references/claude-design-flow.md +11 -11
  43. package/skills/claude-design/references/codebase-analysis.md +5 -4
  44. package/skills/omd-final-qa/SKILL.md +1 -1
  45. package/skills/omd-harness/SKILL.md +51 -50
  46. package/skills/omd-humanize/SKILL.md +116 -0
  47. package/skills/omd-humanize/agents/openai.yaml +4 -0
  48. package/skills/omd-humanize/references/locale-playbooks.md +102 -0
  49. package/skills/omd-humanize/references/sources.md +11 -0
  50. package/skills/omd-init/SKILL.md +18 -12
  51. package/skills/omd-locale-adapter/SKILL.md +58 -91
  52. package/skills/omd-orchestrator/SKILL.md +11 -5
  53. package/skills/omd-reference-capture/SKILL.md +80 -26
  54. package/skills/omd-reference-capture/scripts/PLAYWRIGHT-LICENSE.txt +202 -0
  55. package/skills/omd-reference-capture/scripts/PLAYWRIGHT-NOTICE.txt +5 -0
  56. package/skills/omd-reference-capture/scripts/THIRD_PARTY_NOTICES.md +3 -0
  57. package/skills/omd-reference-capture/scripts/capture-reference-evidence.mjs +159906 -0
  58. package/skills/omd-slop-audit/SKILL.md +110 -0
  59. package/skills/omd-slop-audit/agents/openai.yaml +4 -0
  60. package/skills/omd-slop-audit/references/pattern-catalog.md +96 -0
  61. package/skills/omd-slop-audit/references/sources.md +24 -0
  62. package/web/references/104/DESIGN.md +354 -0
  63. package/web/references/11st/DESIGN.md +197 -316
  64. package/web/references/29cm/DESIGN.md +297 -393
  65. package/web/references/42dot/DESIGN.md +390 -0
  66. package/web/references/8percent/DESIGN.md +438 -0
  67. package/web/references/ably/DESIGN.md +267 -572
  68. package/web/references/acer/DESIGN.md +329 -0
  69. package/web/references/airbnb/DESIGN.md +219 -557
  70. package/web/references/alipay/DESIGN.md +179 -420
  71. package/web/references/apple/DESIGN.md +224 -498
  72. package/web/references/asleep/DESIGN.md +402 -0
  73. package/web/references/au/DESIGN.md +334 -0
  74. package/web/references/autopedia/DESIGN.md +382 -0
  75. package/web/references/aws-cloudscape/DESIGN.md +241 -0
  76. package/web/references/baemin/DESIGN.md +332 -491
  77. package/web/references/banksalad/DESIGN.md +269 -180
  78. package/web/references/barogo/DESIGN.md +416 -0
  79. package/web/references/beusable/DESIGN.md +464 -0
  80. package/web/references/bilibili/DESIGN.md +206 -374
  81. package/web/references/bmw/DESIGN.md +162 -276
  82. package/web/references/brandi/DESIGN.md +184 -325
  83. package/web/references/catchtable/DESIGN.md +219 -264
  84. package/web/references/cgv/DESIGN.md +317 -0
  85. package/web/references/channeltalk/DESIGN.md +210 -336
  86. package/web/references/china-airlines/DESIGN.md +291 -0
  87. package/web/references/cjonstyle/DESIGN.md +453 -0
  88. package/web/references/class101/DESIGN.md +168 -387
  89. package/web/references/classum/DESIGN.md +228 -203
  90. package/web/references/claude/DESIGN.md +242 -479
  91. package/web/references/clay/DESIGN.md +179 -377
  92. package/web/references/clickhouse/DESIGN.md +254 -343
  93. package/web/references/cohere/DESIGN.md +214 -324
  94. package/web/references/coinone/DESIGN.md +199 -179
  95. package/web/references/corca/DESIGN.md +414 -0
  96. package/web/references/coupang/DESIGN.md +205 -475
  97. package/web/references/cursor/DESIGN.md +240 -381
  98. package/web/references/dabang/DESIGN.md +248 -213
  99. package/web/references/dable/DESIGN.md +431 -0
  100. package/web/references/datarize/DESIGN.md +217 -330
  101. package/web/references/dealicious/DESIGN.md +402 -0
  102. package/web/references/drdiary/DESIGN.md +404 -0
  103. package/web/references/elastic/DESIGN.md +238 -0
  104. package/web/references/elevenlabs/DESIGN.md +183 -321
  105. package/web/references/eslite/DESIGN.md +257 -0
  106. package/web/references/evaair/DESIGN.md +338 -0
  107. package/web/references/expo/DESIGN.md +238 -331
  108. package/web/references/familymart-tw/DESIGN.md +254 -0
  109. package/web/references/ferrari/DESIGN.md +160 -377
  110. package/web/references/figma/DESIGN.md +197 -451
  111. package/web/references/fitpet/DESIGN.md +445 -0
  112. package/web/references/flex/DESIGN.md +170 -253
  113. package/web/references/framer/DESIGN.md +197 -340
  114. package/web/references/freee/DESIGN.md +211 -533
  115. package/web/references/friendliai/DESIGN.md +431 -0
  116. package/web/references/frip/DESIGN.md +440 -0
  117. package/web/references/gangnamunni/DESIGN.md +154 -591
  118. package/web/references/gaudiolab/DESIGN.md +414 -0
  119. package/web/references/google/DESIGN.md +290 -398
  120. package/web/references/hashicorp/DESIGN.md +273 -318
  121. package/web/references/heydealer/DESIGN.md +429 -0
  122. package/web/references/humanscape/DESIGN.md +433 -0
  123. package/web/references/hyundai/DESIGN.md +158 -368
  124. package/web/references/hyundaicard/DESIGN.md +171 -121
  125. package/web/references/ibm/DESIGN.md +227 -378
  126. package/web/references/idus/DESIGN.md +450 -0
  127. package/web/references/igaworks/DESIGN.md +437 -0
  128. package/web/references/inflearn/DESIGN.md +221 -366
  129. package/web/references/intuit/DESIGN.md +244 -0
  130. package/web/references/jandi/DESIGN.md +191 -291
  131. package/web/references/jobplanet/DESIGN.md +415 -0
  132. package/web/references/kakao/DESIGN.md +287 -475
  133. package/web/references/kakaobank/DESIGN.md +220 -521
  134. package/web/references/kakaogames/DESIGN.md +220 -0
  135. package/web/references/kakaopay/DESIGN.md +207 -526
  136. package/web/references/karrot/DESIGN.md +227 -251
  137. package/web/references/kb-kookmin/DESIGN.md +308 -0
  138. package/web/references/kbank/DESIGN.md +220 -175
  139. package/web/references/kmong/DESIGN.md +281 -356
  140. package/web/references/krds/DESIGN.md +245 -19
  141. package/web/references/kream/DESIGN.md +224 -359
  142. package/web/references/kurly/DESIGN.md +211 -533
  143. package/web/references/lamborghini/DESIGN.md +198 -360
  144. package/web/references/lguplus/DESIGN.md +259 -0
  145. package/web/references/likelion/DESIGN.md +283 -0
  146. package/web/references/line/DESIGN.md +396 -355
  147. package/web/references/linear.app/DESIGN.md +209 -562
  148. package/web/references/lotteon/DESIGN.md +386 -0
  149. package/web/references/lovable/DESIGN.md +255 -349
  150. package/web/references/makinarocks/DESIGN.md +149 -349
  151. package/web/references/maum-ai/DESIGN.md +423 -0
  152. package/web/references/medibloc/DESIGN.md +425 -0
  153. package/web/references/megabox/DESIGN.md +232 -0
  154. package/web/references/mercari/DESIGN.md +217 -434
  155. package/web/references/microsoft/DESIGN.md +234 -388
  156. package/web/references/mildang/DESIGN.md +411 -0
  157. package/web/references/millie/DESIGN.md +183 -504
  158. package/web/references/minimax/DESIGN.md +264 -322
  159. package/web/references/miricanvas/DESIGN.md +182 -355
  160. package/web/references/miro/DESIGN.md +234 -232
  161. package/web/references/mistral.ai/DESIGN.md +231 -314
  162. package/web/references/mixi/DESIGN.md +234 -0
  163. package/web/references/moin/DESIGN.md +445 -0
  164. package/web/references/mongodb/DESIGN.md +219 -316
  165. package/web/references/musinsa/DESIGN.md +184 -510
  166. package/web/references/mynavi/DESIGN.md +242 -0
  167. package/web/references/myrealtrip/DESIGN.md +169 -406
  168. package/web/references/naver/DESIGN.md +328 -499
  169. package/web/references/naverwebtoon/DESIGN.md +172 -395
  170. package/web/references/nhn/DESIGN.md +169 -346
  171. package/web/references/nhncloud/DESIGN.md +185 -123
  172. package/web/references/notion/DESIGN.md +271 -475
  173. package/web/references/nrise/DESIGN.md +417 -0
  174. package/web/references/ohouse/DESIGN.md +210 -549
  175. package/web/references/oliveyoung/DESIGN.md +217 -326
  176. package/web/references/ollama/DESIGN.md +241 -308
  177. package/web/references/onestore/DESIGN.md +248 -0
  178. package/web/references/palantir/DESIGN.md +256 -0
  179. package/web/references/panasonic/DESIGN.md +276 -0
  180. package/web/references/patternfly/DESIGN.md +259 -0
  181. package/web/references/payhere/DESIGN.md +438 -0
  182. package/web/references/pega/DESIGN.md +262 -0
  183. package/web/references/peoplefund/DESIGN.md +431 -0
  184. package/web/references/petfriends/DESIGN.md +428 -0
  185. package/web/references/pinkfong/DESIGN.md +199 -367
  186. package/web/references/pinterest/DESIGN.md +258 -308
  187. package/web/references/poya/DESIGN.md +286 -0
  188. package/web/references/pozalabs/DESIGN.md +438 -0
  189. package/web/references/protopie/DESIGN.md +419 -0
  190. package/web/references/qraft/DESIGN.md +448 -0
  191. package/web/references/quotabook/DESIGN.md +449 -0
  192. package/web/references/recruit/DESIGN.md +247 -0
  193. package/web/references/remember/DESIGN.md +202 -396
  194. package/web/references/resend/DESIGN.md +274 -348
  195. package/web/references/sakura-internet/DESIGN.md +269 -0
  196. package/web/references/samsung/DESIGN.md +157 -359
  197. package/web/references/sandoll/DESIGN.md +445 -0
  198. package/web/references/sanity/DESIGN.md +241 -375
  199. package/web/references/scatterlab/DESIGN.md +412 -0
  200. package/web/references/selectstar/DESIGN.md +439 -0
  201. package/web/references/servicenow/DESIGN.md +289 -0
  202. package/web/references/shinhanbank/DESIGN.md +161 -341
  203. package/web/references/shinhancard/DESIGN.md +208 -319
  204. package/web/references/sionic/DESIGN.md +454 -0
  205. package/web/references/sktelecom/DESIGN.md +310 -0
  206. package/web/references/socar/DESIGN.md +125 -32
  207. package/web/references/softbank/DESIGN.md +231 -0
  208. package/web/references/sony/DESIGN.md +234 -0
  209. package/web/references/soop/DESIGN.md +261 -0
  210. package/web/references/spacex/DESIGN.md +184 -323
  211. package/web/references/spotify/DESIGN.md +253 -394
  212. package/web/references/squeezebits/DESIGN.md +441 -0
  213. package/web/references/stayfolio/DESIGN.md +422 -0
  214. package/web/references/stripe/DESIGN.md +212 -491
  215. package/web/references/supabase/DESIGN.md +244 -316
  216. package/web/references/tabling/DESIGN.md +415 -0
  217. package/web/references/taishinbank/DESIGN.md +243 -0
  218. package/web/references/teamlab/DESIGN.md +240 -0
  219. package/web/references/teamsparta/DESIGN.md +472 -0
  220. package/web/references/tellingme/DESIGN.md +392 -0
  221. package/web/references/tesla/DESIGN.md +244 -427
  222. package/web/references/thsr/DESIGN.md +284 -0
  223. package/web/references/thumbtack/DESIGN.md +302 -0
  224. package/web/references/together.ai/DESIGN.md +257 -304
  225. package/web/references/toss/DESIGN.md +251 -593
  226. package/web/references/toss-securities/DESIGN.md +255 -178
  227. package/web/references/tossbank/DESIGN.md +195 -491
  228. package/web/references/toyota/DESIGN.md +238 -0
  229. package/web/references/tving/DESIGN.md +197 -245
  230. package/web/references/typed/DESIGN.md +413 -0
  231. package/web/references/uber/DESIGN.md +256 -333
  232. package/web/references/udn/DESIGN.md +250 -0
  233. package/web/references/upbit/DESIGN.md +145 -256
  234. package/web/references/upstage/DESIGN.md +211 -204
  235. package/web/references/uswds/DESIGN.md +287 -0
  236. package/web/references/vercel/DESIGN.md +236 -440
  237. package/web/references/wanted/DESIGN.md +215 -487
  238. package/web/references/wise/DESIGN.md +223 -231
  239. package/web/references/wisetracker/DESIGN.md +462 -0
  240. package/web/references/wooribank/DESIGN.md +245 -0
  241. package/web/references/yanolja/DESIGN.md +250 -419
  242. package/web/references/yeogiotte/DESIGN.md +235 -418
  243. package/web/references/zapier/DESIGN.md +256 -377
  244. package/web/references/zendesk/DESIGN.md +288 -0
  245. package/web/references/zigzag/DESIGN.md +200 -621
  246. package/dist/install-skills-7UUDOLG2.js +0 -734
  247. package/dist/install-skills-7UUDOLG2.js.map +0 -1
@@ -1,734 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/cli/install-skills.ts
4
- import * as p from "@clack/prompts";
5
- import pc from "picocolors";
6
- import {
7
- readFileSync,
8
- readdirSync,
9
- writeFileSync,
10
- existsSync as existsSync2,
11
- mkdirSync,
12
- cpSync
13
- } from "fs";
14
- import { join as join2, dirname, relative } from "path";
15
- import { homedir } from "os";
16
- import { createHash } from "crypto";
17
- import { fileURLToPath } from "url";
18
-
19
- // src/core/agent-detect.ts
20
- import { existsSync } from "fs";
21
- import { join } from "path";
22
- function detectInstalledAgents(projectRoot) {
23
- return {
24
- claudeCode: existsSync(join(projectRoot, ".claude")) || existsSync(join(projectRoot, "CLAUDE.md")),
25
- codex: existsSync(join(projectRoot, ".codex")) || existsSync(join(projectRoot, "AGENTS.md")) || existsSync(join(projectRoot, "AGENTS.override.md")),
26
- opencode: existsSync(join(projectRoot, ".opencode")) || existsSync(join(projectRoot, "opencode.json")) || existsSync(join(projectRoot, "opencode.jsonc")),
27
- cursor: existsSync(join(projectRoot, ".cursor")) || existsSync(join(projectRoot, ".cursorrules"))
28
- };
29
- }
30
-
31
- // src/cli/install-skills.ts
32
- function findPackageRoot() {
33
- let cur = dirname(fileURLToPath(import.meta.url));
34
- for (let i = 0; i < 8; i++) {
35
- if (existsSync2(join2(cur, "skills"))) return cur;
36
- const parent = dirname(cur);
37
- if (parent === cur) break;
38
- cur = parent;
39
- }
40
- return null;
41
- }
42
- function listShippedSkills(packageRoot) {
43
- const skillsDir = join2(packageRoot, "skills");
44
- if (!existsSync2(skillsDir)) return [];
45
- return readdirSync(skillsDir).filter((name) => existsSync2(join2(skillsDir, name, "SKILL.md"))).sort();
46
- }
47
- function listCanonicalAgents(packageRoot) {
48
- const dir = join2(packageRoot, "agents");
49
- if (!existsSync2(dir)) return [];
50
- return readdirSync(dir).filter((name) => name.startsWith("omd-") && name.endsWith(".md")).sort();
51
- }
52
- function parseCanonicalAgent(packageRoot, filename) {
53
- const src = readFileSync(join2(packageRoot, "agents", filename), "utf8");
54
- const match = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/.exec(src);
55
- if (!match) {
56
- throw new Error(`agents/${filename}: missing YAML frontmatter`);
57
- }
58
- const fm = match[1];
59
- const body = match[2];
60
- const grab = (key) => {
61
- const re = new RegExp(`^${key}:\\s*(.+)$`, "m");
62
- const m = re.exec(fm);
63
- return m ? m[1].trim().replace(/^["']|["']$/g, "") : "";
64
- };
65
- return {
66
- name: grab("name") || filename.replace(/\.md$/, ""),
67
- description: grab("description"),
68
- tools: grab("tools").split(",").map((t) => t.trim()).filter(Boolean),
69
- model: grab("model") || "sonnet",
70
- body
71
- };
72
- }
73
- function claudeToolsToCodex(tools) {
74
- const m = {
75
- Read: "read_file",
76
- Write: "write_file",
77
- Edit: "edit_file",
78
- Bash: "shell",
79
- Glob: "search",
80
- Grep: "search",
81
- WebFetch: "web_fetch",
82
- WebSearch: "search",
83
- Agent: "spawn_agent",
84
- TaskCreate: "task",
85
- TaskUpdate: "task",
86
- TaskList: "task"
87
- };
88
- const out = /* @__PURE__ */ new Set();
89
- for (const t of tools) out.add(m[t] ?? t.toLowerCase());
90
- return [...out].sort();
91
- }
92
- function claudeModelToCodex(model) {
93
- const m = {
94
- haiku: "gpt-4.1-mini",
95
- sonnet: "gpt-4.1",
96
- opus: "gpt-4.1"
97
- };
98
- return m[model.toLowerCase()] ?? "gpt-4.1";
99
- }
100
- function renderClaudeAgent(a) {
101
- const fm = [
102
- "---",
103
- `name: ${a.name}`,
104
- `description: ${a.description}`,
105
- `tools: ${a.tools.join(", ")}`,
106
- `model: ${a.model}`,
107
- `omd_managed: true`,
108
- "---",
109
- ""
110
- ].join("\n");
111
- return fm + a.body;
112
- }
113
- function renderCodexAgent(a) {
114
- const tools = claudeToolsToCodex(a.tools);
115
- const model = claudeModelToCodex(a.model);
116
- const desc = a.description.replace(/"/g, '\\"');
117
- return [
118
- `[agent]`,
119
- `name = "${a.name}"`,
120
- `description = "${desc}"`,
121
- `model = "${model}"`,
122
- `max_threads = 1`,
123
- `allowed_tools = [${tools.map((t) => `"${t}"`).join(", ")}]`,
124
- "",
125
- `instructions = """`,
126
- `Source of truth: agents/${a.name}.md (canonical). The full role spec is`,
127
- `mirrored to .claude/agents/${a.name}.md when installed for Claude Code.`,
128
- `Follow that spec verbatim regardless of channel.`,
129
- "",
130
- `Codex notes:`,
131
- `- Spawn sub-agents via spawn_agent with names matching .codex/agents/<name>.toml`,
132
- `- Use shell to invoke CLI helpers (omd init prepare, omd remember, git apply, npx axe-core, npx lighthouse)`,
133
- `- All artifacts go inside .omd/runs/run-<latest>/ (or skills/omd-lab-02-design-harness/runs/<lab-version>-...)`,
134
- `"""`,
135
- ""
136
- ].join("\n");
137
- }
138
- function planForTarget(projectRoot, target) {
139
- switch (target) {
140
- case "claude-code":
141
- return {
142
- target,
143
- destDir: join2(projectRoot, ".claude", "skills"),
144
- layout: "folder"
145
- };
146
- case "codex":
147
- return {
148
- target,
149
- destDir: join2(projectRoot, ".agents", "skills"),
150
- layout: "folder"
151
- };
152
- case "opencode":
153
- return {
154
- target,
155
- destDir: join2(projectRoot, ".opencode", "skills"),
156
- layout: "folder"
157
- };
158
- }
159
- }
160
- var CHANNEL_DATA_DIRS = {
161
- "claude-code": ".claude",
162
- codex: ".codex",
163
- opencode: null,
164
- cursor: null
165
- };
166
- function dataDirFor(target, targets) {
167
- if (target === "cursor") {
168
- return targets.includes("claude-code") ? null : ".claude";
169
- }
170
- return CHANNEL_DATA_DIRS[target];
171
- }
172
- var MANAGED_HEADER = "<!-- omd:installed-skill \u2014 managed by `omd install-skills`. Do not edit; rerun the command to refresh. -->";
173
- var MANAGED_MARKER_SUBSTR = "omd:installed-skill";
174
- function withManagedMarker(src) {
175
- const fm = /^(---\r?\n[\s\S]*?\r?\n---\r?\n)([\s\S]*)$/.exec(src);
176
- if (!fm) {
177
- return MANAGED_HEADER + "\n\n" + src;
178
- }
179
- return fm[1] + MANAGED_HEADER + "\n\n" + fm[2];
180
- }
181
- function isManagedSkillFile(content) {
182
- if (!content) return false;
183
- const head = content.split("\n", 30).join("\n");
184
- return head.includes(MANAGED_MARKER_SUBSTR);
185
- }
186
- var IGNORED_SKILL_ENTRIES = /* @__PURE__ */ new Set([".runtime", "__pycache__", ".DS_Store"]);
187
- function parseSkillChannels(skillMd) {
188
- const fm = /^---\n([\s\S]*?)\n---/.exec(skillMd);
189
- if (!fm) return null;
190
- const m = /^x-omd-channels:\s*(.+)$/m.exec(fm[1]);
191
- if (!m) return null;
192
- const valid = ["claude-code", "codex", "opencode"];
193
- const list = m[1].split(/[,\s]+/).map((s) => s.trim()).filter((s) => valid.includes(s));
194
- return list.length > 0 ? list : null;
195
- }
196
- function skillSupportedChannels(packageRoot, skill) {
197
- return parseSkillChannels(readFileSync(join2(packageRoot, "skills", skill, "SKILL.md"), "utf8")) ?? ["claude-code", "codex", "opencode"];
198
- }
199
- function installOne(packageRoot, plan, skill, force) {
200
- const skillDir = join2(packageRoot, "skills", skill);
201
- const src = readFileSync(join2(skillDir, "SKILL.md"), "utf8");
202
- const managed = withManagedMarker(src);
203
- const channels = parseSkillChannels(src);
204
- if (channels && !channels.includes(plan.target)) {
205
- return {
206
- target: plan.target,
207
- skill,
208
- destPath: join2(plan.destDir, skill + ".md"),
209
- status: "skipped-incompat"
210
- };
211
- }
212
- const extras = readdirSync(skillDir).filter(
213
- (n) => n !== "SKILL.md" && !IGNORED_SKILL_ENTRIES.has(n)
214
- );
215
- const isMultiFile = extras.length > 0;
216
- if (plan.layout !== "folder" && isMultiFile) {
217
- return {
218
- target: plan.target,
219
- skill,
220
- destPath: join2(plan.destDir, skill + ".md"),
221
- status: "skipped-incompat"
222
- };
223
- }
224
- const destPath = plan.layout === "folder" ? join2(plan.destDir, skill, "SKILL.md") : join2(plan.destDir, skill + ".md");
225
- const exists = existsSync2(destPath);
226
- const existing = exists ? readFileSync(destPath, "utf8") : "";
227
- if (exists && existing === managed && !isMultiFile) {
228
- return { target: plan.target, skill, destPath, status: "unchanged" };
229
- }
230
- if (exists && !isManagedSkillFile(existing) && !force) {
231
- return { target: plan.target, skill, destPath, status: "skipped-drift" };
232
- }
233
- mkdirSync(dirname(destPath), { recursive: true });
234
- writeFileSync(destPath, managed, "utf8");
235
- if (plan.layout === "folder" && isMultiFile) {
236
- const destSkillDir = join2(plan.destDir, skill);
237
- for (const entry of extras) {
238
- cpSync(join2(skillDir, entry), join2(destSkillDir, entry), {
239
- recursive: true,
240
- filter: (s) => !/(\/__pycache__|\/\.runtime|\.pyc$|\.DS_Store$)/.test(s)
241
- });
242
- }
243
- }
244
- return {
245
- target: plan.target,
246
- skill,
247
- destPath,
248
- status: exists ? "updated" : "created"
249
- };
250
- }
251
- function installHookFile(packageRoot, projectRoot, filename, force) {
252
- const target = "claude-code";
253
- const skillLabel = `hook:${filename}`;
254
- const srcPath = join2(packageRoot, ".claude", "hooks", filename);
255
- const destPath = join2(projectRoot, ".claude", "hooks", filename);
256
- if (!existsSync2(srcPath)) {
257
- return { target, skill: skillLabel, destPath, status: "skipped-drift" };
258
- }
259
- const src = readFileSync(srcPath, "utf8");
260
- const exists = existsSync2(destPath);
261
- const existing = exists ? readFileSync(destPath, "utf8") : "";
262
- if (exists && existing === src) {
263
- return { target, skill: skillLabel, destPath, status: "unchanged" };
264
- }
265
- if (exists && !force) {
266
- return { target, skill: skillLabel, destPath, status: "skipped-drift" };
267
- }
268
- mkdirSync(dirname(destPath), { recursive: true });
269
- writeFileSync(destPath, src);
270
- return { target, skill: skillLabel, destPath, status: exists ? "updated" : "created" };
271
- }
272
- function installSettingsJson(packageRoot, projectRoot, force) {
273
- const target = "claude-code";
274
- const skillLabel = "settings:.claude/settings.json";
275
- const srcPath = join2(packageRoot, ".claude", "settings.json");
276
- const destPath = join2(projectRoot, ".claude", "settings.json");
277
- if (!existsSync2(srcPath)) {
278
- return { target, skill: skillLabel, destPath, status: "skipped-drift" };
279
- }
280
- const src = readFileSync(srcPath, "utf8");
281
- const exists = existsSync2(destPath);
282
- const existing = exists ? readFileSync(destPath, "utf8") : "";
283
- if (exists && existing === src) {
284
- return { target, skill: skillLabel, destPath, status: "unchanged" };
285
- }
286
- if (exists && !force) {
287
- try {
288
- const parsed = JSON.parse(existing);
289
- if (typeof parsed._doc === "string" && parsed._doc.includes("OmD")) {
290
- } else {
291
- return { target, skill: skillLabel, destPath, status: "skipped-drift" };
292
- }
293
- } catch {
294
- return { target, skill: skillLabel, destPath, status: "skipped-drift" };
295
- }
296
- }
297
- mkdirSync(dirname(destPath), { recursive: true });
298
- writeFileSync(destPath, src);
299
- return { target, skill: skillLabel, destPath, status: exists ? "updated" : "created" };
300
- }
301
- function installDataFile(packageRoot, projectRoot, channelDir, dataFilename, force, target) {
302
- const skillLabel = `data:${dataFilename}`;
303
- const srcPath = join2(packageRoot, "data", dataFilename);
304
- const destPath = join2(projectRoot, channelDir, "data", dataFilename);
305
- if (!existsSync2(srcPath)) {
306
- return { target, skill: skillLabel, destPath, status: "skipped-drift" };
307
- }
308
- const src = readFileSync(srcPath, "utf8");
309
- const exists = existsSync2(destPath);
310
- const existing = exists ? readFileSync(destPath, "utf8") : "";
311
- if (exists && existing === src) {
312
- return { target, skill: skillLabel, destPath, status: "unchanged" };
313
- }
314
- if (exists && !force) {
315
- return { target, skill: skillLabel, destPath, status: "skipped-drift" };
316
- }
317
- mkdirSync(dirname(destPath), { recursive: true });
318
- writeFileSync(destPath, src, "utf8");
319
- return {
320
- target,
321
- skill: skillLabel,
322
- destPath,
323
- status: exists ? "updated" : "created"
324
- };
325
- }
326
- function installAgentFile(packageRoot, projectRoot, channel, filename, force) {
327
- const target = channel === "claude" ? "claude-code" : "codex";
328
- const skillLabel = `agent:${filename}`;
329
- const parsed = parseCanonicalAgent(packageRoot, filename);
330
- const rendered = channel === "claude" ? renderClaudeAgent(parsed) : renderCodexAgent(parsed);
331
- const destFilename = channel === "claude" ? filename : filename.replace(/\.md$/, ".toml");
332
- const destPath = join2(
333
- projectRoot,
334
- channel === "claude" ? ".claude" : ".codex",
335
- "agents",
336
- destFilename
337
- );
338
- const managed = channel === "claude" ? rendered : "# omd:installed-agent \u2014 generated from agents/" + filename + " by `omd install-skills`. Do not edit; rerun the command to refresh.\n\n" + rendered;
339
- const exists = existsSync2(destPath);
340
- const existing = exists ? readFileSync(destPath, "utf8") : "";
341
- if (exists && existing === managed) {
342
- return { target, skill: skillLabel, destPath, status: "unchanged" };
343
- }
344
- const isManaged = channel === "claude" ? /\nomd_managed:\s*true\b/.test(existing) : existing.startsWith("# omd:installed-agent");
345
- if (exists && !isManaged && !force) {
346
- return { target, skill: skillLabel, destPath, status: "skipped-drift" };
347
- }
348
- mkdirSync(dirname(destPath), { recursive: true });
349
- writeFileSync(destPath, managed, "utf8");
350
- return {
351
- target,
352
- skill: skillLabel,
353
- destPath,
354
- status: exists ? "updated" : "created"
355
- };
356
- }
357
- function installReferenceCatalog(packageRoot, installRoot, channelDir, force) {
358
- const srcRoot = join2(packageRoot, "web", "references");
359
- if (!existsSync2(srcRoot)) return 0;
360
- const destRoot = join2(installRoot, channelDir, "data", "references");
361
- let written = 0;
362
- for (const id of readdirSync(srcRoot)) {
363
- const srcDesign = join2(srcRoot, id, "DESIGN.md");
364
- if (!existsSync2(srcDesign)) continue;
365
- const destDesign = join2(destRoot, id, "DESIGN.md");
366
- const src = readFileSync(srcDesign, "utf8");
367
- if (existsSync2(destDesign)) {
368
- const existing = readFileSync(destDesign, "utf8");
369
- if (existing === src) continue;
370
- if (!force) continue;
371
- }
372
- mkdirSync(dirname(destDesign), { recursive: true });
373
- writeFileSync(destDesign, src, "utf8");
374
- written++;
375
- }
376
- return written;
377
- }
378
- var CURSOR_RULE_BODY = [
379
- "The authoritative design spec lives at `@DESIGN.md` (repo root). Open and read before generating/modifying UI.",
380
- "",
381
- "Pending preference corrections: `@.omd/preferences.md`.",
382
- "",
383
- "Precedence: DESIGN.md > preferences.md > framework defaults."
384
- ].join("\n");
385
- function renderCursorRule() {
386
- const hash = createHash("sha256").update(CURSOR_RULE_BODY).digest("hex").slice(0, 12);
387
- return [
388
- "---",
389
- "description: Authoritative brand & UI design system. Read DESIGN.md before UI work.",
390
- "globs:",
391
- ' - "**/*.tsx"',
392
- ' - "**/*.jsx"',
393
- ' - "**/*.vue"',
394
- ' - "**/*.svelte"',
395
- ' - "**/*.css"',
396
- ' - "**/*.scss"',
397
- ' - "**/tailwind.config.*"',
398
- ' - "**/components/**"',
399
- ' - "**/app/**/page.*"',
400
- "alwaysApply: false",
401
- "---",
402
- "",
403
- `<!-- omd:start v=1 hash=${hash} -->`,
404
- CURSOR_RULE_BODY,
405
- "<!-- omd:end -->",
406
- ""
407
- ].join("\n");
408
- }
409
- function installCursorRule(installRoot, force) {
410
- const target = "cursor";
411
- const skillLabel = "rule:omd-design.mdc";
412
- const destPath = join2(installRoot, ".cursor", "rules", "omd-design.mdc");
413
- const rendered = renderCursorRule();
414
- const exists = existsSync2(destPath);
415
- const existing = exists ? readFileSync(destPath, "utf8") : "";
416
- if (exists && existing === rendered) {
417
- return { target, skill: skillLabel, destPath, status: "unchanged" };
418
- }
419
- if (exists && !existing.includes("<!-- omd:start") && !force) {
420
- return { target, skill: skillLabel, destPath, status: "skipped-drift" };
421
- }
422
- mkdirSync(dirname(destPath), { recursive: true });
423
- writeFileSync(destPath, rendered, "utf8");
424
- return { target, skill: skillLabel, destPath, status: exists ? "updated" : "created" };
425
- }
426
- var STATUS_LABEL = {
427
- created: pc.green("created"),
428
- updated: pc.cyan("updated"),
429
- unchanged: pc.dim("unchanged"),
430
- "skipped-drift": pc.yellow("skipped"),
431
- "skipped-incompat": pc.yellow("skipped (claude-code only)")
432
- };
433
- function autoDetectTargets(projectRoot) {
434
- const presence = detectInstalledAgents(projectRoot);
435
- const targets = [];
436
- if (presence.claudeCode) targets.push("claude-code");
437
- if (presence.codex) targets.push("codex");
438
- if (presence.opencode) targets.push("opencode");
439
- if (presence.cursor) targets.push("cursor");
440
- if (targets.length === 0) {
441
- return ["claude-code", "codex", "opencode"];
442
- }
443
- return targets;
444
- }
445
- async function runInstallSkills(opts = {}) {
446
- const projectRoot = opts.dir ?? process.cwd();
447
- const packageRoot = findPackageRoot();
448
- if (!packageRoot) {
449
- console.error(pc.red("omd install-skills: package data not found"));
450
- return 1;
451
- }
452
- const allSkills = listShippedSkills(packageRoot);
453
- if (allSkills.length === 0) {
454
- console.error(pc.red("omd install-skills: no skills found in package"));
455
- return 1;
456
- }
457
- const allAgents = listCanonicalAgents(packageRoot);
458
- const force = opts.force ?? false;
459
- const minimal = opts.skillsOnly === true;
460
- let scope = opts.global ? "global" : "project";
461
- p.intro(
462
- pc.bold("omd install-skills") + pc.dim(` (${relative(process.cwd(), projectRoot) || "."})`)
463
- );
464
- const isTTY = Boolean(process.stdin.isTTY && process.stdout.isTTY);
465
- const interactive = isTTY && !opts.all;
466
- const detected = autoDetectTargets(projectRoot);
467
- const presence = detectInstalledAgents(projectRoot);
468
- const actuallyDetected = [
469
- presence.claudeCode ? "claude-code" : null,
470
- presence.codex ? "codex" : null,
471
- presence.opencode ? "opencode" : null,
472
- presence.cursor ? "cursor" : null
473
- ].filter((x) => x !== null);
474
- if (!opts.global && interactive) {
475
- const scopeResult = await p.select({
476
- message: "Install scope \xB7 \uC5B4\uB514\uC5D0 \uC124\uCE58\uD560\uAE4C\uC694?",
477
- options: [
478
- { value: "project", label: "Project", hint: `${relative(process.cwd(), projectRoot) || "."}/.claude/skills \xB7 \uC774 \uD504\uB85C\uC81D\uD2B8\uB9CC` },
479
- { value: "global", label: "Global", hint: "~/.claude/skills \xB7 \uBAA8\uB4E0 \uD504\uB85C\uC81D\uD2B8 (skills + sub-agents, hooks/settings \uC81C\uC678)" }
480
- ],
481
- initialValue: "project"
482
- });
483
- if (p.isCancel(scopeResult)) {
484
- p.cancel("Install cancelled.");
485
- return 130;
486
- }
487
- scope = scopeResult;
488
- }
489
- let skills;
490
- if (opts.skillsFilter) {
491
- skills = allSkills.filter((s) => opts.skillsFilter.includes(s));
492
- } else if (interactive) {
493
- const skillResult = await p.multiselect({
494
- message: "Skills \xB7 space = \uD1A0\uAE00 \xB7 a = \uC804\uCCB4 \xB7 enter = \uD655\uC778 (default ALL)",
495
- options: allSkills.map((s) => ({ value: s, label: s, hint: "omd skill" })),
496
- initialValues: allSkills,
497
- required: true
498
- });
499
- if (p.isCancel(skillResult)) {
500
- p.cancel("Install cancelled.");
501
- return 130;
502
- }
503
- skills = skillResult;
504
- } else {
505
- skills = allSkills;
506
- }
507
- let canonicalAgents;
508
- if (minimal) {
509
- canonicalAgents = [];
510
- } else if (opts.agentsFilter) {
511
- canonicalAgents = allAgents.filter((a) => opts.agentsFilter.includes(a.replace(/\.md$/, "")));
512
- } else if (interactive && allAgents.length > 0) {
513
- const agentResult = await p.multiselect({
514
- message: "Sub-agents \xB7 space = \uD1A0\uAE00 \xB7 a = \uC804\uCCB4 \xB7 enter = \uD655\uC778 (default ALL)",
515
- options: allAgents.map((a) => ({ value: a, label: a.replace(/\.md$/, ""), hint: "subagent" })),
516
- initialValues: allAgents,
517
- required: false
518
- });
519
- if (p.isCancel(agentResult)) {
520
- p.cancel("Install cancelled.");
521
- return 130;
522
- }
523
- canonicalAgents = agentResult;
524
- } else {
525
- canonicalAgents = allAgents;
526
- }
527
- const supportedTargets = (() => {
528
- const set = new Set(skills.flatMap((s) => skillSupportedChannels(packageRoot, s)));
529
- set.add("cursor");
530
- return ["claude-code", "codex", "opencode", "cursor"].filter((t) => set.has(t));
531
- })();
532
- const channelLabel = {
533
- "claude-code": "Claude Code",
534
- codex: "Codex",
535
- opencode: "OpenCode",
536
- cursor: "Cursor"
537
- };
538
- const channelDir = {
539
- "claude-code": ".claude",
540
- codex: ".codex",
541
- opencode: ".opencode",
542
- cursor: ".cursor"
543
- };
544
- let targets;
545
- if (opts.agents) {
546
- targets = opts.agents;
547
- } else if (interactive) {
548
- const defaults = actuallyDetected.filter((t) => supportedTargets.includes(t));
549
- const targetResult = await p.multiselect({
550
- message: "Agent channels \xB7 \uC5B4\uB514\uC5D0 \uC124\uCE58\uD560\uAE4C\uC694? \xB7 space = \uD1A0\uAE00 \xB7 enter = \uD655\uC778",
551
- options: supportedTargets.map((t) => ({
552
- value: t,
553
- label: channelLabel[t],
554
- hint: actuallyDetected.includes(t) ? `${channelDir[t]}/ detected` : ""
555
- })),
556
- initialValues: defaults.length > 0 ? defaults : supportedTargets,
557
- required: true
558
- });
559
- if (p.isCancel(targetResult)) {
560
- p.cancel("Install cancelled.");
561
- return 130;
562
- }
563
- targets = targetResult;
564
- } else {
565
- targets = opts.all ? ["claude-code", "codex", "opencode"] : minimal ? actuallyDetected.length > 0 ? actuallyDetected : ["claude-code"] : detected;
566
- const narrowed = targets.filter((t) => supportedTargets.includes(t));
567
- if (narrowed.length > 0) targets = narrowed;
568
- }
569
- const installRoot = scope === "global" ? homedir() : projectRoot;
570
- const skillChannelTargets = targets.filter(
571
- (t) => t !== "cursor"
572
- );
573
- const plans = skillChannelTargets.map((t) => planForTarget(installRoot, t));
574
- p.log.message(
575
- pc.bold("Scope: ") + pc.cyan(scope) + pc.dim(scope === "global" ? " (~/.claude)" : ` (${relative(process.cwd(), projectRoot) || "."})`)
576
- );
577
- p.log.message(
578
- pc.bold(`Skills (${skills.length}): `) + skills.map((s) => pc.cyan(s)).join(", ")
579
- );
580
- if (minimal) {
581
- canonicalAgents = [];
582
- p.log.message(pc.bold("Agents: ") + pc.dim("skipped (--skills-only)"));
583
- } else if (canonicalAgents.length > 0) {
584
- p.log.message(
585
- pc.bold(`Agents (${canonicalAgents.length}): `) + canonicalAgents.map((a) => pc.cyan(a.replace(/\.md$/, ""))).join(", ")
586
- );
587
- }
588
- p.log.message(
589
- pc.bold("Targets: ") + targets.map((t) => pc.cyan(t)).join(", ")
590
- );
591
- const results = [];
592
- let catalogCount = 0;
593
- for (const plan of plans) {
594
- for (const skill of skills) {
595
- results.push(installOne(packageRoot, plan, skill, force));
596
- }
597
- }
598
- for (const target of targets) {
599
- if (target === "claude-code") {
600
- for (const filename of canonicalAgents) {
601
- results.push(installAgentFile(packageRoot, installRoot, "claude", filename, force));
602
- }
603
- } else if (target === "codex") {
604
- for (const filename of canonicalAgents) {
605
- results.push(installAgentFile(packageRoot, installRoot, "codex", filename, force));
606
- }
607
- }
608
- }
609
- if (!minimal) {
610
- if (targets.includes("cursor")) {
611
- results.push(installCursorRule(installRoot, force));
612
- }
613
- const dataFiles = [
614
- "reference-fingerprints.json",
615
- "reference-tags.md",
616
- "vocabulary.json",
617
- "synonyms.json",
618
- "opt-out-corpus.json"
619
- ];
620
- for (const target of targets) {
621
- const dataDir = dataDirFor(target, targets);
622
- if (!dataDir) continue;
623
- for (const dataFile of dataFiles) {
624
- results.push(installDataFile(packageRoot, installRoot, dataDir, dataFile, force, target));
625
- }
626
- }
627
- for (const target of targets) {
628
- const dataDir = dataDirFor(target, targets);
629
- if (dataDir) {
630
- catalogCount += installReferenceCatalog(packageRoot, installRoot, dataDir, force);
631
- }
632
- }
633
- for (const target of targets) {
634
- const cd = CHANNEL_DATA_DIRS[target];
635
- if (!cd) continue;
636
- for (const helper of ["ctx-prime.cjs", "context.cjs"]) {
637
- const srcHelper = join2(packageRoot, "scripts", helper);
638
- if (!existsSync2(srcHelper)) continue;
639
- const destHelper = join2(installRoot, cd, "data", "scripts", helper);
640
- const srcTxt = readFileSync(srcHelper, "utf8");
641
- if (existsSync2(destHelper) && readFileSync(destHelper, "utf8") === srcTxt) continue;
642
- mkdirSync(dirname(destHelper), { recursive: true });
643
- writeFileSync(destHelper, srcTxt, "utf8");
644
- }
645
- }
646
- if (scope === "project" && targets.includes("claude-code")) {
647
- for (const hookFile of [
648
- "skill-activation.cjs",
649
- "session-state-loader.cjs",
650
- "post-edit-watch.cjs",
651
- "session-end-foldin.cjs",
652
- // Shared modules required by the fold-in / state-loader / watch hooks.
653
- // Live in a lib/ subdir; installHookFile preserves the relative path
654
- // under .claude/hooks/.
655
- join2("lib", "preferences-parser.cjs"),
656
- join2("lib", "preferences-writer.cjs")
657
- ]) {
658
- results.push(installHookFile(packageRoot, installRoot, hookFile, force));
659
- }
660
- results.push(installSettingsJson(packageRoot, installRoot, force));
661
- }
662
- }
663
- p.log.message(pc.bold("\nResults:"));
664
- for (const r of results) {
665
- const rel = relative(installRoot, r.destPath);
666
- p.log.message(
667
- ` ${STATUS_LABEL[r.status]} ${pc.dim(r.target.padEnd(12))} ${rel}`
668
- );
669
- }
670
- const driftCount = results.filter((r) => r.status === "skipped-drift").length;
671
- const writtenCount = results.filter(
672
- (r) => r.status === "created" || r.status === "updated"
673
- ).length;
674
- if (driftCount > 0) {
675
- p.outro(
676
- pc.yellow(
677
- `${writtenCount} written, ${driftCount} skipped (existing files lack the omd marker \u2014 rerun with --force to overwrite).`
678
- )
679
- );
680
- return 0;
681
- }
682
- if (minimal) {
683
- for (const r of results.filter((x) => x.status === "skipped-incompat")) {
684
- p.log.warn(
685
- `${pc.bold(r.skill)} ${pc.dim("skipped for ")}${pc.cyan(r.target)}${pc.dim(" \u2014 declares x-omd-channels (channel not supported).")}`
686
- );
687
- }
688
- const installed = results.filter(
689
- (r) => r.status === "created" || r.status === "updated"
690
- );
691
- if (installed.length === 0) {
692
- p.outro(pc.yellow("Nothing installed \u2014 no compatible skill/channel match."));
693
- return 0;
694
- }
695
- p.outro(
696
- pc.green(
697
- `Done. Installed ${skills.map((s) => pc.bold(s)).join(", ")} ${scope === "global" ? "globally (~/.claude/skills)" : `for ${targets.join(", ")}`}.`
698
- ) + pc.dim(" \u2192 restart your agent, then use the skill (e.g. ") + pc.cyan("/claude-design") + pc.dim(").")
699
- );
700
- return 0;
701
- }
702
- const nextSteps = [
703
- `${pc.bold("Restart your agent, then type your first prompt:")}`,
704
- "",
705
- ` ${pc.cyan("EN")} ${pc.dim("Set up our design system \u2014 Toss-style, for a family meal-tracking app.")}`,
706
- ` ${pc.cyan("KR")} ${pc.dim("\uD1A0\uC2A4 \uC2A4\uD0C0\uC77C\uB85C \uAC00\uC871 \uC2DD\uB2E8 \uACF5\uC720 \uC571 \uB514\uC790\uC778 \uC2DC\uC2A4\uD15C \uC7A1\uC544\uC918")}`,
707
- "",
708
- `${pc.dim("Your agent runs omd:init and writes DESIGN.md. Then build against it:")}`,
709
- ` ${pc.cyan("EN")} ${pc.dim("Design the home screen.")} ${pc.cyan("KR")} ${pc.dim("\uD648 \uD654\uBA74 \uB514\uC790\uC778\uD574\uC918")}`,
710
- "",
711
- `${pc.dim('Full walkthrough \u2192 "Your first 60 seconds" in the README. Routing is automatic \u2014 no slash command needed.')}`,
712
- `${pc.dim("Power user: ")}${pc.cyan("/omd-harness <task>")}${pc.dim(" \u2014 jump straight into the pipeline.")}`,
713
- "",
714
- `${pc.yellow("\u26A0 Already-running session?")} ${pc.dim("Run `/agents` to reload \u2014 or quit (Cmd+Q on macOS) and relaunch. Without reload, hooks/agents do not load.")}`
715
- ].join("\n");
716
- p.note(nextSteps, "Next");
717
- const hookCount = scope === "project" && targets.includes("claude-code") ? 4 : 0;
718
- if (catalogCount > 0) {
719
- p.log.message(
720
- pc.bold("Reference catalog: ") + pc.cyan(`${catalogCount}`) + pc.dim(" DESIGN.md copied \u2192 .claude/data/references/<id>/DESIGN.md")
721
- );
722
- }
723
- p.outro(
724
- pc.green(
725
- `Done. ${skills.length} skills \xB7 ${canonicalAgents.length} sub-agents \xB7 ${hookCount} hooks \xB7 ${catalogCount} catalog refs installed (${writtenCount} files)${scope === "global" ? " globally (~/.claude)" : ""}.`
726
- )
727
- );
728
- return 0;
729
- }
730
- export {
731
- dataDirFor,
732
- runInstallSkills
733
- };
734
- //# sourceMappingURL=install-skills-7UUDOLG2.js.map