sh-ui-cli 0.21.2 → 0.22.1

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.md CHANGED
@@ -51,6 +51,82 @@ npx sh-ui list
51
51
  npx sh-ui remove button
52
52
  ```
53
53
 
54
+ ### mcp — AI 에게 sh-ui 를 알려주기 (v0.21.0+)
55
+
56
+ `sh-ui mcp` 는 [Model Context Protocol](https://modelcontextprotocol.io) 서버를 stdio 로 시작한다. IDE-내 AI(Claude Code, Cursor, Windsurf 등) 가 sh-ui 컴포넌트를 자동으로 검색·설치할 수 있게 7개 툴을 노출한다.
57
+
58
+ **한 번만 등록하면 끝** — 빈 폴더에서도 _"다크 모던 sh-ui 로 세팅하고 button 추가해줘"_ 만 말하면 AI 가 알아서 처리.
59
+
60
+ #### 자동 등록 (권장, v0.22.0+)
61
+
62
+ 빈 폴더에서도 바로 — `sh-ui-cli` 설치 불필요, `-y` 플래그로 자동 확인:
63
+
64
+ ```bash
65
+ npx -y sh-ui-cli mcp init --client claude-code # → .mcp.json
66
+ npx -y sh-ui-cli mcp init --client cursor # → .cursor/mcp.json
67
+ npx -y sh-ui-cli mcp init --client claude-desktop # → 사용자 전역 (재시작 필요)
68
+
69
+ # 사용자 전역 설정에 등록하려면
70
+ npx -y sh-ui-cli mcp init --client claude-code --scope user
71
+ ```
72
+
73
+ > 참고: 위 다른 명령들의 `npx sh-ui ...` 표기는 `sh-ui-cli` 가 dev 의존성으로 설치된 상태를 가정한다. 빈 폴더에서 한 번에 쓰려면 동일하게 `npx -y sh-ui-cli ...` 로 호출.
74
+
75
+ 기존 설정 파일이 있으면 다른 MCP 서버 엔트리를 보존하며 `sh-ui` 만 머지·갱신.
76
+
77
+ #### 수동 등록
78
+
79
+ **Claude Code** — `~/.claude/mcp.json` 또는 프로젝트 `.mcp.json`:
80
+
81
+ ```json
82
+ {
83
+ "mcpServers": {
84
+ "sh-ui": {
85
+ "command": "npx",
86
+ "args": ["-y", "sh-ui-cli", "mcp"]
87
+ }
88
+ }
89
+ }
90
+ ```
91
+
92
+ **Cursor** — `~/.cursor/mcp.json` 또는 `.cursor/mcp.json`:
93
+
94
+ ```json
95
+ {
96
+ "mcpServers": {
97
+ "sh-ui": {
98
+ "command": "npx",
99
+ "args": ["-y", "sh-ui-cli", "mcp"]
100
+ }
101
+ }
102
+ }
103
+ ```
104
+
105
+ **Claude Desktop** — `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS):
106
+
107
+ ```json
108
+ {
109
+ "mcpServers": {
110
+ "sh-ui": {
111
+ "command": "npx",
112
+ "args": ["-y", "sh-ui-cli", "mcp"]
113
+ }
114
+ }
115
+ }
116
+ ```
117
+
118
+ #### 노출되는 툴
119
+
120
+ | 툴 | 설명 |
121
+ |---|---|
122
+ | `sh_ui_describe_init` | platform/base/radius/mode 선택지 + 한글 설명 — 자연어 의도("다크 모던") → enum 매핑용 |
123
+ | `sh_ui_init` | `sh-ui.config.json` 생성 (비대화형) |
124
+ | `sh_ui_list_components` | 플랫폼별 전체 컴포넌트 + 요약 + deps |
125
+ | `sh_ui_get_component` | 단일 컴포넌트 메타·소스파일·deps |
126
+ | `sh_ui_add_component` | 컴포넌트 설치 (외부 패키지 자동 설치) |
127
+ | `sh_ui_remove_component` | 삭제 (수정 파일 보호) |
128
+ | `sh_ui_get_changelog` | 변경 내역 조회 |
129
+
54
130
  ## 지원 플랫폼
