oh-my-design-cli 1.7.0 → 1.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/hooks/lib/preferences-parser.cjs +82 -0
- package/.claude/hooks/post-edit-watch.cjs +15 -7
- package/.claude/hooks/session-end-foldin.cjs +11 -14
- package/.claude/hooks/session-state-loader.cjs +3 -2
- package/.claude/hooks/skill-activation.cjs +40 -7
- package/README.ja.md +32 -103
- package/README.ko.md +46 -226
- package/README.md +33 -151
- package/README.zh-TW.md +32 -103
- package/agents/omd-master.md +7 -1
- package/dist/bin/oh-my-design.js +3 -3
- package/dist/bin/oh-my-design.js.map +1 -1
- package/dist/{install-skills-YYHEC4CS.js → install-skills-KDW74C5K.js} +140 -12
- package/dist/install-skills-KDW74C5K.js.map +1 -0
- package/package.json +2 -1
- package/skills/omd-harness/SKILL.md +22 -8
- package/skills/omd-init/SKILL.md +51 -1
- package/skills/omd-reference-capture/SKILL.md +10 -3
- package/dist/install-skills-YYHEC4CS.js.map +0 -1
package/README.zh-TW.md
CHANGED
|
@@ -5,19 +5,16 @@
|
|
|
5
5
|
<h1 align="center">oh-my-design</h1>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<strong
|
|
9
|
-
</p>
|
|
10
|
-
|
|
11
|
-
<p align="center">
|
|
12
|
-
<strong>新增 OmD v0.1 Philosophy Layer。</strong>Voice・Narrative・Principles・Personas・States・Motion — 讓 Claude Code 輸出你的品牌,而不是 AI 的預設值。
|
|
8
|
+
<strong>為 AI 編碼代理打造的技能驅動設計 — 一個指令完成引導。</strong>221 個真實企業設計系統。安裝過程零 AI 呼叫。之後只要和你的代理對話即可。
|
|
13
9
|
</p>
|
|
14
10
|
|
|
15
11
|
<p align="center">
|
|
12
|
+
<a href="https://www.npmjs.com/package/oh-my-design-cli"><img src="https://img.shields.io/npm/v/oh-my-design-cli?style=flat-square&color=cb3837" alt="npm version" /></a>
|
|
13
|
+
<a href="https://www.npmjs.com/package/oh-my-design-cli"><img src="https://img.shields.io/npm/dm/oh-my-design-cli?style=flat-square&color=cb3837" alt="npm downloads" /></a>
|
|
16
14
|
<a href="LICENSE"><img src="https://img.shields.io/github/license/kwakseongjae/oh-my-design?style=flat-square" alt="License" /></a>
|
|
17
15
|
<a href="https://github.com/kwakseongjae/oh-my-design/stargazers"><img src="https://img.shields.io/github/stars/kwakseongjae/oh-my-design?style=social" alt="GitHub Stars" /></a>
|
|
18
|
-
<img src="https://img.shields.io/badge/
|
|
19
|
-
<img src="https://img.shields.io/badge/
|
|
20
|
-
<img src="https://img.shields.io/badge/references-107-7c5cfc?style=flat-square" alt="107 References" />
|
|
16
|
+
<img src="https://img.shields.io/badge/references-221-7c5cfc?style=flat-square" alt="221 References" />
|
|
17
|
+
<img src="https://img.shields.io/badge/CLI%20commands-1-blue?style=flat-square" alt="One CLI command" />
|
|
21
18
|
</p>
|
|
22
19
|
|
|
23
20
|
<p align="center">
|
|
@@ -28,118 +25,50 @@
|
|
|
28
25
|
|
|
29
26
|
## 什麼是 oh-my-design?
|
|
30
27
|
|
|
31
|
-
**oh-my-design (OmD)**
|
|
32
|
-
|
|
33
|
-
[Google 提出的](https://stitch.withgoogle.com/docs/design-md/overview/) `DESIGN.md` 本質上是**令牌文件** — 色彩、字體、元件。必要,但不夠。只靠令牌產 UI,形狀看似合理,卻「不像任何品牌」,會收斂到 AI 的預設:Inter-on-white、紫色漸層、毫無緣由的 emoji。OmD v0.1 在其上疊加**品牌哲學層**:**Voice・Narrative・Principles・Personas・States・Motion**。將 OmD 格式的 `DESIGN.md` 放在專案根目錄,代理的輸出就不再通用,而是「你的」。
|
|
34
|
-
|
|
35
|
-
三個組成部分:
|
|
36
|
-
|
|
37
|
-
1. **[規範](spec/omd-v0.1.md)** — 版本化的 Google Stitch 擴充,MIT 授權。
|
|
38
|
-
2. **[Claude Code skill](.claude/skills/omd/SKILL.md)** — 將規範作為硬性約束自動套用。
|
|
39
|
-
3. **[107 個參考檔案](references/)** — 真實企業的 `DESIGN.md`,可以 fork、透過 builder 客製化後直接上線。
|
|
40
|
-
|
|
41
|
-
**無須 API 金鑰。零 AI 呼叫。全部在客戶端執行。**
|
|
42
|
-
|
|
43
|
-
## OmD v0.1 Philosophy Layer
|
|
44
|
-
|
|
45
|
-
OmD 在 Google Stitch 的 9 個章節之上再加的 6 個章節:
|
|
46
|
-
|
|
47
|
-
| 章節 | 用途 |
|
|
48
|
-
|---|---|
|
|
49
|
-
| **10. Voice & Tone** | 微文案約束 — 按鈕文字、錯誤訊息、導引流程 |
|
|
50
|
-
| **11. Brand Narrative** | 「為什麼」 — 品牌拒絕什麼、試圖撼動哪個類別 |
|
|
51
|
-
| **12. Principles** | 令牌無法裁決時用來拍板的 5〜10 條第一性原理 |
|
|
52
|
-
| **13. Personas** | 2〜4 位具體使用者,讓代理輸出扎根於實際情境 |
|
|
53
|
-
| **14. States** | Empty / loading / error / skeleton 模式 — 避免通用的「無資料」 |
|
|
54
|
-
| **15. Motion & Easing** | 命名的 duration + easing 令牌 — Stitch 9 章節遺漏的維度 |
|
|
55
|
-
|
|
56
|
-
**目前已有 10 個參考附帶完整的 Philosophy Layer:**
|
|
57
|
-
Toss · Claude · Line · Stripe · Linear · Vercel · Notion · Airbnb · Apple · Figma — 每個都包含 voice、narrative、principles、personas、states、motion,全部基於公開來源撰寫。
|
|
28
|
+
**oh-my-design (OmD)** 是為 AI 編碼代理打造的設計系統。它讓 Claude Code / Codex / OpenCode / Cursor 變成一位記得你品牌的資深產品設計師。安裝一次之後,只要描述你想要的東西 — 元件、畫面、文案、素材、圖表 — 代理就會套用專案的設計系統並交付成果。`DESIGN.md` 是品牌規格([Google Stitch](https://stitch.withgoogle.com/docs/design-md/overview/) 令牌 + 品牌哲學層: Voice / Narrative / Principles / Personas / States / Motion),套件內建 221 個真實企業的 DESIGN.md。**無須 API 金鑰。無須外部基礎設施。一切都在你既有的 CLI 工作階段內執行。**
|
|
58
29
|
|
|
59
|
-
|
|
30
|
+
## 安裝
|
|
60
31
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
- **Design Systems 目錄** ([oh-my-design.kr/design-systems](https://oh-my-design.kr/design-systems)) — 107 個參考中有 34 個擁有官方設計系統或品牌指南頁面,可從目錄配合即時縮圖直接前往。
|
|
65
|
-
- **Personal Curation** ([oh-my-design.kr/curation](https://oh-my-design.kr/curation)) — 透過 MBTI 風格的簡短測驗,將你的設計偏好對應到 107 個參考之一,並直接帶你進入已預選該參考的 Builder。
|
|
66
|
-
|
|
67
|
-
## 107 個支援的參考
|
|
68
|
-
|
|
69
|
-
| 類別 | 企業 |
|
|
70
|
-
|------|------|
|
|
71
|
-
| **AI & LLM** | Claude, Cohere, ElevenLabs, Minimax, Mistral AI, Ollama, OpenCode AI, Replicate, RunwayML, Together AI, VoltAgent, xAI |
|
|
72
|
-
| **設計工具** | Airtable, Clay, Figma, Framer, Miro, Webflow |
|
|
73
|
-
| **開發者工具** | Cursor, Expo, Lovable, Raycast, Superhuman, Vercel, Warp |
|
|
74
|
-
| **生產力** | Cal.com, freee, Intercom, Linear, Mintlify, Notion, Resend, Zapier |
|
|
75
|
-
| **消費科技** | Airbnb, Apple, Baemin, Dcard, IBM, Kakao, Karrot, LINE, Mercari, NVIDIA, Pinkoi, Pinterest, SpaceX, Spotify, Uber |
|
|
76
|
-
| **金融科技** | Coinbase, Kraken, Revolut, Stripe, Toss, Wise |
|
|
77
|
-
| **後端 & DevOps** | ClickHouse, Composio, Hashicorp, MongoDB, PostHog, Sanity, Sentry, Supabase |
|
|
78
|
-
| **汽車** | BMW, Ferrari, Lamborghini, Renault, Tesla |
|
|
79
|
-
| **行銷** | Semrush |
|
|
80
|
-
|
|
81
|
-
> 使用 Builder 中的**國家篩選器**按地區瀏覽 (韓國、台灣、日本、法國、義大利、德國、英國、美國)。
|
|
82
|
-
|
|
83
|
-
## 匯出的 DESIGN.md
|
|
84
|
-
|
|
85
|
-
以 [Google Stitch DESIGN.md 格式](https://stitch.withgoogle.com/docs/design-md/overview/)為基礎 — 1〜9 章節,加上選用的 OmD v0.1 Philosophy Layer(10〜15 章節):
|
|
32
|
+
```bash
|
|
33
|
+
npx oh-my-design-cli install-skills
|
|
34
|
+
```
|
|
86
35
|
|
|
87
|
-
|
|
88
|
-
1. Visual Theme & Atmosphere
|
|
89
|
-
2. Color Palette & Roles
|
|
90
|
-
3. Typography Rules
|
|
91
|
-
4. Component Stylings
|
|
92
|
-
5. Layout Principles
|
|
93
|
-
6. Depth & Elevation
|
|
94
|
-
7. Do's and Don'ts
|
|
95
|
-
8. Responsive Behavior
|
|
96
|
-
9. Agent Prompt Guide
|
|
36
|
+
安裝後請重新啟動你的代理 (Claude Code 為 Cmd+Q 後重新開啟) — 新的 skills + agents 才會載入。
|
|
97
37
|
|
|
98
|
-
|
|
38
|
+
這是你唯一需要執行的 CLI 指令。其餘一切都是對代理說的自然語言。
|
|
99
39
|
|
|
100
|
-
|
|
101
|
-
11. Brand Narrative
|
|
102
|
-
12. Principles
|
|
103
|
-
13. Personas
|
|
104
|
-
14. States
|
|
105
|
-
15. Motion & Easing
|
|
40
|
+
## 支援的代理
|
|
106
41
|
|
|
107
|
-
|
|
42
|
+
| 代理 | 通道 | 安裝內容 |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| **Claude Code** | `--agent claude-code` (預設) | 完整套件 — `.claude/` 下的 skills、16 個子代理、hooks、data |
|
|
45
|
+
| **Codex** | `--agent codex` | `.agents/skills/` 技能套件 (官方 discovery 路徑) |
|
|
46
|
+
| **OpenCode** | `--agent opencode` | `.opencode/skills/` 技能套件 |
|
|
47
|
+
| **Cursor** | `--agent cursor` | 正式 rules 通道 — `.cursor/rules/omd-design.mdc` shim + 共用 `.claude/data` 目錄 (不含 skills/hooks) |
|
|
108
48
|
|
|
109
|
-
|
|
49
|
+
預設會安裝到所有偵測到的代理; 只要單一通道請加 `--agent <name>`。
|
|
110
50
|
|
|
111
|
-
|
|
112
|
-
oh-my-design/
|
|
113
|
-
spec/ OmD v0.1 規範 (正本)
|
|
114
|
-
.claude/skills/omd/ Claude Code skill 包
|
|
115
|
-
references/ 107 家企業的 DESIGN.md 檔案
|
|
116
|
-
src/ CLI 核心 (TypeScript)
|
|
117
|
-
web/ Next.js 網頁 builder
|
|
118
|
-
src/app/ Landing + Builder + Directory 頁面
|
|
119
|
-
src/components/ Wizard、Preview、Export
|
|
120
|
-
test/ CLI Vitest 套件 (unit/、integration/、scripts/)
|
|
121
|
-
```
|
|
51
|
+
## 套件內容
|
|
122
52
|
|
|
123
|
-
|
|
53
|
+
**16 個 skills · 16 個子代理 · 221 個經驗證的參考 · 活性化 hooks** — 上述一個指令全部安裝完成。
|
|
124
54
|
|
|
125
|
-
|
|
55
|
+
每個參考也以 raw markdown 形式提供於 `oh-my-design.kr/design-systems/<id>.md`,代理可以直接抓取。完整的 skill 與 agent 參考文件: **[oh-my-design.kr/docs](https://oh-my-design.kr/docs)**。
|
|
126
56
|
|
|
127
|
-
|
|
57
|
+
## 升級
|
|
128
58
|
|
|
129
59
|
```bash
|
|
130
|
-
|
|
131
|
-
cd web && npm test # Web:107 個測試 — generate-css、config-hash、survey
|
|
60
|
+
npx oh-my-design-cli@latest install-skills
|
|
132
61
|
```
|
|
133
62
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
## 致謝
|
|
63
|
+
Idempotent。帶有 `<!-- omd:installed-skill -->` 標記的受管檔案會就地更新,使用者編輯過的檔案則保持不動 (要覆寫請加 `--force`)。重新執行後請重新啟動代理。
|
|
137
64
|
|
|
138
|
-
|
|
139
|
-
- [kzhrknt/awesome-design-md-jp](https://github.com/kzhrknt/awesome-design-md-jp) — 日本市場的設計系統參考。
|
|
65
|
+
## 連結
|
|
140
66
|
|
|
141
|
-
oh-my-design
|
|
67
|
+
- **目錄** — [oh-my-design.kr/design-systems](https://oh-my-design.kr/design-systems)
|
|
68
|
+
- **精選集** — [oh-my-design.kr/collections](https://oh-my-design.kr/collections)
|
|
69
|
+
- **文件** — [oh-my-design.kr/docs](https://oh-my-design.kr/docs)
|
|
70
|
+
- **更新紀錄** — [CHANGELOG.md](CHANGELOG.md)
|
|
142
71
|
|
|
143
72
|
## 授權
|
|
144
73
|
|
|
145
|
-
[MIT](LICENSE)
|
|
74
|
+
[MIT](LICENSE) — 參考資料屬於各企業所有,僅為教育性參考而重現。
|
package/agents/omd-master.md
CHANGED
|
@@ -168,7 +168,13 @@ Each turn you are in one state. Determine current state from `.handoff.json` `st
|
|
|
168
168
|
```
|
|
169
169
|
(Edit 툴로 첫 줄 prepend.)
|
|
170
170
|
|
|
171
|
-
**Step 4.2 — Reference DESIGN.md Read.**
|
|
171
|
+
**Step 4.2 — Reference DESIGN.md Read.** chosen ref의 DESIGN.md를 다음 우선순위로 resolve (omd:init Phase 4와 동일):
|
|
172
|
+
1. `.claude/data/references/<chosen_ref_id>/DESIGN.md` (installer가 복사 — npx 설치 기본 경로)
|
|
173
|
+
2. `node_modules/oh-my-design-cli/web/references/<chosen_ref_id>/DESIGN.md` (로컬 npm 설치)
|
|
174
|
+
3. `web/references/<chosen_ref_id>/DESIGN.md` (개발 레포)
|
|
175
|
+
4. `https://oh-my-design.kr/design-systems/<chosen_ref_id>.md` 를 fetch (WebFetch 또는 `curl -fsSL`) — 200이면 본문이 곧 reference DESIGN.md. 가져온 내용을 `.claude/data/references/<chosen_ref_id>/DESIGN.md`로 캐시.
|
|
176
|
+
|
|
177
|
+
4개 경로 전부 miss면 **DESIGN.md를 임의로 지어내지 말 것** — 사용자에게 reference 자료 누락을 알리고 종료. 카탈로그 안의 모든 ref에 DESIGN.md 있음.
|
|
172
178
|
|
|
173
179
|
**Step 4.3 — delta axes 추론.** 사용자 description + chosen ref base를 비교해서 다음 axes 중 사용자가 명시했거나 함의한 것만 shift 대상으로 표시:
|
|
174
180
|
- `hue_deg` (색상 각도, 예: +30 = warmer rotation)
|
package/dist/bin/oh-my-design.js
CHANGED
|
@@ -24,10 +24,10 @@ function readPackageVersion() {
|
|
|
24
24
|
}
|
|
25
25
|
var program = new Command();
|
|
26
26
|
program.name("oh-my-design").description("Bootstrap oh-my-design skills + agents into your project. After install, talk to your agent in natural language \u2014 no other CLI commands.").version(readPackageVersion()).showSuggestionAfterError(true).showHelpAfterError(true);
|
|
27
|
-
program.command("install-skills").description("Install omd skill files + canonical agents into agent directories (.claude/, .codex/, .opencode/). Interactive multiselect TUI by default \u2014 picks which skills + sub-agents to install.").option("--dir <path>", "Project root (defaults to cwd)").option("--agent <name...>", "Restrict to specific channels (claude-code | codex | opencode)").option("--force", "Overwrite existing files even without the omd marker").option("--all", "Skip the interactive TUI and install every shipped skill + agent (use in CI)").option("--skills <names>", "Comma-separated skill names to install (overrides TUI)", (v) => v.split(",").map((s) => s.trim()).filter(Boolean)).option("--agents-only <names>", "Comma-separated agent names to install (overrides TUI). Use --agents-only to disambiguate from --agent (channel selector).", (v) => v.split(",").map((s) => s.trim()).filter(Boolean)).option("--skills-only", "Install only the named skill files \u2014 skip sub-agents, hooks, and settings.json (minimal single-skill install, e.g. --skills claude-design --skills-only)").option("--global", "Install to the user-level dir (~/.claude/skills, available in every project) instead of this project. Writes skills + sub-agents; never touches global hooks/settings.").action(
|
|
27
|
+
program.command("install-skills").description("Install omd skill files + canonical agents into agent directories (.claude/, .codex/, .opencode/). Interactive multiselect TUI by default \u2014 picks which skills + sub-agents to install.").option("--dir <path>", "Project root (defaults to cwd)").option("--agent <name...>", "Restrict to specific channels (claude-code | codex | opencode | cursor)").option("--force", "Overwrite existing files even without the omd marker").option("--all", "Skip the interactive TUI and install every shipped skill + agent (use in CI)").option("--skills <names>", "Comma-separated skill names to install (overrides TUI)", (v) => v.split(",").map((s) => s.trim()).filter(Boolean)).option("--agents-only <names>", "Comma-separated agent names to install (overrides TUI). Use --agents-only to disambiguate from --agent (channel selector).", (v) => v.split(",").map((s) => s.trim()).filter(Boolean)).option("--skills-only", "Install only the named skill files \u2014 skip sub-agents, hooks, and settings.json (minimal single-skill install, e.g. --skills claude-design --skills-only)").option("--global", "Install to the user-level dir (~/.claude/skills, available in every project) instead of this project. Writes skills + sub-agents; never touches global hooks/settings.").action(
|
|
28
28
|
async (opts) => {
|
|
29
|
-
const { runInstallSkills } = await import("../install-skills-
|
|
30
|
-
const validAgents = ["claude-code", "codex", "opencode"];
|
|
29
|
+
const { runInstallSkills } = await import("../install-skills-KDW74C5K.js");
|
|
30
|
+
const validAgents = ["claude-code", "codex", "opencode", "cursor"];
|
|
31
31
|
const agents = opts.agent ? opts.agent.filter(
|
|
32
32
|
(a) => validAgents.includes(a)
|
|
33
33
|
) : void 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../bin/oh-my-design.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { readFileSync, existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nfunction readPackageVersion(): string {\n let cur = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 8; i++) {\n const pkg = join(cur, 'package.json');\n if (existsSync(pkg)) {\n try {\n return JSON.parse(readFileSync(pkg, 'utf8')).version ?? '0.0.0';\n } catch {\n return '0.0.0';\n }\n }\n const parent = dirname(cur);\n if (parent === cur) break;\n cur = parent;\n }\n return '0.0.0';\n}\n\nconst program = new Command();\n\nprogram\n .name('oh-my-design')\n .description('Bootstrap oh-my-design skills + agents into your project. After install, talk to your agent in natural language — no other CLI commands.')\n .version(readPackageVersion())\n .showSuggestionAfterError(true)\n .showHelpAfterError(true);\n\nprogram\n .command('install-skills')\n .description('Install omd skill files + canonical agents into agent directories (.claude/, .codex/, .opencode/). Interactive multiselect TUI by default — picks which skills + sub-agents to install.')\n .option('--dir <path>', 'Project root (defaults to cwd)')\n .option('--agent <name...>', 'Restrict to specific channels (claude-code | codex | opencode)')\n .option('--force', 'Overwrite existing files even without the omd marker')\n .option('--all', 'Skip the interactive TUI and install every shipped skill + agent (use in CI)')\n .option('--skills <names>', 'Comma-separated skill names to install (overrides TUI)', (v) => v.split(',').map((s) => s.trim()).filter(Boolean))\n .option('--agents-only <names>', 'Comma-separated agent names to install (overrides TUI). Use --agents-only to disambiguate from --agent (channel selector).', (v) => v.split(',').map((s) => s.trim()).filter(Boolean))\n .option('--skills-only', 'Install only the named skill files — skip sub-agents, hooks, and settings.json (minimal single-skill install, e.g. --skills claude-design --skills-only)')\n .option('--global', 'Install to the user-level dir (~/.claude/skills, available in every project) instead of this project. Writes skills + sub-agents; never touches global hooks/settings.')\n .action(\n async (opts: {\n dir?: string;\n agent?: string[];\n force?: boolean;\n all?: boolean;\n skills?: string[];\n agentsOnly?: string[];\n skillsOnly?: boolean;\n global?: boolean;\n }) => {\n const { runInstallSkills } = await import('../src/cli/install-skills.js');\n const validAgents = ['claude-code', 'codex', 'opencode'] as const;\n type Agent = (typeof validAgents)[number];\n const agents = opts.agent\n ? (opts.agent.filter((a): a is Agent =>\n (validAgents as readonly string[]).includes(a)\n ) as Agent[])\n : undefined;\n const code = await runInstallSkills({\n dir: opts.dir,\n agents,\n force: opts.force,\n all: opts.all,\n skillsFilter: opts.skills,\n agentsFilter: opts.agentsOnly,\n skillsOnly: opts.skillsOnly,\n global: opts.global,\n });\n if (code !== 0) process.exit(code);\n }\n );\n\nprogram.parse();\n"],"mappings":";;;AAAA,SAAS,eAAe;AACxB,SAAS,cAAc,kBAAkB;AACzC,SAAS,SAAS,YAAY;AAC9B,SAAS,qBAAqB;AAE9B,SAAS,qBAA6B;AACpC,MAAI,MAAM,QAAQ,cAAc,YAAY,GAAG,CAAC;AAChD,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,MAAM,KAAK,KAAK,cAAc;AACpC,QAAI,WAAW,GAAG,GAAG;AACnB,UAAI;AACF,eAAO,KAAK,MAAM,aAAa,KAAK,MAAM,CAAC,EAAE,WAAW;AAAA,MAC1D,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AACA,UAAM,SAAS,QAAQ,GAAG;AAC1B,QAAI,WAAW,IAAK;AACpB,UAAM;AAAA,EACR;AACA,SAAO;AACT;AAEA,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,cAAc,EACnB,YAAY,+IAA0I,EACtJ,QAAQ,mBAAmB,CAAC,EAC5B,yBAAyB,IAAI,EAC7B,mBAAmB,IAAI;AAE1B,QACG,QAAQ,gBAAgB,EACxB,YAAY,8LAAyL,EACrM,OAAO,gBAAgB,gCAAgC,EACvD,OAAO,qBAAqB,
|
|
1
|
+
{"version":3,"sources":["../../bin/oh-my-design.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { readFileSync, existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nfunction readPackageVersion(): string {\n let cur = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 8; i++) {\n const pkg = join(cur, 'package.json');\n if (existsSync(pkg)) {\n try {\n return JSON.parse(readFileSync(pkg, 'utf8')).version ?? '0.0.0';\n } catch {\n return '0.0.0';\n }\n }\n const parent = dirname(cur);\n if (parent === cur) break;\n cur = parent;\n }\n return '0.0.0';\n}\n\nconst program = new Command();\n\nprogram\n .name('oh-my-design')\n .description('Bootstrap oh-my-design skills + agents into your project. After install, talk to your agent in natural language — no other CLI commands.')\n .version(readPackageVersion())\n .showSuggestionAfterError(true)\n .showHelpAfterError(true);\n\nprogram\n .command('install-skills')\n .description('Install omd skill files + canonical agents into agent directories (.claude/, .codex/, .opencode/). Interactive multiselect TUI by default — picks which skills + sub-agents to install.')\n .option('--dir <path>', 'Project root (defaults to cwd)')\n .option('--agent <name...>', 'Restrict to specific channels (claude-code | codex | opencode | cursor)')\n .option('--force', 'Overwrite existing files even without the omd marker')\n .option('--all', 'Skip the interactive TUI and install every shipped skill + agent (use in CI)')\n .option('--skills <names>', 'Comma-separated skill names to install (overrides TUI)', (v) => v.split(',').map((s) => s.trim()).filter(Boolean))\n .option('--agents-only <names>', 'Comma-separated agent names to install (overrides TUI). Use --agents-only to disambiguate from --agent (channel selector).', (v) => v.split(',').map((s) => s.trim()).filter(Boolean))\n .option('--skills-only', 'Install only the named skill files — skip sub-agents, hooks, and settings.json (minimal single-skill install, e.g. --skills claude-design --skills-only)')\n .option('--global', 'Install to the user-level dir (~/.claude/skills, available in every project) instead of this project. Writes skills + sub-agents; never touches global hooks/settings.')\n .action(\n async (opts: {\n dir?: string;\n agent?: string[];\n force?: boolean;\n all?: boolean;\n skills?: string[];\n agentsOnly?: string[];\n skillsOnly?: boolean;\n global?: boolean;\n }) => {\n const { runInstallSkills } = await import('../src/cli/install-skills.js');\n const validAgents = ['claude-code', 'codex', 'opencode', 'cursor'] as const;\n type Agent = (typeof validAgents)[number];\n const agents = opts.agent\n ? (opts.agent.filter((a): a is Agent =>\n (validAgents as readonly string[]).includes(a)\n ) as Agent[])\n : undefined;\n const code = await runInstallSkills({\n dir: opts.dir,\n agents,\n force: opts.force,\n all: opts.all,\n skillsFilter: opts.skills,\n agentsFilter: opts.agentsOnly,\n skillsOnly: opts.skillsOnly,\n global: opts.global,\n });\n if (code !== 0) process.exit(code);\n }\n );\n\nprogram.parse();\n"],"mappings":";;;AAAA,SAAS,eAAe;AACxB,SAAS,cAAc,kBAAkB;AACzC,SAAS,SAAS,YAAY;AAC9B,SAAS,qBAAqB;AAE9B,SAAS,qBAA6B;AACpC,MAAI,MAAM,QAAQ,cAAc,YAAY,GAAG,CAAC;AAChD,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,MAAM,KAAK,KAAK,cAAc;AACpC,QAAI,WAAW,GAAG,GAAG;AACnB,UAAI;AACF,eAAO,KAAK,MAAM,aAAa,KAAK,MAAM,CAAC,EAAE,WAAW;AAAA,MAC1D,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AACA,UAAM,SAAS,QAAQ,GAAG;AAC1B,QAAI,WAAW,IAAK;AACpB,UAAM;AAAA,EACR;AACA,SAAO;AACT;AAEA,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,cAAc,EACnB,YAAY,+IAA0I,EACtJ,QAAQ,mBAAmB,CAAC,EAC5B,yBAAyB,IAAI,EAC7B,mBAAmB,IAAI;AAE1B,QACG,QAAQ,gBAAgB,EACxB,YAAY,8LAAyL,EACrM,OAAO,gBAAgB,gCAAgC,EACvD,OAAO,qBAAqB,yEAAyE,EACrG,OAAO,WAAW,sDAAsD,EACxE,OAAO,SAAS,8EAA8E,EAC9F,OAAO,oBAAoB,0DAA0D,CAAC,MAAM,EAAE,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO,CAAC,EAC7I,OAAO,yBAAyB,8HAA8H,CAAC,MAAM,EAAE,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO,CAAC,EACtN,OAAO,iBAAiB,+JAA0J,EAClL,OAAO,YAAY,wKAAwK,EAC3L;AAAA,EACC,OAAO,SASD;AACJ,UAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,+BAA8B;AACxE,UAAM,cAAc,CAAC,eAAe,SAAS,YAAY,QAAQ;AAEjE,UAAM,SAAS,KAAK,QACf,KAAK,MAAM;AAAA,MAAO,CAAC,MACjB,YAAkC,SAAS,CAAC;AAAA,IAC/C,IACA;AACJ,UAAM,OAAO,MAAM,iBAAiB;AAAA,MAClC,KAAK,KAAK;AAAA,MACV;AAAA,MACA,OAAO,KAAK;AAAA,MACZ,KAAK,KAAK;AAAA,MACV,cAAc,KAAK;AAAA,MACnB,cAAc,KAAK;AAAA,MACnB,YAAY,KAAK;AAAA,MACjB,QAAQ,KAAK;AAAA,IACf,CAAC;AACD,QAAI,SAAS,EAAG,SAAQ,KAAK,IAAI;AAAA,EACnC;AACF;AAEF,QAAQ,MAAM;","names":[]}
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from "fs";
|
|
14
14
|
import { join as join2, dirname, relative } from "path";
|
|
15
15
|
import { homedir } from "os";
|
|
16
|
+
import { createHash } from "crypto";
|
|
16
17
|
import { fileURLToPath } from "url";
|
|
17
18
|
|
|
18
19
|
// src/core/agent-detect.ts
|
|
@@ -157,6 +158,19 @@ function planForTarget(projectRoot, target) {
|
|
|
157
158
|
}
|
|
158
159
|
}
|
|
159
160
|
var MANAGED_HEADER = "<!-- omd:installed-skill \u2014 managed by `omd install-skills`. Do not edit; rerun the command to refresh. -->";
|
|
161
|
+
var MANAGED_MARKER_SUBSTR = "omd:installed-skill";
|
|
162
|
+
function withManagedMarker(src) {
|
|
163
|
+
const fm = /^(---\r?\n[\s\S]*?\r?\n---\r?\n)([\s\S]*)$/.exec(src);
|
|
164
|
+
if (!fm) {
|
|
165
|
+
return MANAGED_HEADER + "\n\n" + src;
|
|
166
|
+
}
|
|
167
|
+
return fm[1] + MANAGED_HEADER + "\n\n" + fm[2];
|
|
168
|
+
}
|
|
169
|
+
function isManagedSkillFile(content) {
|
|
170
|
+
if (!content) return false;
|
|
171
|
+
const head = content.split("\n", 30).join("\n");
|
|
172
|
+
return head.includes(MANAGED_MARKER_SUBSTR);
|
|
173
|
+
}
|
|
160
174
|
var IGNORED_SKILL_ENTRIES = /* @__PURE__ */ new Set([".runtime", "__pycache__", ".DS_Store"]);
|
|
161
175
|
function parseSkillChannels(skillMd) {
|
|
162
176
|
const fm = /^---\n([\s\S]*?)\n---/.exec(skillMd);
|
|
@@ -173,7 +187,7 @@ function skillSupportedChannels(packageRoot, skill) {
|
|
|
173
187
|
function installOne(packageRoot, plan, skill, force) {
|
|
174
188
|
const skillDir = join2(packageRoot, "skills", skill);
|
|
175
189
|
const src = readFileSync(join2(skillDir, "SKILL.md"), "utf8");
|
|
176
|
-
const managed =
|
|
190
|
+
const managed = withManagedMarker(src);
|
|
177
191
|
const channels = parseSkillChannels(src);
|
|
178
192
|
if (channels && !channels.includes(plan.target)) {
|
|
179
193
|
return {
|
|
@@ -201,7 +215,7 @@ function installOne(packageRoot, plan, skill, force) {
|
|
|
201
215
|
if (exists && existing === managed && !isMultiFile) {
|
|
202
216
|
return { target: plan.target, skill, destPath, status: "unchanged" };
|
|
203
217
|
}
|
|
204
|
-
if (exists && !existing
|
|
218
|
+
if (exists && !isManagedSkillFile(existing) && !force) {
|
|
205
219
|
return { target: plan.target, skill, destPath, status: "skipped-drift" };
|
|
206
220
|
}
|
|
207
221
|
mkdirSync(dirname(destPath), { recursive: true });
|
|
@@ -272,8 +286,7 @@ function installSettingsJson(packageRoot, projectRoot, force) {
|
|
|
272
286
|
writeFileSync(destPath, src);
|
|
273
287
|
return { target, skill: skillLabel, destPath, status: exists ? "updated" : "created" };
|
|
274
288
|
}
|
|
275
|
-
function installDataFile(packageRoot, projectRoot, channelDir, dataFilename, force) {
|
|
276
|
-
const target = channelDir === ".claude" ? "claude-code" : "codex";
|
|
289
|
+
function installDataFile(packageRoot, projectRoot, channelDir, dataFilename, force, target = channelDir === ".claude" ? "claude-code" : "codex") {
|
|
277
290
|
const skillLabel = `data:${dataFilename}`;
|
|
278
291
|
const srcPath = join2(packageRoot, "data", dataFilename);
|
|
279
292
|
const destPath = join2(projectRoot, channelDir, "data", dataFilename);
|
|
@@ -329,6 +342,75 @@ function installAgentFile(packageRoot, projectRoot, channel, filename, force) {
|
|
|
329
342
|
status: exists ? "updated" : "created"
|
|
330
343
|
};
|
|
331
344
|
}
|
|
345
|
+
function installReferenceCatalog(packageRoot, installRoot, channelDir, force) {
|
|
346
|
+
const srcRoot = join2(packageRoot, "web", "references");
|
|
347
|
+
if (!existsSync2(srcRoot)) return 0;
|
|
348
|
+
const destRoot = join2(installRoot, channelDir, "data", "references");
|
|
349
|
+
let written = 0;
|
|
350
|
+
for (const id of readdirSync(srcRoot)) {
|
|
351
|
+
const srcDesign = join2(srcRoot, id, "DESIGN.md");
|
|
352
|
+
if (!existsSync2(srcDesign)) continue;
|
|
353
|
+
const destDesign = join2(destRoot, id, "DESIGN.md");
|
|
354
|
+
const src = readFileSync(srcDesign, "utf8");
|
|
355
|
+
if (existsSync2(destDesign)) {
|
|
356
|
+
const existing = readFileSync(destDesign, "utf8");
|
|
357
|
+
if (existing === src) continue;
|
|
358
|
+
if (!force) continue;
|
|
359
|
+
}
|
|
360
|
+
mkdirSync(dirname(destDesign), { recursive: true });
|
|
361
|
+
writeFileSync(destDesign, src, "utf8");
|
|
362
|
+
written++;
|
|
363
|
+
}
|
|
364
|
+
return written;
|
|
365
|
+
}
|
|
366
|
+
var CURSOR_RULE_BODY = [
|
|
367
|
+
"The authoritative design spec lives at `@DESIGN.md` (repo root). Open and read before generating/modifying UI.",
|
|
368
|
+
"",
|
|
369
|
+
"Pending preference corrections: `@.omd/preferences.md`.",
|
|
370
|
+
"",
|
|
371
|
+
"Precedence: DESIGN.md > preferences.md > framework defaults."
|
|
372
|
+
].join("\n");
|
|
373
|
+
function renderCursorRule() {
|
|
374
|
+
const hash = createHash("sha256").update(CURSOR_RULE_BODY).digest("hex").slice(0, 12);
|
|
375
|
+
return [
|
|
376
|
+
"---",
|
|
377
|
+
"description: Authoritative brand & UI design system. Read DESIGN.md before UI work.",
|
|
378
|
+
"globs:",
|
|
379
|
+
' - "**/*.tsx"',
|
|
380
|
+
' - "**/*.jsx"',
|
|
381
|
+
' - "**/*.vue"',
|
|
382
|
+
' - "**/*.svelte"',
|
|
383
|
+
' - "**/*.css"',
|
|
384
|
+
' - "**/*.scss"',
|
|
385
|
+
' - "**/tailwind.config.*"',
|
|
386
|
+
' - "**/components/**"',
|
|
387
|
+
' - "**/app/**/page.*"',
|
|
388
|
+
"alwaysApply: false",
|
|
389
|
+
"---",
|
|
390
|
+
"",
|
|
391
|
+
`<!-- omd:start v=1 hash=${hash} -->`,
|
|
392
|
+
CURSOR_RULE_BODY,
|
|
393
|
+
"<!-- omd:end -->",
|
|
394
|
+
""
|
|
395
|
+
].join("\n");
|
|
396
|
+
}
|
|
397
|
+
function installCursorRule(installRoot, force) {
|
|
398
|
+
const target = "cursor";
|
|
399
|
+
const skillLabel = "rule:omd-design.mdc";
|
|
400
|
+
const destPath = join2(installRoot, ".cursor", "rules", "omd-design.mdc");
|
|
401
|
+
const rendered = renderCursorRule();
|
|
402
|
+
const exists = existsSync2(destPath);
|
|
403
|
+
const existing = exists ? readFileSync(destPath, "utf8") : "";
|
|
404
|
+
if (exists && existing === rendered) {
|
|
405
|
+
return { target, skill: skillLabel, destPath, status: "unchanged" };
|
|
406
|
+
}
|
|
407
|
+
if (exists && !existing.includes("<!-- omd:start") && !force) {
|
|
408
|
+
return { target, skill: skillLabel, destPath, status: "skipped-drift" };
|
|
409
|
+
}
|
|
410
|
+
mkdirSync(dirname(destPath), { recursive: true });
|
|
411
|
+
writeFileSync(destPath, rendered, "utf8");
|
|
412
|
+
return { target, skill: skillLabel, destPath, status: exists ? "updated" : "created" };
|
|
413
|
+
}
|
|
332
414
|
var STATUS_LABEL = {
|
|
333
415
|
created: pc.green("created"),
|
|
334
416
|
updated: pc.cyan("updated"),
|
|
@@ -342,6 +424,7 @@ function autoDetectTargets(projectRoot) {
|
|
|
342
424
|
if (presence.claudeCode) targets.push("claude-code");
|
|
343
425
|
if (presence.codex) targets.push("codex");
|
|
344
426
|
if (presence.opencode) targets.push("opencode");
|
|
427
|
+
if (presence.cursor) targets.push("cursor");
|
|
345
428
|
if (targets.length === 0) {
|
|
346
429
|
return ["claude-code", "codex", "opencode"];
|
|
347
430
|
}
|
|
@@ -373,7 +456,8 @@ async function runInstallSkills(opts = {}) {
|
|
|
373
456
|
const actuallyDetected = [
|
|
374
457
|
presence.claudeCode ? "claude-code" : null,
|
|
375
458
|
presence.codex ? "codex" : null,
|
|
376
|
-
presence.opencode ? "opencode" : null
|
|
459
|
+
presence.opencode ? "opencode" : null,
|
|
460
|
+
presence.cursor ? "cursor" : null
|
|
377
461
|
].filter((x) => x !== null);
|
|
378
462
|
if (!opts.global && interactive) {
|
|
379
463
|
const scopeResult = await p.select({
|
|
@@ -430,17 +514,20 @@ async function runInstallSkills(opts = {}) {
|
|
|
430
514
|
}
|
|
431
515
|
const supportedTargets = (() => {
|
|
432
516
|
const set = new Set(skills.flatMap((s) => skillSupportedChannels(packageRoot, s)));
|
|
433
|
-
|
|
517
|
+
set.add("cursor");
|
|
518
|
+
return ["claude-code", "codex", "opencode", "cursor"].filter((t) => set.has(t));
|
|
434
519
|
})();
|
|
435
520
|
const channelLabel = {
|
|
436
521
|
"claude-code": "Claude Code",
|
|
437
522
|
codex: "Codex",
|
|
438
|
-
opencode: "OpenCode"
|
|
523
|
+
opencode: "OpenCode",
|
|
524
|
+
cursor: "Cursor"
|
|
439
525
|
};
|
|
440
526
|
const channelDir = {
|
|
441
527
|
"claude-code": ".claude",
|
|
442
528
|
codex: ".codex",
|
|
443
|
-
opencode: ".opencode"
|
|
529
|
+
opencode: ".opencode",
|
|
530
|
+
cursor: ".cursor"
|
|
444
531
|
};
|
|
445
532
|
let targets;
|
|
446
533
|
if (opts.agents) {
|
|
@@ -468,7 +555,10 @@ async function runInstallSkills(opts = {}) {
|
|
|
468
555
|
if (narrowed.length > 0) targets = narrowed;
|
|
469
556
|
}
|
|
470
557
|
const installRoot = scope === "global" ? homedir() : projectRoot;
|
|
471
|
-
const
|
|
558
|
+
const skillChannelTargets = targets.filter(
|
|
559
|
+
(t) => t !== "cursor"
|
|
560
|
+
);
|
|
561
|
+
const plans = skillChannelTargets.map((t) => planForTarget(installRoot, t));
|
|
472
562
|
p.log.message(
|
|
473
563
|
pc.bold("Scope: ") + pc.cyan(scope) + pc.dim(scope === "global" ? " (~/.claude)" : ` (${relative(process.cwd(), projectRoot) || "."})`)
|
|
474
564
|
);
|
|
@@ -487,6 +577,7 @@ async function runInstallSkills(opts = {}) {
|
|
|
487
577
|
pc.bold("Targets: ") + targets.map((t) => pc.cyan(t)).join(", ")
|
|
488
578
|
);
|
|
489
579
|
const results = [];
|
|
580
|
+
let catalogCount = 0;
|
|
490
581
|
for (const plan of plans) {
|
|
491
582
|
for (const skill of skills) {
|
|
492
583
|
results.push(installOne(packageRoot, plan, skill, force));
|
|
@@ -504,6 +595,9 @@ async function runInstallSkills(opts = {}) {
|
|
|
504
595
|
}
|
|
505
596
|
}
|
|
506
597
|
if (!minimal) {
|
|
598
|
+
if (targets.includes("cursor")) {
|
|
599
|
+
results.push(installCursorRule(installRoot, force));
|
|
600
|
+
}
|
|
507
601
|
const dataFiles = [
|
|
508
602
|
"reference-fingerprints.json",
|
|
509
603
|
"reference-tags.md",
|
|
@@ -520,6 +614,32 @@ async function runInstallSkills(opts = {}) {
|
|
|
520
614
|
for (const dataFile of dataFiles) {
|
|
521
615
|
results.push(installDataFile(packageRoot, installRoot, ".codex", dataFile, force));
|
|
522
616
|
}
|
|
617
|
+
} else if (target === "cursor" && !targets.includes("claude-code")) {
|
|
618
|
+
for (const dataFile of dataFiles) {
|
|
619
|
+
results.push(installDataFile(packageRoot, installRoot, ".claude", dataFile, force, "cursor"));
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
for (const target of targets) {
|
|
624
|
+
if (target === "claude-code") {
|
|
625
|
+
catalogCount += installReferenceCatalog(packageRoot, installRoot, ".claude", force);
|
|
626
|
+
} else if (target === "codex") {
|
|
627
|
+
catalogCount += installReferenceCatalog(packageRoot, installRoot, ".codex", force);
|
|
628
|
+
} else if (target === "cursor" && !targets.includes("claude-code")) {
|
|
629
|
+
catalogCount += installReferenceCatalog(packageRoot, installRoot, ".claude", force);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
for (const target of targets) {
|
|
633
|
+
const cd = target === "claude-code" ? ".claude" : target === "codex" ? ".codex" : null;
|
|
634
|
+
if (!cd) continue;
|
|
635
|
+
for (const helper of ["ctx-prime.cjs", "context.cjs"]) {
|
|
636
|
+
const srcHelper = join2(packageRoot, "scripts", helper);
|
|
637
|
+
if (!existsSync2(srcHelper)) continue;
|
|
638
|
+
const destHelper = join2(installRoot, cd, "data", "scripts", helper);
|
|
639
|
+
const srcTxt = readFileSync(srcHelper, "utf8");
|
|
640
|
+
if (existsSync2(destHelper) && readFileSync(destHelper, "utf8") === srcTxt) continue;
|
|
641
|
+
mkdirSync(dirname(destHelper), { recursive: true });
|
|
642
|
+
writeFileSync(destHelper, srcTxt, "utf8");
|
|
523
643
|
}
|
|
524
644
|
}
|
|
525
645
|
if (scope === "project" && targets.includes("claude-code")) {
|
|
@@ -527,7 +647,10 @@ async function runInstallSkills(opts = {}) {
|
|
|
527
647
|
"skill-activation.cjs",
|
|
528
648
|
"session-state-loader.cjs",
|
|
529
649
|
"post-edit-watch.cjs",
|
|
530
|
-
"session-end-foldin.cjs"
|
|
650
|
+
"session-end-foldin.cjs",
|
|
651
|
+
// Shared module required by the fold-in / state-loader hooks. Lives in a
|
|
652
|
+
// lib/ subdir; installHookFile preserves the relative path under .claude/hooks/.
|
|
653
|
+
join2("lib", "preferences-parser.cjs")
|
|
531
654
|
]) {
|
|
532
655
|
results.push(installHookFile(packageRoot, installRoot, hookFile, force));
|
|
533
656
|
}
|
|
@@ -589,9 +712,14 @@ async function runInstallSkills(opts = {}) {
|
|
|
589
712
|
].join("\n");
|
|
590
713
|
p.note(nextSteps, "Next");
|
|
591
714
|
const hookCount = scope === "project" && targets.includes("claude-code") ? 4 : 0;
|
|
715
|
+
if (catalogCount > 0) {
|
|
716
|
+
p.log.message(
|
|
717
|
+
pc.bold("Reference catalog: ") + pc.cyan(`${catalogCount}`) + pc.dim(" DESIGN.md copied \u2192 .claude/data/references/<id>/DESIGN.md")
|
|
718
|
+
);
|
|
719
|
+
}
|
|
592
720
|
p.outro(
|
|
593
721
|
pc.green(
|
|
594
|
-
`Done. ${skills.length} skills \xB7 ${canonicalAgents.length} sub-agents \xB7 ${hookCount} hooks installed (${writtenCount} files)${scope === "global" ? " globally (~/.claude)" : ""}.`
|
|
722
|
+
`Done. ${skills.length} skills \xB7 ${canonicalAgents.length} sub-agents \xB7 ${hookCount} hooks \xB7 ${catalogCount} catalog refs installed (${writtenCount} files)${scope === "global" ? " globally (~/.claude)" : ""}.`
|
|
595
723
|
)
|
|
596
724
|
);
|
|
597
725
|
return 0;
|
|
@@ -599,4 +727,4 @@ async function runInstallSkills(opts = {}) {
|
|
|
599
727
|
export {
|
|
600
728
|
runInstallSkills
|
|
601
729
|
};
|
|
602
|
-
//# sourceMappingURL=install-skills-
|
|
730
|
+
//# sourceMappingURL=install-skills-KDW74C5K.js.map
|