cx-skills 1.3.0 → 1.7.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/README.ko.md CHANGED
@@ -39,8 +39,11 @@ npm install -g cx-skills
39
39
  # 수동 모드 — agent 직접 지정
40
40
  /cx-auto 3:codex "src/api, src/auth, src/payment 각각 리뷰"
41
41
 
42
- # 단일 에이전트 라우팅
43
- /cx-route executor "JWT 인증 미들웨어 구현" implement 300
42
+ # Codex-only (Gemini 불필요)
43
+ /cx-codex "리팩터링 + 리뷰"
44
+
45
+ # Gemini-only (Codex 불필요)
46
+ /cx-gemini "구현 + 문서화"
44
47
  ```
45
48
 
46
49
  끝입니다. `cli-route.sh`는 세션 시작 시 자동 설치됩니다.
@@ -64,10 +67,8 @@ npm install -g cx-skills
64
67
  |------|---------|------|
65
68
  | **cx-auto** (자동) | N (AI 결정) | 복합 작업 — AI가 분류 + 분해 |
66
69
  | **cx-auto** (수동) | N:agent | 직접 제어 — agent와 워커 수 지정 |
67
- | **cx-route** | 1 | 단일 작업 에이전트 하나, 프롬프트 하나 |
68
- | **bteam** | N Codex | 코드 중심 병렬 작업 |
69
- | **hteam** | Codex + Gemini + Claude | 혼합 작업 (코드 + UI + 테스트) |
70
- | **kteam** | N (Windows 안정화) | Windows 환경 Gemini 안정화 적용 |
70
+ | **cx-codex** | N (Codex-only) | Gemini CLI 없는 환경 |
71
+ | **cx-gemini** | N (Gemini-only) | Codex CLI 없는 환경 |
71
72
 
72
73
  ---
73
74
 
@@ -78,9 +79,10 @@ npm install -g cx-skills
78
79
  | 스킬 | 설명 | 예시 |
79
80
  |------|------|------|
80
81
  | **cx-auto** | 통합 병렬 오케스트레이터 | `/cx-auto "분석 + 구현 + 테스트"` |
81
- | **cx-route** | 단일 에이전트 CLI 라우팅 | `/cx-route executor "기능 구현"` |
82
+ | **cx-codex** | Codex-only 오케스트레이터 (Gemini 불필요) | `/cx-codex "리팩터링 + 리뷰"` |
83
+ | **cx-gemini** | Gemini-only 오케스트레이터 (Codex 불필요) | `/cx-gemini "구현 + 문서화"` |
82
84
 
83
- ### CLI 커맨드 (`sc` 라우터 경유)
85
+ ### CLI 커맨드 (cx-auto 숏컷)
84
86
 
85
87
  | 커맨드 | 라우팅 대상 | 용도 |
86
88
  |--------|-----------|------|
@@ -93,16 +95,6 @@ npm install -g cx-skills
93
95
  | `/test` | Claude | 테스트 실행 |
94
96
  | `/explain` | Codex | 코드 설명 |
95
97
 
96
- [전체 커맨드 목록 →](skills/sc/SKILL.md)
97
-
98
- ### 팀 오케스트레이션
99
-
100
- | 스킬 | 설명 |
101
- |------|------|
102
- | **bteam** | Codex 워커 기반 멀티 에이전트 팀 |
103
- | **hteam** | 하이브리드 팀 (Codex + Gemini + Claude) |
104
- | **kteam** | Windows 안정화 팀 변형 |
105
-
106
98
  ### 유틸리티
107
99
 
108
100
  | 스킬 | 설명 |
@@ -221,12 +213,23 @@ gemini -p "echo hello"
221
213
 
222
214
  ### CLI 없이 사용
223
215
 
224
- Codex/Gemini CLI가 없어도 cx-skills는 동작합니다. 해당 CLI없으면 자동으로 **Claude 네이티브 에이전트로 fallback**합니다.
216
+ Codex/Gemini CLI가 없어도 cx-skills는 동작합니다. `cli-route.sh` (v1.4+)가 자동으로 **fallback** 처리합니다:
225
217
 
226
218
  ```
