oh-my-design-cli 1.8.6 → 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.
- package/.claude/hooks/lib/preferences-parser.cjs +1 -0
- package/.claude/hooks/lib/preferences-writer.cjs +1 -0
- package/.claude/hooks/post-edit-watch.cjs +1 -0
- package/.claude/hooks/session-end-foldin.cjs +1 -0
- package/.claude/hooks/session-state-loader.cjs +1 -0
- package/.claude/hooks/skill-activation.cjs +1 -0
- package/.claude/settings.json +10 -1
- package/.github/assets/logo-bg.png +0 -0
- package/AGENTS.md +20 -1
- package/CHANGELOG.md +482 -0
- package/MIGRATION.md +103 -0
- package/README.ja.md +56 -19
- package/README.ko.md +47 -30
- package/README.md +48 -30
- package/README.zh-TW.md +59 -22
- package/agents/AGENT.md +8 -7
- package/agents/omd-codex-image.md +1 -1
- package/agents/omd-critic.md +6 -7
- package/agents/omd-designer-review.md +1 -1
- package/agents/omd-final-qa.md +1 -1
- package/agents/omd-humanizer.md +38 -0
- package/agents/omd-kr-writer.md +3 -4
- package/agents/omd-locale-adapter.md +12 -16
- package/agents/omd-master.md +24 -19
- package/agents/omd-microcopy.md +1 -1
- package/agents/omd-orchestrator.md +7 -8
- package/agents/omd-slop-auditor.md +31 -0
- package/agents/omd-ux-researcher.md +1 -1
- package/data/reference-fingerprints.json +2761 -85
- package/dist/bin/oh-my-design.js +16 -8
- package/dist/bin/oh-my-design.js.map +1 -1
- package/dist/chunk-UJIHCEYF.js +99 -0
- package/dist/chunk-UJIHCEYF.js.map +1 -0
- package/dist/doctor-LDWSFM3I.js +763 -0
- package/dist/doctor-LDWSFM3I.js.map +1 -0
- package/dist/install-skills-ZZFNJ2II.js +1120 -0
- package/dist/install-skills-ZZFNJ2II.js.map +1 -0
- package/docs/CLI_QUICKSTART.md +157 -0
- package/package.json +25 -4
- package/scripts/postinstall.cjs +5 -4
- package/skills/claude-design/SKILL.md +38 -14
- package/skills/claude-design/references/claude-design-flow.md +11 -11
- package/skills/claude-design/references/codebase-analysis.md +5 -4
- package/skills/omd-final-qa/SKILL.md +1 -1
- package/skills/omd-harness/SKILL.md +51 -50
- package/skills/omd-humanize/SKILL.md +116 -0
- package/skills/omd-humanize/agents/openai.yaml +4 -0
- package/skills/omd-humanize/references/locale-playbooks.md +102 -0
- package/skills/omd-humanize/references/sources.md +11 -0
- package/skills/omd-init/SKILL.md +18 -12
- package/skills/omd-locale-adapter/SKILL.md +58 -91
- package/skills/omd-orchestrator/SKILL.md +11 -5
- package/skills/omd-reference-capture/SKILL.md +80 -26
- package/skills/omd-reference-capture/scripts/PLAYWRIGHT-LICENSE.txt +202 -0
- package/skills/omd-reference-capture/scripts/PLAYWRIGHT-NOTICE.txt +5 -0
- package/skills/omd-reference-capture/scripts/THIRD_PARTY_NOTICES.md +3 -0
- package/skills/omd-reference-capture/scripts/capture-reference-evidence.mjs +159906 -0
- package/skills/omd-slop-audit/SKILL.md +110 -0
- package/skills/omd-slop-audit/agents/openai.yaml +4 -0
- package/skills/omd-slop-audit/references/pattern-catalog.md +96 -0
- package/skills/omd-slop-audit/references/sources.md +24 -0
- package/web/references/104/DESIGN.md +354 -0
- package/web/references/11st/DESIGN.md +197 -316
- package/web/references/29cm/DESIGN.md +297 -393
- package/web/references/42dot/DESIGN.md +390 -0
- package/web/references/8percent/DESIGN.md +438 -0
- package/web/references/ably/DESIGN.md +267 -572
- package/web/references/acer/DESIGN.md +329 -0
- package/web/references/airbnb/DESIGN.md +219 -557
- package/web/references/airbridge/DESIGN.md +451 -0
- package/web/references/alipay/DESIGN.md +179 -420
- package/web/references/apple/DESIGN.md +224 -498
- package/web/references/asleep/DESIGN.md +402 -0
- package/web/references/au/DESIGN.md +334 -0
- package/web/references/autopedia/DESIGN.md +382 -0
- package/web/references/aws-cloudscape/DESIGN.md +241 -0
- package/web/references/baemin/DESIGN.md +332 -491
- package/web/references/banksalad/DESIGN.md +269 -180
- package/web/references/barogo/DESIGN.md +416 -0
- package/web/references/beusable/DESIGN.md +464 -0
- package/web/references/bigin/DESIGN.md +454 -0
- package/web/references/bilibili/DESIGN.md +206 -374
- package/web/references/bmw/DESIGN.md +162 -276
- package/web/references/brandi/DESIGN.md +184 -325
- package/web/references/buzzvil/DESIGN.md +457 -0
- package/web/references/cafe24/DESIGN.md +472 -0
- package/web/references/catchtable/DESIGN.md +219 -264
- package/web/references/cgv/DESIGN.md +317 -0
- package/web/references/channeltalk/DESIGN.md +210 -336
- package/web/references/china-airlines/DESIGN.md +291 -0
- package/web/references/cjonstyle/DESIGN.md +453 -0
- package/web/references/class101/DESIGN.md +168 -387
- package/web/references/classum/DESIGN.md +228 -203
- package/web/references/claude/DESIGN.md +242 -479
- package/web/references/clay/DESIGN.md +179 -377
- package/web/references/clickhouse/DESIGN.md +254 -343
- package/web/references/codeit/DESIGN.md +470 -0
- package/web/references/cohere/DESIGN.md +214 -324
- package/web/references/coinone/DESIGN.md +199 -179
- package/web/references/corca/DESIGN.md +414 -0
- package/web/references/coupang/DESIGN.md +205 -475
- package/web/references/cursor/DESIGN.md +240 -381
- package/web/references/dabang/DESIGN.md +248 -213
- package/web/references/dable/DESIGN.md +431 -0
- package/web/references/datarize/DESIGN.md +338 -0
- package/web/references/dealicious/DESIGN.md +402 -0
- package/web/references/drdiary/DESIGN.md +404 -0
- package/web/references/elastic/DESIGN.md +238 -0
- package/web/references/elevenlabs/DESIGN.md +183 -321
- package/web/references/elice/DESIGN.md +445 -0
- package/web/references/eslite/DESIGN.md +257 -0
- package/web/references/evaair/DESIGN.md +338 -0
- package/web/references/expo/DESIGN.md +238 -331
- package/web/references/familymart-tw/DESIGN.md +254 -0
- package/web/references/ferrari/DESIGN.md +160 -377
- package/web/references/figma/DESIGN.md +197 -451
- package/web/references/fitpet/DESIGN.md +445 -0
- package/web/references/flex/DESIGN.md +170 -253
- package/web/references/framer/DESIGN.md +197 -340
- package/web/references/freee/DESIGN.md +211 -533
- package/web/references/friendliai/DESIGN.md +431 -0
- package/web/references/frip/DESIGN.md +440 -0
- package/web/references/furiosaai/DESIGN.md +450 -0
- package/web/references/gangnamunni/DESIGN.md +154 -591
- package/web/references/gaudiolab/DESIGN.md +414 -0
- package/web/references/google/DESIGN.md +290 -398
- package/web/references/goorm/DESIGN.md +470 -0
- package/web/references/greencar/DESIGN.md +420 -0
- package/web/references/hackle/DESIGN.md +472 -0
- package/web/references/hashicorp/DESIGN.md +273 -318
- package/web/references/heydealer/DESIGN.md +429 -0
- package/web/references/humanscape/DESIGN.md +433 -0
- package/web/references/hwahae/DESIGN.md +453 -0
- package/web/references/hyundai/DESIGN.md +158 -368
- package/web/references/hyundaicard/DESIGN.md +171 -121
- package/web/references/ibm/DESIGN.md +227 -378
- package/web/references/idus/DESIGN.md +450 -0
- package/web/references/igaworks/DESIGN.md +437 -0
- package/web/references/inflearn/DESIGN.md +221 -366
- package/web/references/intuit/DESIGN.md +244 -0
- package/web/references/jandi/DESIGN.md +191 -291
- package/web/references/jobplanet/DESIGN.md +415 -0
- package/web/references/kakao/DESIGN.md +287 -475
- package/web/references/kakaobank/DESIGN.md +220 -521
- package/web/references/kakaogames/DESIGN.md +220 -0
- package/web/references/kakaopay/DESIGN.md +207 -526
- package/web/references/karrot/DESIGN.md +227 -251
- package/web/references/kb-kookmin/DESIGN.md +308 -0
- package/web/references/kbank/DESIGN.md +220 -175
- package/web/references/kcd/DESIGN.md +432 -0
- package/web/references/kmong/DESIGN.md +281 -356
- package/web/references/krds/DESIGN.md +245 -19
- package/web/references/kream/DESIGN.md +224 -359
- package/web/references/kurly/DESIGN.md +211 -533
- package/web/references/kyobobook/DESIGN.md +445 -0
- package/web/references/lablup/DESIGN.md +474 -0
- package/web/references/lamborghini/DESIGN.md +198 -360
- package/web/references/lemonbase/DESIGN.md +452 -0
- package/web/references/lguplus/DESIGN.md +259 -0
- package/web/references/likelion/DESIGN.md +283 -0
- package/web/references/line/DESIGN.md +396 -355
- package/web/references/linear.app/DESIGN.md +209 -562
- package/web/references/lotteon/DESIGN.md +386 -0
- package/web/references/lovable/DESIGN.md +255 -349
- package/web/references/makinarocks/DESIGN.md +242 -0
- package/web/references/maum-ai/DESIGN.md +423 -0
- package/web/references/medibloc/DESIGN.md +425 -0
- package/web/references/megabox/DESIGN.md +232 -0
- package/web/references/mercari/DESIGN.md +217 -434
- package/web/references/microsoft/DESIGN.md +234 -388
- package/web/references/mildang/DESIGN.md +411 -0
- package/web/references/millie/DESIGN.md +183 -504
- package/web/references/minimax/DESIGN.md +264 -322
- package/web/references/miricanvas/DESIGN.md +182 -355
- package/web/references/miro/DESIGN.md +234 -232
- package/web/references/mistral.ai/DESIGN.md +231 -314
- package/web/references/mixi/DESIGN.md +234 -0
- package/web/references/moin/DESIGN.md +445 -0
- package/web/references/mongodb/DESIGN.md +219 -316
- package/web/references/moreh/DESIGN.md +437 -0
- package/web/references/musinsa/DESIGN.md +184 -510
- package/web/references/mynavi/DESIGN.md +242 -0
- package/web/references/myrealtrip/DESIGN.md +169 -406
- package/web/references/naver/DESIGN.md +328 -499
- package/web/references/naverwebtoon/DESIGN.md +172 -395
- package/web/references/neosapience/DESIGN.md +441 -0
- package/web/references/nhn/DESIGN.md +169 -346
- package/web/references/nhncloud/DESIGN.md +185 -123
- package/web/references/nota/DESIGN.md +451 -0
- package/web/references/notion/DESIGN.md +271 -475
- package/web/references/nrise/DESIGN.md +417 -0
- package/web/references/ohouse/DESIGN.md +210 -549
- package/web/references/oliveyoung/DESIGN.md +217 -326
- package/web/references/ollama/DESIGN.md +241 -308
- package/web/references/onestore/DESIGN.md +248 -0
- package/web/references/palantir/DESIGN.md +256 -0
- package/web/references/panasonic/DESIGN.md +276 -0
- package/web/references/patternfly/DESIGN.md +259 -0
- package/web/references/payhere/DESIGN.md +438 -0
- package/web/references/pega/DESIGN.md +262 -0
- package/web/references/peoplefund/DESIGN.md +431 -0
- package/web/references/petfriends/DESIGN.md +428 -0
- package/web/references/pinkfong/DESIGN.md +199 -367
- package/web/references/pinterest/DESIGN.md +258 -308
- package/web/references/portone/DESIGN.md +446 -0
- package/web/references/poya/DESIGN.md +286 -0
- package/web/references/pozalabs/DESIGN.md +438 -0
- package/web/references/protopie/DESIGN.md +419 -0
- package/web/references/qraft/DESIGN.md +448 -0
- package/web/references/queenit/DESIGN.md +432 -0
- package/web/references/quotabook/DESIGN.md +449 -0
- package/web/references/rebellions/DESIGN.md +449 -0
- package/web/references/recruit/DESIGN.md +247 -0
- package/web/references/remember/DESIGN.md +202 -396
- package/web/references/resend/DESIGN.md +274 -348
- package/web/references/returnzero/DESIGN.md +460 -0
- package/web/references/sakura-internet/DESIGN.md +269 -0
- package/web/references/samsung/DESIGN.md +157 -359
- package/web/references/sandoll/DESIGN.md +445 -0
- package/web/references/sanity/DESIGN.md +241 -375
- package/web/references/saramin/DESIGN.md +465 -0
- package/web/references/scatterlab/DESIGN.md +412 -0
- package/web/references/selectstar/DESIGN.md +439 -0
- package/web/references/servicenow/DESIGN.md +289 -0
- package/web/references/shiftee/DESIGN.md +468 -0
- package/web/references/shinhanbank/DESIGN.md +161 -341
- package/web/references/shinhancard/DESIGN.md +208 -319
- package/web/references/sionic/DESIGN.md +454 -0
- package/web/references/sktelecom/DESIGN.md +310 -0
- package/web/references/socar/DESIGN.md +125 -32
- package/web/references/softbank/DESIGN.md +231 -0
- package/web/references/solapi/DESIGN.md +483 -0
- package/web/references/sony/DESIGN.md +234 -0
- package/web/references/soop/DESIGN.md +261 -0
- package/web/references/spacex/DESIGN.md +184 -323
- package/web/references/spotify/DESIGN.md +253 -394
- package/web/references/squeezebits/DESIGN.md +441 -0
- package/web/references/stayfolio/DESIGN.md +422 -0
- package/web/references/stripe/DESIGN.md +212 -491
- package/web/references/supabase/DESIGN.md +244 -316
- package/web/references/supertone/DESIGN.md +413 -0
- package/web/references/tabling/DESIGN.md +415 -0
- package/web/references/taishinbank/DESIGN.md +243 -0
- package/web/references/teamlab/DESIGN.md +240 -0
- package/web/references/teamsparta/DESIGN.md +472 -0
- package/web/references/tellingme/DESIGN.md +392 -0
- package/web/references/tesla/DESIGN.md +244 -427
- package/web/references/thsr/DESIGN.md +284 -0
- package/web/references/thumbtack/DESIGN.md +302 -0
- package/web/references/together.ai/DESIGN.md +257 -304
- package/web/references/toss/DESIGN.md +251 -593
- package/web/references/toss-securities/DESIGN.md +255 -178
- package/web/references/tossbank/DESIGN.md +195 -491
- package/web/references/toyota/DESIGN.md +238 -0
- package/web/references/tving/DESIGN.md +197 -245
- package/web/references/typed/DESIGN.md +413 -0
- package/web/references/uber/DESIGN.md +256 -333
- package/web/references/udn/DESIGN.md +250 -0
- package/web/references/upbit/DESIGN.md +145 -256
- package/web/references/upstage/DESIGN.md +211 -204
- package/web/references/uswds/DESIGN.md +287 -0
- package/web/references/vercel/DESIGN.md +236 -440
- package/web/references/vuno/DESIGN.md +413 -0
- package/web/references/wanted/DESIGN.md +215 -487
- package/web/references/weverse/DESIGN.md +437 -0
- package/web/references/wise/DESIGN.md +223 -231
- package/web/references/wisetracker/DESIGN.md +462 -0
- package/web/references/wooribank/DESIGN.md +245 -0
- package/web/references/yanolja/DESIGN.md +250 -419
- package/web/references/yeogiotte/DESIGN.md +235 -418
- package/web/references/zapier/DESIGN.md +256 -377
- package/web/references/zendesk/DESIGN.md +288 -0
- package/web/references/zigzag/DESIGN.md +200 -621
- package/dist/install-skills-7UUDOLG2.js +0 -734
- 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
|