55
131
 
56
132
  - **React (Next.js)** — `src/shared/ui/` 또는 `sh-ui.config.json` 에 지정된 경로로 복사
package/bin/sh-ui.mjs CHANGED
@@ -14,6 +14,8 @@ const usage = `사용법:
14
14
  sh-ui list 현재 설치된 컴포넌트 목록 표시
15
15
  sh-ui remove <component...> 설치된 컴포넌트 파일 삭제
16
16
  sh-ui mcp MCP 서버(stdio) 시작 — IDE-내 AI용
17
+ sh-ui mcp init --client <name> IDE MCP 설정 파일에 sh-ui 엔트리 자동 추가
18
+ (claude-code | cursor | claude-desktop)
17
19
  옵션:
18
20
  --skip-install (add) 외부 패키지 자동 설치 생략
19
21
  --diff (add) 파일을 쓰지 않고 변경 내역만 출력
@@ -45,8 +47,15 @@ try {
45
47
  break;
46
48
  }
47
49
  case "mcp": {
48
- const { startMcpServer } = await import("../src/mcp.mjs");
49
- await startMcpServer();
50
+ // `sh-ui mcp init ...` 설정 파일에 엔트리 추가
51
+ // `sh-ui mcp` → MCP 서버 시작
52
+ if (rest[0] === "init") {
53
+ const { mcpInit } = await import("../src/mcp-init.mjs");
54
+ await mcpInit({ cwd: process.cwd(), args: rest.slice(1) });
55
+ } else {
56
+ const { startMcpServer } = await import("../src/mcp.mjs");
57
+ await startMcpServer();
58
+ }
50
59
  break;
51
60
  }
52
61
  case "remove":
@@ -2,6 +2,31 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$description": "sh-ui 릴리즈 노트 단일 소스. docs(React)와 showcase(Flutter)가 함께 읽는다. 새 릴리즈마다 맨 앞에 추가.",
4
4
  "versions": [
5
+ {
6
+ "version": "0.22.1",
7
+ "date": "2026-04-27",
8
+ "title": "mcp init claude-code user 스코프 경로 수정",
9
+ "type": "patch",
10
+ "highlights": [
11
+ "sh-ui mcp init --client claude-code --scope user 가 잘못된 경로(~/.claude/mcp.json)에 쓰던 문제 수정 — Claude Code 가 실제로 읽지 않는 파일이라 등록 후에도 /mcp 화면에 sh-ui 가 안 보였음",
12
+ "올바른 경로 ~/.claude.json (사용자 settings 전체 파일) 로 머지 — mcpServers 외 projects·history 등 다른 키는 그대로 보존",
13
+ "0.22.0 으로 user 스코프 등록한 사용자는 새 버전으로 다시 실행 후 ~/.claude/mcp.json 수동 삭제"
14
+ ],
15
+ "url": "https://github.com/sanghyeonKim0201/sh-ui/releases/tag/v0.22.1"
16
+ },
17
+ {
18
+ "version": "0.22.0",
19
+ "date": "2026-04-27",
20
+ "title": "sh-ui mcp init — IDE MCP 설정 자동 등록",
21
+ "type": "minor",
22
+ "highlights": [
23
+ "sh-ui mcp init --client <claude-code|cursor|claude-desktop> 추가 — IDE 별 MCP 설정 파일을 자동으로 찾아 sh-ui 엔트리 머지",
24
+ "스코프 분기 — 기본 project(.mcp.json / .cursor/mcp.json), --scope user 로 전역(~/.claude/mcp.json 등) 선택 가능. claude-desktop 은 user 전용(OS 별 경로 자동 분기)",
25
+ "기존 JSON 보존 — 다른 MCP 서버 엔트리·기타 키를 건드리지 않고 sh-ui 만 머지·갱신",
26
+ "수동 JSON 편집 없이 npx sh-ui mcp init --client cursor 한 줄로 끝"
27
+ ],
28
+ "url": "https://github.com/sanghyeonKim0201/sh-ui/releases/tag/v0.22.0"
29
+ },
5
30
  {
6
31
  "version": "0.21.2",
7
32
  "date": "2026-04-27",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sh-ui-cli",
3
- "version": "0.21.2",
3
+ "version": "0.22.1",
4
4
  "description": "sh-ui CLI — 디자인 시스템 컴포넌트를 프로젝트로 복사하는 CLI (sh-ui init / add / list / remove)",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -0,0 +1,146 @@
1
+ // sh-ui mcp init — 타깃 IDE 의 MCP 설정 파일에 sh-ui 엔트리를 자동 추가.
2
+ //
3
+ // 지원 클라이언트:
4
+ // claude-code — project: <cwd>/.mcp.json, user: ~/.claude.json
5
+ // cursor — project: <cwd>/.cursor/mcp.json, user: ~/.cursor/mcp.json
6
+ // claude-desktop — user 만 (OS 별 경로 자동 분기)
7
+ //
8
+ // 주의: Claude Code 의 user-scope 설정은 `~/.claude/mcp.json` 같은 별도
9
+ // 파일이 아니라 사용자 settings 전체가 들어가는 단일 JSON `~/.claude.json`
10
+ // 이다. 이 파일에는 mcpServers 외에도 projects·history 등 다른 키가 같이
11
+ // 들어 있으므로, 머지 시 다른 키를 절대 건드리지 않아야 한다.
12
+ //
13
+ // 동작: 기존 JSON 의 mcpServers.sh-ui 를 머지(있으면 덮어쓰기), 디렉토리 자동 생성.
14
+
15
+ import { readFile, writeFile, mkdir } from "node:fs/promises";
16
+ import { existsSync } from "node:fs";
17
+ import { dirname, resolve, relative } from "node:path";
18
+ import { homedir, platform as osPlatform } from "node:os";
19
+
20
+ const CLIENTS = ["claude-code", "cursor", "claude-desktop"];
21
+
22
+ const SH_UI_ENTRY = {
23
+ command: "npx",
24
+ args: ["-y", "sh-ui-cli", "mcp"],
25
+ };
26
+
27
+ /** 클라이언트·스코프별 설정 파일 절대 경로. */
28
+ function resolveConfigPath(client, scope, cwd) {
29
+ const home = homedir();
30
+ if (client === "claude-code") {
31
+ return scope === "user"
32
+ ? resolve(home, ".claude.json")
33
+ : resolve(cwd, ".mcp.json");
34
+ }
35
+ if (client === "cursor") {
36
+ return scope === "user"
37
+ ? resolve(home, ".cursor", "mcp.json")
38
+ : resolve(cwd, ".cursor", "mcp.json");
39
+ }
40
+ if (client === "claude-desktop") {
41
+ if (scope !== "user") {
42
+ throw new Error(
43
+ "claude-desktop 은 user 스코프만 지원합니다. --scope user 또는 --scope 생략.",
44
+ );
45
+ }
46
+ const os = osPlatform();
47
+ if (os === "darwin") {
48
+ return resolve(
49
+ home,
50
+ "Library",
51
+ "Application Support",
52
+ "Claude",
53
+ "claude_desktop_config.json",
54
+ );
55
+ }
56
+ if (os === "win32") {
57
+ const appData = process.env.APPDATA ?? resolve(home, "AppData", "Roaming");
58
+ return resolve(appData, "Claude", "claude_desktop_config.json");
59
+ }
60
+ // linux + 기타
61
+ return resolve(home, ".config", "Claude", "claude_desktop_config.json");
62
+ }
63
+ throw new Error(`알 수 없는 클라이언트: ${client}. 허용: ${CLIENTS.join(", ")}`);
64
+ }
65
+
66
+ /** 클라이언트별 기본 스코프. claude-desktop 은 user 강제. */
67
+ function defaultScope(client) {
68
+ return client === "claude-desktop" ? "user" : "project";
69
+ }
70
+
71
+ /** JSON 읽기 (없으면 빈 객체). 깨진 JSON 은 명시적 에러. */
72
+ async function readJsonOrEmpty(path) {
73
+ if (!existsSync(path)) return {};
74
+ let raw;
75
+ try {
76
+ raw = await readFile(path, "utf8");
77
+ } catch (err) {
78
+ throw new Error(`설정 파일 읽기 실패: ${path}\n ${err.message}`);
79
+ }
80
+ if (raw.trim() === "") return {};
81
+ try {
82
+ return JSON.parse(raw);
83
+ } catch (err) {
84
+ throw new Error(
85
+ `기존 ${path} 가 유효한 JSON 이 아닙니다. 수동으로 고치고 다시 시도하세요.\n ${err.message}`,
86
+ );
87
+ }
88
+ }
89
+
90
+ export async function mcpInit({ cwd, args }) {
91
+ const flags = parseFlags(args);
92
+ const client = flags.client;
93
+ if (!client) {
94
+ throw new Error(
95
+ `--client 가 필요합니다. 허용: ${CLIENTS.join(", ")}\n` +
96
+ `예: sh-ui mcp init --client claude-code`,
97
+ );
98
+ }
99
+ if (!CLIENTS.includes(client)) {
100
+ throw new Error(
101
+ `알 수 없는 클라이언트: '${client}'. 허용: ${CLIENTS.join(", ")}`,
102
+ );
103
+ }
104
+
105
+ const scope = flags.scope ?? defaultScope(client);
106
+ if (!["user", "project"].includes(scope)) {
107
+ throw new Error(`--scope 는 'user' 또는 'project' 여야 합니다.`);
108
+ }
109
+
110
+ const configPath = resolveConfigPath(client, scope, cwd);
111
+ const config = await readJsonOrEmpty(configPath);
112
+
113
+ if (!config.mcpServers || typeof config.mcpServers !== "object") {
114
+ config.mcpServers = {};
115
+ }
116
+
117
+ const before = config.mcpServers["sh-ui"];
118
+ config.mcpServers["sh-ui"] = SH_UI_ENTRY;
119
+
120
+ await mkdir(dirname(configPath), { recursive: true });
121
+ await writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf8");
122
+
123
+ const rel = relative(cwd, configPath);
124
+ const display = rel.startsWith("..") ? configPath : rel;
125
+ const verb = before ? "갱신" : "추가";
126
+ console.log(`✓ sh-ui MCP 엔트리 ${verb} → ${display}`);
127
+ console.log(` client: ${client} (scope: ${scope})`);
128
+ if (client === "claude-code" || client === "cursor") {
129
+ console.log(`\n다음 단계: ${client === "claude-code" ? "Claude Code" : "Cursor"} 를 재시작하면 sh-ui 툴이 활성화됩니다.`);
130
+ } else {
131
+ console.log(`\n다음 단계: Claude Desktop 을 종료 후 재시작하면 sh-ui 툴이 활성화됩니다.`);
132
+ }
133
+ }
134
+
135
+ /** --key=value / --key value 파싱 */
136
+ function parseFlags(args) {
137
+ const flags = {};
138
+ for (let i = 0; i < args.length; i++) {
139
+ const a = args[i];
140
+ if (!a.startsWith("--")) continue;
141
+ const eq = a.indexOf("=");
142
+ if (eq > -1) flags[a.slice(2, eq)] = a.slice(eq + 1);
143
+ else flags[a.slice(2)] = args[++i];
144
+ }
145
+ return flags;
146
+ }
package/src/mcp.mjs CHANGED
@@ -102,7 +102,7 @@ function resolveCwd(input) {
102
102
 
103
103
  export async function startMcpServer() {
104
104
  const server = new McpServer(
105
- { name: "sh-ui", version: "0.21.2" },
105
+ { name: "sh-ui", version: "0.22.1" },
106
106
  { capabilities: { tools: {} } },
107
107
  );
108
108