227
- Codex 없음 executor/debugger 등이 Claude Sonnet Agent로 실행
228
- Gemini 없음 → designer/writer 등이 Claude Sonnet Agent로 실행
229
- 둘 다 없음 → 모든 작업이 Claude 네이티브로 실행 (비용 증가)
219
+ Codex 없음 Codex 에이전트를 Gemini로 리매핑 (있으면) 또는 Claude 네이티브
220
+ Gemini 없음 → Gemini 에이전트를 Codex로 리매핑 (있으면) 또는 Claude 네이티브
221
+ 둘 다 없음 → 모든 작업이 Claude 네이티브로 실행 (비용 증가)
222
+ ```
223
+
224
+ 환경변수 또는 전용 스킬로 모드를 강제할 수도 있습니다:
225
+
226
+ ```bash
227
+ # 환경변수
228
+ CX_CLI_MODE=codex bash ~/.claude/scripts/cli-route.sh executor "작업" implement
229
+
230
+ # 전용 스킬
231
+ /cx-codex "작업" # 모든 외부 CLI 작업을 Codex로 강제
232
+ /cx-gemini "작업" # 모든 외부 CLI 작업을 Gemini로 강제
230
233
  ```
231
234
 
232
235
  ---
@@ -263,6 +266,8 @@ head -6 ~/.claude/scripts/cli-route.sh | grep VERSION
263
266
  | v1.1 | stderr 분리, 출력 필터링, 타임아웃, MCP 프로필 지원 |
264
267
  | v1.2 | effort 동적 라우팅, bg/fg 모드, Opus 직접 수행, Gemini 모델 분기 |
265
268
  | v1.3 | context-file 지원, DAG 실행 연계 |
269
+ | v1.4 | CX_CLI_MODE 지원 (codex-only/gemini-only), CLI 미설치 자동 fallback |
270
+ | v1.5 | MCP 인벤토리 캐싱 — 실제 서버 가용성 기반 동적 힌트 생성 |
266
271
 
267
272
  ### 자동 업데이트
268
273
 
@@ -298,15 +303,16 @@ cx-skills/
298
303
  ├── hooks/
299
304
  │ └── hooks.json # 라이프사이클 훅 (자동 설정)
300
305
  ├── scripts/
301
- │ ├── cli-route.sh # CLI 라우팅 래퍼 (v1.3)
306
+ │ ├── cli-route.sh # CLI 라우팅 래퍼 (v1.5)
307
+ │ ├── mcp-check.mjs # MCP 인벤토리 캐시 (백그라운드)
302
308
  │ └── setup.mjs # 세션 시작 시 자동 설치
303
- ├── skills/ # 16개 스킬 (자동 발견)
309
+ ├── skills/
304
310
  │ ├── cx-auto/ # 통합 병렬 오케스트레이터
305
- │ ├── cx-route/ # 단일 에이전트 라우팅
306
- │ ├── sc/ # CLI 커맨드 라우터
307
- │ ├── bteam/ # Codex
308
- │ ├── hteam/ # 하이브리드
309
- │ └── ...
311
+ │ ├── cx-codex/ # Codex-only 오케스트레이터
312
+ │ ├── cx-gemini/ # Gemini-only 오케스트레이터
313
+ │ ├── auto-verify/ # 실행 후 검증
314
+ │ ├── manage-skills/ # 스킬 관리
315
+ │ └── verify-implementation/ # 통합 검증
310
316
  ├── hud/
311
317
  │ └── hud-qos-status.mjs # 상태바 HUD
312
318
  ├── package.json
package/README.md CHANGED
@@ -41,8 +41,11 @@ npm install -g cx-skills
41
41
  # Manual mode — specify agent directly
42
42
  /cx-auto 3:codex "review src/api, src/auth, src/payment"
43
43
 
44
- # Single agent routing
45
- /cx-route executor "implement JWT auth middleware" implement 300
44
+ # Codex-only (no Gemini needed)
45
+ /cx-codex "refactor + review"
46
+
47
+ # Gemini-only (no Codex needed)
48
+ /cx-gemini "implement + document"
46
49
  ```
47
50
 
48
51
  That's it. `cli-route.sh` is auto-installed on session start.
@@ -66,10 +69,8 @@ That's it. `cli-route.sh` is auto-installed on session start.
66
69
  |------|---------|----------|
67
70
  | **cx-auto** (auto) | N (AI decides) | Complex multi-part tasks — AI classifies + decomposes |
68
71
  | **cx-auto** (manual) | N:agent | Direct control — you choose agent and worker count |
69
- | **cx-route** | 1 | Single focused task one agent, one prompt |
70
- | **bteam** | N Codex | Code-heavy parallel work |
71
- | **hteam** | Codex + Gemini + Claude | Mixed tasks (code + UI + testing) |
72
- | **kteam** | N (Windows stable) | Windows environments with Gemini stability fixes |
72
+ | **cx-codex** | N (Codex-only) | Environments without Gemini CLI |
73
+ | **cx-gemini** | N (Gemini-only) | Environments without Codex CLI |
73
74
 
74
75
  ---
75
76
 
@@ -80,9 +81,10 @@ That's it. `cli-route.sh` is auto-installed on session start.
80
81
  | Skill | Description | Example |
81
82
  |-------|-------------|---------|
82
83
  | **cx-auto** | Unified parallel orchestrator | `/cx-auto "analyze + implement + test"` |
83
- | **cx-route** | Single agent CLI routing | `/cx-route executor "implement feature"` |
84
+ | **cx-codex** | Codex-only orchestrator (no Gemini) | `/cx-codex "refactor + review"` |
85
+ | **cx-gemini** | Gemini-only orchestrator (no Codex) | `/cx-gemini "implement + document"` |
84
86
 
85
- ### CLI Commands (via `sc` router)
87
+ ### CLI Commands (via cx-auto shortcuts)
86
88
 
87
89
  | Command | Routes To | Use Case |
88
90
  |---------|-----------|----------|
@@ -95,16 +97,6 @@ That's it. `cli-route.sh` is auto-installed on session start.
95
97
  | `/test` | Claude | Test execution |
96
98
  | `/explain` | Codex | Code explanation |
97
99
 
98
- [Full command list →](skills/sc/SKILL.md)
99
-
100
- ### Team Orchestration
101
-
102
- | Skill | Description |
103
- |-------|-------------|
104
- | **bteam** | Multi-agent team with Codex workers |
105
- | **hteam** | Hybrid team (Codex + Gemini + Claude) |
106
- | **kteam** | Windows-stabilized team variant |
107
-
108
100
  ### Utilities
109
101
 
110
102
  | Skill | Description |
@@ -223,14 +215,25 @@ gemini -p "echo hello"
223
215
 
224
216
  ### Without External CLIs
225
217
 
226
- cx-skills works without Codex/Gemini. Missing CLIs automatically **fallback to Claude native agents**:
218
+ cx-skills works without Codex/Gemini. Missing CLIs automatically **fallback** via `cli-route.sh` (v1.4+):
227
219
 
228
220
  ```
229
- No Codex executor/debugger run as Claude Sonnet Agent
230
- No Gemini → designer/writer run as Claude Sonnet Agent
221
+ No Codex Codex agents reroute to Gemini (if available) or Claude native
222
+ No Gemini → Gemini agents reroute to Codex (if available) or Claude native
231
223
  Neither → All tasks run via Claude native (higher cost)
232
224
  ```
233
225
 
226
+ You can also force a specific mode via environment variable or dedicated skills:
227
+
228
+ ```bash
229
+ # Environment variable
230
+ CX_CLI_MODE=codex bash ~/.claude/scripts/cli-route.sh executor "task" implement
231
+
232
+ # Dedicated skills
233
+ /cx-codex "task" # Forces all external CLI work to Codex
234
+ /cx-gemini "task" # Forces all external CLI work to Gemini
235
+ ```
236
+
234
237
  ---
235
238
 
236
239
  ## OMC Compatibility
@@ -276,6 +279,8 @@ Follows [Semantic Versioning](https://semver.org/):
276
279
  | v1.1 | stderr separation, output filtering, timeout, MCP profile support |
277
280
  | v1.2 | Dynamic effort routing, bg/fg modes, Opus oversight, Gemini model branching |
278
281
  | v1.3 | Context-file support, DAG execution integration |
282
+ | v1.4 | CX_CLI_MODE support (codex-only/gemini-only), auto-fallback for missing CLIs |
283
+ | v1.5 | MCP inventory caching — dynamic hints based on actual server availability |
279
284
 
280
285
  ### Auto-Update
281
286
 
@@ -298,15 +303,16 @@ cx-skills/
298
303
  ├── hooks/
299
304
  │ └── hooks.json # Lifecycle hooks (auto-setup)
300
305
  ├── scripts/
301
- │ ├── cli-route.sh # CLI routing wrapper (v1.3)
306
+ │ ├── cli-route.sh # CLI routing wrapper (v1.5)
307
+ │ ├── mcp-check.mjs # MCP inventory cache (background)
302
308
  │ └── setup.mjs # Auto-install on session start
303
- ├── skills/ # 16 skills (auto-discovered)
309
+ ├── skills/
304
310
  │ ├── cx-auto/ # Unified parallel orchestrator
305
- │ ├── cx-route/ # Single agent routing
306
- │ ├── sc/ # CLI command router
307
- │ ├── bteam/ # Codex team
308
- │ ├── hteam/ # Hybrid team
309
- │ └── ...
311
+ │ ├── cx-codex/ # Codex-only orchestrator
312
+ │ ├── cx-gemini/ # Gemini-only orchestrator
313
+ │ ├── auto-verify/ # Post-execution verification
314
+ │ ├── manage-skills/ # Skill management
315
+ │ └── verify-implementation/ # Integration verification
310
316
  ├── hud/
311
317
  │ └── hud-qos-status.mjs # Status bar HUD
312
318
  ├── package.json
package/bin/cx-skills.mjs CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
  // cx-skills CLI — setup, doctor, version
3
3
  import { copyFileSync, existsSync, readFileSync, mkdirSync, chmodSync, readdirSync } from "fs";
4
4
  import { join, dirname } from "path";
@@ -34,6 +34,32 @@ function which(cmd) {
34
34
  } catch { return null; }
35
35
  }
36
36
 
37
+ function whichInShell(cmd, shell) {
38
+ const cmds = {
39
+ bash: `bash -c 'command -v ${cmd} 2>/dev/null'`,
40
+ cmd: `cmd /c where ${cmd} 2>nul`,
41
+ pwsh: `pwsh -NoProfile -c "(Get-Command ${cmd} -EA SilentlyContinue).Source"`,
42
+ };
43
+ const command = cmds[shell];
44
+ if (!command) return null;
45
+ try {
46
+ const result = execSync(command, {
47
+ encoding: "utf8",
48
+ timeout: 8000,
49
+ stdio: ["pipe", "pipe", "ignore"],
50
+ }).trim();
51
+ return result.split(/\r?\n/)[0] || null;
52
+ } catch { return null; }
53
+ }
54
+
55
+ function checkShellAvailable(shell) {
56
+ const cmds = { bash: "bash --version", cmd: "cmd /c echo ok", pwsh: "pwsh -NoProfile -c echo ok" };
57
+ try {
58
+ execSync(cmds[shell], { encoding: "utf8", timeout: 5000, stdio: ["pipe", "pipe", "ignore"] });
59
+ return true;
60
+ } catch { return false; }
61
+ }
62
+
37
63
  function getVersion(filePath) {
38
64
  try {
39
65
  const content = readFileSync(filePath, "utf8");
@@ -61,17 +87,57 @@ function syncFile(src, dst, label) {
61
87
  return true;
62
88
  }
63
89
 
64
- if (srcVer && dstVer && srcVer !== dstVer) {
90
+ const srcContent = readFileSync(src, "utf8");
91
+ const dstContent = readFileSync(dst, "utf8");
92
+ if (srcContent !== dstContent) {
65
93
  copyFileSync(src, dst);
66
94
  try { chmodSync(dst, 0o755); } catch {}
67
- ok(`${label}: 업데이트됨 (v${dstVer} v${srcVer})`);
95
+ const verInfo = (srcVer && dstVer && srcVer !== dstVer)
96
+ ? `(v${dstVer} → v${srcVer})`
97
+ : srcVer ? `(v${srcVer}, 내용 변경)` : "(내용 변경)";
98
+ ok(`${label}: 업데이트됨 ${verInfo}`);
68
99
  return true;
69
100
  }
70
101
 
71
- ok(`${label}: 최신 상태 ${dstVer ? `(v${dstVer})` : ""}`);
102
+ ok(`${label}: 최신 상태 ${srcVer ? `(v${srcVer})` : ""}`);
72
103
  return false;
73
104
  }
74
105
 
106
+ // ── 크로스 셸 진단 ──
107
+
108
+ function checkCliCrossShell(cmd, installHint) {
109
+ const shells = process.platform === "win32" ? ["bash", "cmd", "pwsh"] : ["bash"];
110
+ let anyFound = false;
111
+ let bashMissing = false;
112
+
113
+ for (const shell of shells) {
114
+ if (!checkShellAvailable(shell)) {
115
+ info(`${shell}: ${DIM}셸 없음 (건너뜀)${RESET}`);
116
+ continue;
117
+ }
118
+ const p = whichInShell(cmd, shell);
119
+ if (p) {
120
+ ok(`${shell}: ${p}`);
121
+ anyFound = true;
122
+ } else {
123
+ fail(`${shell}: 미발견`);
124
+ if (shell === "bash") bashMissing = true;
125
+ }
126
+ }
127
+
128
+ if (!anyFound) {
129
+ info(`미설치 (선택사항) — ${installHint}`);
130
+ info("없으면 Claude 네이티브 에이전트로 fallback");
131
+ return 1;
132
+ }
133
+ if (bashMissing) {
134
+ warn("bash에서 미발견 — cli-route.sh 실행 불가");
135
+ info('→ ~/.bashrc에 추가: export PATH="$PATH:$APPDATA/npm"');
136
+ return 1;
137
+ }
138
+ return 0;
139
+ }
140
+
75
141
  // ── 명령어 ──
76
142
 
77
143
  function cmdSetup() {
@@ -150,37 +216,26 @@ function cmdDoctor() {
150
216
  warn("미설치 (선택사항) — cx-skills setup으로 설치 가능");
151
217
  }
152
218
 
153
- // 3. Codex CLI
219
+ // 3. Codex CLI (크로스 셸 체크)
154
220
  console.log(`${CYAN}[Codex CLI]${RESET}`);
155
- const codexPath = which("codex");
156
- if (codexPath) {
157
- ok(`설치됨 — ${codexPath}`);
158
- // API 키 확인
221
+ issues += checkCliCrossShell("codex", "npm install -g @openai/codex");
222
+ if (which("codex")) {
159
223
  if (process.env.OPENAI_API_KEY) {
160
224
  ok("OPENAI_API_KEY 설정됨");
161
225
  } else {
162
226
  warn("OPENAI_API_KEY 미설정 (Codex Pro 구독이면 불필요)");
163
227
  }
164
- } else {
165
- warn("미설치 (선택사항) — npm install -g @openai/codex");
166
- info("없으면 Claude 네이티브 에이전트로 fallback");
167
- issues++;
168
228
  }
169
229
 
170
- // 4. Gemini CLI
230
+ // 4. Gemini CLI (크로스 셸 체크)
171
231
  console.log(`${CYAN}[Gemini CLI]${RESET}`);
172
- const geminiPath = which("gemini");
173
- if (geminiPath) {
174
- ok(`설치됨 — ${geminiPath}`);
232
+ issues += checkCliCrossShell("gemini", "npm install -g @google/gemini-cli");
233
+ if (which("gemini")) {
175
234
  if (process.env.GEMINI_API_KEY) {
176
235
  ok("GEMINI_API_KEY 설정됨");
177
236
  } else {
178
237
  warn("GEMINI_API_KEY 미설정 (gemini auth login 필요할 수 있음)");
179
238
  }
180
- } else {
181
- warn("미설치 (선택사항) — npm install -g @google/gemini-cli");
182
- info("없으면 Claude 네이티브 에이전트로 fallback");
183
- issues++;
184
239
  }
185
240
 
186
241
  // 5. Claude Code
@@ -193,7 +248,33 @@ function cmdDoctor() {
193
248
  issues++;
194
249
  }
195
250
 
196
- // 6. 플러그인 등록
251
+ // 6. 스킬 설치 상태
252
+ console.log(`${CYAN}[Skills]${RESET}`);
253
+ const skillsSrc = join(PKG_ROOT, "skills");
254
+ const skillsDst = join(CLAUDE_DIR, "skills");
255
+ if (existsSync(skillsSrc)) {
256
+ let installed = 0;
257
+ let total = 0;
258
+ const missing = [];
259
+ for (const name of readdirSync(skillsSrc)) {
260
+ if (!existsSync(join(skillsSrc, name, "SKILL.md"))) continue;
261
+ total++;
262
+ if (existsSync(join(skillsDst, name, "SKILL.md"))) {
263
+ installed++;
264
+ } else {
265
+ missing.push(name);
266
+ }
267
+ }
268
+ if (installed === total) {
269
+ ok(`${installed}/${total}개 설치됨`);
270
+ } else {
271
+ warn(`${installed}/${total}개 설치됨 — 미설치: ${missing.join(", ")}`);
272
+ info("cx-skills setup으로 동기화 가능");
273
+ issues++;
274
+ }
275
+ }
276
+
277
+ // 7. 플러그인 등록
197
278
  console.log(`${CYAN}[Plugin]${RESET}`);
198
279
  const pluginsFile = join(CLAUDE_DIR, "plugins", "installed_plugins.json");
199
280
  if (existsSync(pluginsFile)) {
@@ -208,6 +289,29 @@ function cmdDoctor() {
208
289
  info("플러그인 시스템 감지 안 됨 — npm 단독 사용");
209
290
  }
210
291
 
292
+ // 8. MCP 인벤토리
293
+ console.log(`${CYAN}[MCP Inventory]${RESET}`);
294
+ const mcpCache = join(CLAUDE_DIR, "cache", "mcp-inventory.json");
295
+ if (existsSync(mcpCache)) {
296
+ try {
297
+ const inv = JSON.parse(readFileSync(mcpCache, "utf8"));
298
+ ok(`캐시 존재 (${inv.timestamp})`);
299
+ if (inv.codex?.servers?.length) {
300
+ const names = inv.codex.servers.map(s => s.name).join(", ");
301
+ info(`Codex: ${inv.codex.servers.length}개 서버 (${names})`);
302
+ }
303
+ if (inv.gemini?.servers?.length) {
304
+ const names = inv.gemini.servers.map(s => s.name).join(", ");
305
+ info(`Gemini: ${inv.gemini.servers.length}개 서버 (${names})`);
306
+ }
307
+ } catch {
308
+ warn("캐시 파일 파싱 실패");
309
+ }
310
+ } else {
311
+ warn("캐시 없음 — 다음 세션 시작 시 자동 생성");
312
+ info(`수동: node ${join(PKG_ROOT, "scripts", "mcp-check.mjs")}`);
313
+ }
314
+
211
315
  // 결과
212
316
  console.log("");
213
317
  if (issues === 0) {
@@ -217,10 +321,185 @@ function cmdDoctor() {
217
321
  }
218
322
  }
219
323
 
324
+ function cmdUpdate() {
325
+ console.log(`\n${BOLD}cx-skills update${RESET}\n`);
326
+
327
+ // 1. 설치 방식 감지
328
+ const pluginsFile = join(CLAUDE_DIR, "plugins", "installed_plugins.json");
329
+ let installMode = "unknown";
330
+ let pluginPath = null;
331
+
332
+ // 플러그인 모드 감지
333
+ if (existsSync(pluginsFile)) {
334
+ try {
335
+ const plugins = JSON.parse(readFileSync(pluginsFile, "utf8"));
336
+ for (const [key, entries] of Object.entries(plugins.plugins || {})) {
337
+ if (key.startsWith("cx-skills")) {
338
+ pluginPath = entries[0]?.installPath;
339
+ installMode = "plugin";
340
+ break;
341
+ }
342
+ }
343
+ } catch {}
344
+ }
345
+
346
+ // PKG_ROOT가 플러그인 캐시 내에 있으면 플러그인 모드
347
+ if (installMode === "unknown" && PKG_ROOT.includes(join(".claude", "plugins"))) {
348
+ installMode = "plugin";
349
+ pluginPath = PKG_ROOT;
350
+ }
351
+
352
+ // npm global 감지
353
+ if (installMode === "unknown") {
354
+ try {
355
+ const npmList = execSync("npm list -g cx-skills --depth=0", {
356
+ encoding: "utf8",
357
+ timeout: 10000,
358
+ stdio: ["pipe", "pipe", "ignore"],
359
+ });
360
+ if (npmList.includes("cx-skills")) installMode = "npm-global";
361
+ } catch {}
362
+ }
363
+
364
+ // npm local 감지
365
+ if (installMode === "unknown") {
366
+ const localPkg = join(process.cwd(), "node_modules", "cx-skills");
367
+ if (existsSync(localPkg)) installMode = "npm-local";
368
+ }
369
+
370
+ // git 저장소 직접 사용
371
+ if (installMode === "unknown" && existsSync(join(PKG_ROOT, ".git"))) {
372
+ installMode = "git-local";
373
+ }
374
+
375
+ info(`검색: ${installMode === "plugin" ? "플러그인" : installMode === "npm-global" ? "npm global" : installMode === "npm-local" ? "npm local" : installMode === "git-local" ? "git 로컬 저장소" : "알 수 없음"} 설치 감지`);
376
+
377
+ // 2. 설치 방식에 따라 업데이트
378
+ const oldVer = PKG.version;
379
+ let updated = false;
380
+
381
+ try {
382
+ switch (installMode) {
383
+ case "plugin": {
384
+ const gitDir = pluginPath || PKG_ROOT;
385
+ const result = execSync("git pull", {
386
+ encoding: "utf8",
387
+ timeout: 30000,
388
+ cwd: gitDir,
389
+ }).trim();
390
+ ok(`git pull — ${result}`);
391
+ updated = true;
392
+ break;
393
+ }
394
+ case "npm-global": {
395
+ const result = execSync("npm update -g cx-skills", {
396
+ encoding: "utf8",
397
+ timeout: 60000,
398
+ stdio: ["pipe", "pipe", "ignore"],
399
+ }).trim().split(/\r?\n/)[0];
400
+ ok(`npm update -g — ${result || "완료"}`);
401
+ updated = true;
402
+ break;
403
+ }
404
+ case "npm-local": {
405
+ const result = execSync("npm update cx-skills", {
406
+ encoding: "utf8",
407
+ timeout: 60000,
408
+ cwd: process.cwd(),
409
+ stdio: ["pipe", "pipe", "ignore"],
410
+ }).trim().split(/\r?\n/)[0];
411
+ ok(`npm update — ${result || "완료"}`);
412
+ updated = true;
413
+ break;
414
+ }
415
+ case "git-local": {
416
+ const result = execSync("git pull", {
417
+ encoding: "utf8",
418
+ timeout: 30000,
419
+ cwd: PKG_ROOT,
420
+ }).trim();
421
+ ok(`git pull — ${result}`);
422
+ updated = true;
423
+ break;
424
+ }
425
+ default:
426
+ fail("설치 방식을 감지할 수 없음");
427
+ info("수동 업데이트: cd <cx-skills-dir> && git pull");
428
+ return;
429
+ }
430
+ } catch (e) {
431
+ fail(`업데이트 실패: ${e.message}`);
432
+ return;
433
+ }
434
+
435
+ // 3. setup 재실행 (cli-route.sh, HUD, 스킬 동기화)
436
+ if (updated) {
437
+ console.log("");
438
+ // 업데이트 후 새 버전 읽기
439
+ let newVer = oldVer;
440
+ try {
441
+ const newPkg = JSON.parse(readFileSync(join(PKG_ROOT, "package.json"), "utf8"));
442
+ newVer = newPkg.version;
443
+ } catch {}
444
+
445
+ if (newVer !== oldVer) {
446
+ ok(`버전: v${oldVer} → v${newVer}`);
447
+ } else {
448
+ ok(`버전: v${oldVer} (이미 최신)`);
449
+ }
450
+
451
+ // setup 재실행
452
+ console.log("");
453
+ info("setup 재실행 중...");
454
+ cmdSetup();
455
+ }
456
+
457
+ console.log(`${GREEN}${BOLD}업데이트 완료${RESET}\n`);
458
+ }
459
+
460
+ function cmdList() {
461
+ console.log(`\n${BOLD}cx-skills list${RESET}\n`);
462
+
463
+ // 플러그인 내 스킬
464
+ const pluginSkills = join(PKG_ROOT, "skills");
465
+ const installedSkills = join(CLAUDE_DIR, "skills");
466
+
467
+ console.log(`${CYAN}[패키지 스킬]${RESET}`);
468
+ if (existsSync(pluginSkills)) {
469
+ for (const name of readdirSync(pluginSkills).sort()) {
470
+ const src = join(pluginSkills, name, "SKILL.md");
471
+ if (!existsSync(src)) continue;
472
+ const dst = join(installedSkills, name, "SKILL.md");
473
+ const installed = existsSync(dst);
474
+ const status = installed ? `${GREEN}✓${RESET}` : `${RED}✗${RESET}`;
475
+ console.log(` ${status} ${BOLD}${name}${RESET}${installed ? "" : ` ${DIM}(미설치)${RESET}`}`);
476
+ }
477
+ }
478
+
479
+ // 설치된 스킬 중 패키지에 없는 것
480
+ console.log(`\n${CYAN}[사용자 스킬]${RESET}`);
481
+ const pkgNames = new Set(existsSync(pluginSkills) ? readdirSync(pluginSkills) : []);
482
+ let userCount = 0;
483
+ if (existsSync(installedSkills)) {
484
+ for (const name of readdirSync(installedSkills).sort()) {
485
+ if (pkgNames.has(name)) continue;
486
+ const skill = join(installedSkills, name, "SKILL.md");
487
+ if (!existsSync(skill)) continue;
488
+ console.log(` ${DIM}●${RESET} ${name}`);
489
+ userCount++;
490
+ }
491
+ }
492
+ if (userCount === 0) info("없음");
493
+
494
+ console.log(`\n${DIM}설치 위치: ${installedSkills}${RESET}\n`);
495
+ }
496
+
220
497
  function cmdVersion() {
221
498
  console.log(`cx-skills v${PKG.version}`);
222
499
  const routeVer = getVersion(join(CLAUDE_DIR, "scripts", "cli-route.sh"));
223
500
  if (routeVer) console.log(`cli-route.sh v${routeVer}`);
501
+ const hudVer = getVersion(join(CLAUDE_DIR, "hud", "hud-qos-status.mjs"));
502
+ if (hudVer) console.log(`hud-qos-status.mjs v${hudVer}`);
224
503
  }
225
504
 
226
505
  function cmdHelp() {
@@ -228,8 +507,10 @@ function cmdHelp() {
228
507
  ${BOLD}cx-skills${RESET} — CLI-first multi-model orchestrator for Claude Code
229
508
 
230
509
  ${CYAN}Usage:${RESET}
231
- cx-skills setup cli-route.sh HUD ~/.claude/에 설치/업데이트
232
- cx-skills doctor Codex/Gemini 연결 상태 진단
510
+ cx-skills setup cli-route.sh, HUD, 스킬을 ~/.claude/에 설치/업데이트
511
+ cx-skills doctor 크로스 CLI 진단 + MCP 인벤토리 확인
512
+ cx-skills update 최신 버전으로 업데이트
513
+ cx-skills list 설치된 스킬 목록 표시
233
514
  cx-skills version 버전 표시
234
515
  cx-skills help 이 도움말
235
516
 
@@ -248,6 +529,8 @@ const cmd = process.argv[2] || "help";
248
529
  switch (cmd) {
249
530
  case "setup": cmdSetup(); break;
250
531
  case "doctor": cmdDoctor(); break;
532
+ case "update": cmdUpdate(); break;
533
+ case "list": case "ls": cmdList(); break;
251
534
  case "version": case "--version": case "-v": cmdVersion(); break;
252
535
  case "help": case "--help": case "-h": cmdHelp(); break;
253
536
  default: