sh-ui-cli 0.56.4 → 0.58.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/bin/sh-ui.mjs +21 -2
  2. package/data/changelog/versions.json +32 -0
  3. package/package.json +1 -1
  4. package/src/api.d.ts +34 -0
  5. package/src/api.js +7 -0
  6. package/src/create/architectures/archSchema.js +69 -0
  7. package/src/create/architectures/flat.js +51 -0
  8. package/src/create/architectures/fsd.js +42 -0
  9. package/src/create/architectures/index.js +55 -0
  10. package/src/create/cli-args.js +8 -1
  11. package/src/create/generator.js +101 -32
  12. package/src/create/index.mjs +7 -0
  13. package/src/create/plugins/authJwt.js +14 -8
  14. package/src/create/plugins/nextIntl.js +53 -44
  15. package/src/create/plugins/pluginSchema.js +26 -10
  16. package/src/create/plugins/sentry.js +9 -5
  17. package/src/mcp.mjs +50 -0
  18. package/src/rename-app.mjs +321 -0
  19. package/templates/nextjs-app/_arch/flat/app/api/proxy/[...path]/route.ts +112 -0
  20. package/templates/nextjs-app/_arch/flat/app/layout.tsx +16 -0
  21. package/templates/nextjs-app/_arch/flat/components/common/PrefetchBoundary/index.tsx +35 -0
  22. package/templates/nextjs-app/_arch/flat/components/layouts/RootLayout.tsx +11 -0
  23. package/templates/nextjs-app/_arch/flat/components/providers/tanstack/QueryClientProvider.tsx +14 -0
  24. package/templates/nextjs-app/_arch/flat/lib/utils/getQueryClient.ts +14 -0
  25. package/templates/nextjs-app/_arch/flat/tsconfig.json +25 -0
  26. package/templates/nextjs-standalone/_arch/flat/app/api/proxy/[...path]/route.ts +112 -0
  27. package/templates/nextjs-standalone/_arch/flat/app/layout.tsx +16 -0
  28. package/templates/nextjs-standalone/_arch/flat/components/common/FallbackBoundary/index.tsx +89 -0
  29. package/templates/nextjs-standalone/_arch/flat/components/common/PrefetchBoundary/index.tsx +35 -0
  30. package/templates/nextjs-standalone/_arch/flat/components/layouts/RootLayout.tsx +11 -0
  31. package/templates/nextjs-standalone/_arch/flat/components/providers/GlobalProvider/index.tsx +23 -0
  32. package/templates/nextjs-standalone/_arch/flat/components/providers/index.tsx +1 -0
  33. package/templates/nextjs-standalone/_arch/flat/components/providers/tanstack/QueryClientProvider.tsx +14 -0
  34. package/templates/nextjs-standalone/_arch/flat/components/providers/tanstack/TanstackDevtoolsProvider.tsx +13 -0
  35. package/templates/nextjs-standalone/_arch/flat/components/providers/theme/ThemeProviders.tsx +12 -0
  36. package/templates/nextjs-standalone/_arch/flat/lib/api/apiTypes.ts +21 -0
  37. package/templates/nextjs-standalone/_arch/flat/lib/api/clientFetch.ts +40 -0
  38. package/templates/nextjs-standalone/_arch/flat/lib/api/error.ts +12 -0
  39. package/templates/nextjs-standalone/_arch/flat/lib/api/http.ts +13 -0
  40. package/templates/nextjs-standalone/_arch/flat/lib/api/observability.ts +20 -0
  41. package/templates/nextjs-standalone/_arch/flat/lib/api/queryClient.ts +30 -0
  42. package/templates/nextjs-standalone/_arch/flat/lib/api/serverFetch.ts +59 -0
  43. package/templates/nextjs-standalone/_arch/flat/lib/hooks/useAppMutation.ts +52 -0
  44. package/templates/nextjs-standalone/_arch/flat/lib/test/createTestQueryClient.ts +18 -0
  45. package/templates/nextjs-standalone/_arch/flat/lib/test/index.ts +2 -0
  46. package/templates/nextjs-standalone/_arch/flat/lib/test/renderWithProviders.tsx +40 -0
  47. package/templates/nextjs-standalone/_arch/flat/lib/utils/formatDate.ts +22 -0
  48. package/templates/nextjs-standalone/_arch/flat/lib/utils/formatPrice.ts +10 -0
  49. package/templates/nextjs-standalone/_arch/flat/lib/utils/getQueryClient.ts +14 -0
  50. package/templates/nextjs-standalone/_arch/flat/sh-ui.config.json +19 -0
  51. package/templates/nextjs-standalone/_arch/flat/tsconfig.json +41 -0
  52. package/templates/nextjs-standalone/_arch/fsd/src/app/providers/GlobalProvider/index.tsx +23 -0
  53. package/templates/nextjs-standalone/_arch/fsd/src/app/providers/index.tsx +1 -0
  54. package/templates/nextjs-standalone/_arch/fsd/src/app/providers/tanstack/TanstackDevtoolsProvider.tsx +13 -0
  55. package/templates/nextjs-standalone/_arch/fsd/src/app/providers/theme/ThemeProviders.tsx +12 -0
  56. package/templates/nextjs-standalone/_arch/fsd/src/entities/.gitkeep +0 -0
  57. package/templates/nextjs-standalone/_arch/fsd/src/features/.gitkeep +0 -0
  58. package/templates/nextjs-standalone/_arch/fsd/src/shared/api/.gitkeep +0 -0
  59. package/templates/nextjs-standalone/_arch/fsd/src/shared/api/apiTypes.ts +21 -0
  60. package/templates/nextjs-standalone/_arch/fsd/src/shared/api/clientFetch.ts +40 -0
  61. package/templates/nextjs-standalone/_arch/fsd/src/shared/api/error.ts +12 -0
  62. package/templates/nextjs-standalone/_arch/fsd/src/shared/api/http.ts +13 -0
  63. package/templates/nextjs-standalone/_arch/fsd/src/shared/api/observability.ts +20 -0
  64. package/templates/nextjs-standalone/_arch/fsd/src/shared/api/queryClient.ts +30 -0
  65. package/templates/nextjs-standalone/_arch/fsd/src/shared/api/serverFetch.ts +59 -0
  66. package/templates/nextjs-standalone/_arch/fsd/src/shared/config/.gitkeep +0 -0
  67. package/templates/nextjs-standalone/_arch/fsd/src/shared/hooks/.gitkeep +0 -0
  68. package/templates/nextjs-standalone/_arch/fsd/src/shared/hooks/useAppMutation.ts +52 -0
  69. package/templates/nextjs-standalone/_arch/fsd/src/shared/lib/formatDate.ts +22 -0
  70. package/templates/nextjs-standalone/_arch/fsd/src/shared/lib/formatPrice.ts +10 -0
  71. package/templates/nextjs-standalone/_arch/fsd/src/shared/lib/utils.ts +6 -0
  72. package/templates/nextjs-standalone/_arch/fsd/src/shared/model/.gitkeep +0 -0
  73. package/templates/nextjs-standalone/_arch/fsd/src/shared/styles/tokens.css +135 -0
  74. package/templates/nextjs-standalone/_arch/fsd/src/shared/test/createTestQueryClient.ts +18 -0
  75. package/templates/nextjs-standalone/_arch/fsd/src/shared/test/index.ts +2 -0
  76. package/templates/nextjs-standalone/_arch/fsd/src/shared/test/renderWithProviders.tsx +40 -0
  77. package/templates/nextjs-standalone/_arch/fsd/src/shared/ui/.gitkeep +0 -0
  78. package/templates/nextjs-standalone/_arch/fsd/src/shared/ui/FallbackBoundary/index.tsx +89 -0
  79. package/templates/nextjs-standalone/_arch/fsd/src/views/.gitkeep +0 -0
  80. package/templates/nextjs-standalone/_arch/fsd/src/widgets/.gitkeep +0 -0
  81. /package/templates/nextjs-app/{src/entities → _arch/flat/components/common}/.gitkeep +0 -0
  82. /package/templates/nextjs-app/{src/shared/ui → _arch/flat/components/common}/FallbackBoundary/index.tsx +0 -0
  83. /package/templates/nextjs-app/{src/app → _arch/flat/components}/providers/GlobalProvider/index.tsx +0 -0
  84. /package/templates/nextjs-app/{src/app → _arch/flat/components}/providers/index.tsx +0 -0
  85. /package/templates/nextjs-app/{src/app → _arch/flat/components}/providers/tanstack/TanstackDevtoolsProvider.tsx +0 -0
  86. /package/templates/nextjs-app/{src/app → _arch/flat/components}/providers/theme/ThemeProviders.tsx +0 -0
  87. /package/templates/nextjs-app/{src/features → _arch/flat/lib/api}/.gitkeep +0 -0
  88. /package/templates/nextjs-app/{src/shared → _arch/flat/lib}/api/apiTypes.ts +0 -0
  89. /package/templates/nextjs-app/{src/shared → _arch/flat/lib}/api/clientFetch.ts +0 -0
  90. /package/templates/nextjs-app/{src/shared → _arch/flat/lib}/api/error.ts +0 -0
  91. /package/templates/nextjs-app/{src/shared → _arch/flat/lib}/api/http.ts +0 -0
  92. /package/templates/nextjs-app/{src/shared → _arch/flat/lib}/api/observability.ts +0 -0
  93. /package/templates/nextjs-app/{src/shared → _arch/flat/lib}/api/queryClient.ts +0 -0
  94. /package/templates/nextjs-app/{src/shared → _arch/flat/lib}/api/serverFetch.ts +0 -0
  95. /package/templates/nextjs-app/{src/shared/api → _arch/flat/lib/config}/.gitkeep +0 -0
  96. /package/templates/nextjs-app/{src/shared/config → _arch/flat/lib/hooks}/.gitkeep +0 -0
  97. /package/templates/nextjs-app/{src/shared → _arch/flat/lib}/hooks/useAppMutation.ts +0 -0
  98. /package/templates/nextjs-app/{src/shared → _arch/flat/lib}/test/createTestQueryClient.ts +0 -0
  99. /package/templates/nextjs-app/{src/shared → _arch/flat/lib}/test/index.ts +0 -0
  100. /package/templates/nextjs-app/{src/shared → _arch/flat/lib}/test/renderWithProviders.tsx +0 -0
  101. /package/templates/nextjs-app/{src/shared/hooks → _arch/flat/lib/utils}/.gitkeep +0 -0
  102. /package/templates/nextjs-app/{src/shared/lib → _arch/flat/lib/utils}/formatDate.ts +0 -0
  103. /package/templates/nextjs-app/{src/shared/lib → _arch/flat/lib/utils}/formatPrice.ts +0 -0
  104. /package/templates/nextjs-app/{app → _arch/fsd/app}/api/proxy/[...path]/route.ts +0 -0
  105. /package/templates/nextjs-app/{app → _arch/fsd/app}/layout.tsx +0 -0
  106. /package/templates/nextjs-app/{src → _arch/fsd/src}/app/layouts/RootLayout.tsx +0 -0
  107. /package/templates/{nextjs-standalone → nextjs-app/_arch/fsd}/src/app/providers/GlobalProvider/index.tsx +0 -0
  108. /package/templates/{nextjs-standalone → nextjs-app/_arch/fsd}/src/app/providers/index.tsx +0 -0
  109. /package/templates/nextjs-app/{src → _arch/fsd/src}/app/providers/tanstack/QueryClientProvider.tsx +0 -0
  110. /package/templates/{nextjs-standalone → nextjs-app/_arch/fsd}/src/app/providers/tanstack/TanstackDevtoolsProvider.tsx +0 -0
  111. /package/templates/{nextjs-standalone → nextjs-app/_arch/fsd}/src/app/providers/theme/ThemeProviders.tsx +0 -0
  112. /package/templates/nextjs-app/{src/shared/lib → _arch/fsd/src/entities}/.gitkeep +0 -0
  113. /package/templates/nextjs-app/{src/shared/model → _arch/fsd/src/features}/.gitkeep +0 -0
  114. /package/templates/nextjs-app/{src/shared/ui → _arch/fsd/src/shared/api}/.gitkeep +0 -0
  115. /package/templates/{nextjs-standalone → nextjs-app/_arch/fsd}/src/shared/api/apiTypes.ts +0 -0
  116. /package/templates/{nextjs-standalone → nextjs-app/_arch/fsd}/src/shared/api/clientFetch.ts +0 -0
  117. /package/templates/{nextjs-standalone → nextjs-app/_arch/fsd}/src/shared/api/error.ts +0 -0
  118. /package/templates/{nextjs-standalone → nextjs-app/_arch/fsd}/src/shared/api/http.ts +0 -0
  119. /package/templates/{nextjs-standalone → nextjs-app/_arch/fsd}/src/shared/api/observability.ts +0 -0
  120. /package/templates/{nextjs-standalone → nextjs-app/_arch/fsd}/src/shared/api/queryClient.ts +0 -0
  121. /package/templates/{nextjs-standalone → nextjs-app/_arch/fsd}/src/shared/api/serverFetch.ts +0 -0
  122. /package/templates/nextjs-app/{src/views → _arch/fsd/src/shared/config}/.gitkeep +0 -0
  123. /package/templates/nextjs-app/{src/widgets → _arch/fsd/src/shared/hooks}/.gitkeep +0 -0
  124. /package/templates/{nextjs-standalone → nextjs-app/_arch/fsd}/src/shared/hooks/useAppMutation.ts +0 -0
  125. /package/templates/{nextjs-standalone/src/entities → nextjs-app/_arch/fsd/src/shared/lib}/.gitkeep +0 -0
  126. /package/templates/{nextjs-standalone → nextjs-app/_arch/fsd}/src/shared/lib/formatDate.ts +0 -0
  127. /package/templates/{nextjs-standalone → nextjs-app/_arch/fsd}/src/shared/lib/formatPrice.ts +0 -0
  128. /package/templates/nextjs-app/{src → _arch/fsd/src}/shared/lib/getQueryClient.ts +0 -0
  129. /package/templates/{nextjs-standalone/src/features → nextjs-app/_arch/fsd/src/shared/model}/.gitkeep +0 -0
  130. /package/templates/{nextjs-standalone → nextjs-app/_arch/fsd}/src/shared/test/createTestQueryClient.ts +0 -0
  131. /package/templates/{nextjs-standalone → nextjs-app/_arch/fsd}/src/shared/test/index.ts +0 -0
  132. /package/templates/{nextjs-standalone → nextjs-app/_arch/fsd}/src/shared/test/renderWithProviders.tsx +0 -0
  133. /package/templates/{nextjs-standalone/src/shared/api → nextjs-app/_arch/fsd/src/shared/ui}/.gitkeep +0 -0
  134. /package/templates/{nextjs-standalone → nextjs-app/_arch/fsd}/src/shared/ui/FallbackBoundary/index.tsx +0 -0
  135. /package/templates/nextjs-app/{src → _arch/fsd/src}/shared/ui/PrefetchBoundary/index.tsx +0 -0
  136. /package/templates/{nextjs-standalone/src/shared/config → nextjs-app/_arch/fsd/src/views}/.gitkeep +0 -0
  137. /package/templates/{nextjs-standalone/src/shared/hooks → nextjs-app/_arch/fsd/src/widgets}/.gitkeep +0 -0
  138. /package/templates/nextjs-app/{tsconfig.json → _arch/fsd/tsconfig.json} +0 -0
  139. /package/templates/nextjs-standalone/{src/shared/model → _arch/flat/components/common}/.gitkeep +0 -0
  140. /package/templates/nextjs-standalone/{src/shared/ui → _arch/flat/lib/api}/.gitkeep +0 -0
  141. /package/templates/nextjs-standalone/{src/views → _arch/flat/lib/config}/.gitkeep +0 -0
  142. /package/templates/nextjs-standalone/{src/widgets → _arch/flat/lib/hooks}/.gitkeep +0 -0
  143. /package/templates/nextjs-standalone/{src/shared → _arch/flat/lib}/styles/tokens.css +0 -0
  144. /package/templates/nextjs-standalone/{src/shared/lib → _arch/flat/lib/utils}/utils.ts +0 -0
  145. /package/templates/nextjs-standalone/{app → _arch/fsd/app}/api/proxy/[...path]/route.ts +0 -0
  146. /package/templates/nextjs-standalone/{app → _arch/fsd/app}/layout.tsx +0 -0
  147. /package/templates/nextjs-standalone/{sh-ui.config.json → _arch/fsd/sh-ui.config.json} +0 -0
  148. /package/templates/nextjs-standalone/{src → _arch/fsd/src}/app/layouts/RootLayout.tsx +0 -0
  149. /package/templates/nextjs-standalone/{src → _arch/fsd/src}/app/providers/tanstack/QueryClientProvider.tsx +0 -0
  150. /package/templates/nextjs-standalone/{src → _arch/fsd/src}/shared/lib/getQueryClient.ts +0 -0
  151. /package/templates/nextjs-standalone/{src → _arch/fsd/src}/shared/ui/PrefetchBoundary/index.tsx +0 -0
  152. /package/templates/nextjs-standalone/{tsconfig.json → _arch/fsd/tsconfig.json} +0 -0
package/bin/sh-ui.mjs CHANGED
@@ -15,17 +15,21 @@ const usage = `사용법:
15
15
  특수값: tokens → 설정 기반 토큰 파일 생성
16
16
  sh-ui list 현재 설치된 컴포넌트 목록 표시
17
17
  sh-ui remove <component...> 설치된 컴포넌트 파일 삭제
18
+ sh-ui rename-app <old> <new> monorepo 의 앱 이름 일괄 변경
19
+ (apps/<old>/, packages/ui/ui-apps/ui-<old>/
20
+ 디렉토리 + 모든 import/path 패턴)
18
21
  sh-ui mcp MCP 서버(stdio) 시작 — IDE-내 AI용
19
22
  sh-ui mcp init --client <name> IDE MCP 설정 파일에 sh-ui 엔트리 자동 추가
20
23
  (claude-code | cursor | claude-desktop)
21
24
  옵션:
22
- --skip-install (add) 외부 패키지 자동 설치 생략
25
+ --skip-install (add, rename-app) 외부 패키지 자동 설치 생략
23
26
  --diff (add) 파일을 쓰지 않고 변경 내역만 출력
24
27
  --force (add) 기존 파일을 모두 덮어쓰기 (prompt 없음)
25
28
  (remove) 사용자가 수정한 파일도 삭제
26
29
  --keep (add) 기존 파일을 모두 유지 (prompt 없음)
27
30
  --all (list) 설치되지 않은 컴포넌트까지 표시
28
- --dry-run (remove) 삭제 대상만 출력하고 실행 안 함
31
+ --dry-run (remove, rename-app) 변경 대상만 출력하고 실행 안 함
32
+ --yes (rename-app) 대화형 확인 생략
29
33
  `;
30
34
 
31
35
  try {
@@ -74,6 +78,21 @@ try {
74
78
  }
75
79
  break;
76
80
  }
81
+ case "rename-app": {
82
+ const yes = rest.includes("--yes");
83
+ const dryRun = rest.includes("--dry-run");
84
+ const skipInstall = rest.includes("--skip-install");
85
+ const positional = rest.filter((a) => !a.startsWith("--"));
86
+ if (positional.length < 2) {
87
+ console.error("에러: rename-app 은 <old> <new> 두 인자가 필요합니다.\n");
88
+ console.error(usage);
89
+ process.exit(1);
90
+ }
91
+ const [oldName, newName] = positional;
92
+ const { renameApp } = await import("../src/rename-app.mjs");
93
+ await renameApp({ cwd: process.cwd(), oldName, newName, yes, dryRun, skipInstall });
94
+ break;
95
+ }
77
96
  case "remove":
78
97
  case "rm": {
79
98
  const force = rest.includes("--force");
@@ -2,6 +2,38 @@
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.58.0",
7
+ "date": "2026-05-05",
8
+ "title": "아키텍처 (arch) 1급 시민 도입 + flat 추가",
9
+ "type": "minor",
10
+ "highlights": [
11
+ "**`--arch` CLI 플래그 + MCP `arch` 파라미터 신설** — 프로젝트의 *모양* (폴더 구조, import alias 컨벤션, tsconfig paths) 을 1급 개념으로 분리. 플러그인과 직교하는 별도 축 — 사용자는 arch 한 개 + 플러그인 N 개를 동시에 선택. 미지정 시 기본 `fsd` (v0.57 까지의 동작과 동일).",
12
+ "**아키텍처 디스크립터 모델** — `src/create/architectures/{fsd,flat}.js` 가 8개 논리 키 (`layouts`, `providers`, `api`, `config`, `hooks`, `utils`, `ui`, `test`) 의 fs 경로 / import alias / tsconfig paths 를 선언. 플러그인은 하드코딩된 `src/shared/api/...` 대신 `arch.paths.api` / `arch.aliases.api` 를 조회해 산출물 emit — 한 번 작성된 플러그인이 모든 arch 에서 동작.",
13
+ "**`flat` 아키텍처 추가** — 슬라이스 (entities/features/widgets/views) 없는 vanilla Next 관용 구조 (`lib/api`, `lib/utils`, `components/layouts`, `components/providers`, `components/common`). 토이 프로젝트, 데모, 일회성 도구처럼 FSD 6 레이어가 과한 경우용. tsconfig paths 는 카테고리별 scoped (`@/lib/*`, `@/components/*`, `@/app/*`) — FSD 의 catch-all `@/*` 와 의도적으로 다른 컨벤션.",
14
+ "**플러그인 3개 (sentry/next-intl/auth-jwt) 모두 arch-aware 로 리팩토링** — files / transforms / preExport / providerImports 가 `(arch) => ...` 함수형으로 전환. 회귀 가드: FSD 디스크립터 값이 v0.57 까지의 하드코딩과 1:1 일치하므로 FSD 사용자 입장 변화 0.",
15
+ "**Cross-platform 대비** — 디스크립터에 `platforms: ['next' | 'flutter']` 필드 박힘. 현재 fsd/flat 둘 다 next 만 지원하지만, 향후 `flutter-clean` / `flutter-bloc` 같은 Flutter arch 추가 시 generator/CLI 수정 0 — 디스크립터만 추가하면 됨.",
16
+ "**베이스 템플릿 재구조화** — `templates/nextjs-app` / `templates/nextjs-standalone` 이 arch-neutral 베이스 + `_arch/{fsd,flat}/` 오버레이로 분리. generator 가 base 카피 후 선택된 arch 의 오버레이를 머지. 새 arch 추가 절차는 `packages/cli/ARCHITECTURE.md` 에 문서화.",
17
+ "**docs 사이드바에 Architectures 그룹 신설** — `/architectures` 허브 + `/architectures/fsd` + `/architectures/flat` 페이지. arch 선택 가이드 + 폴더 구조 시각화 + FSD ↔ flat 마이그레이션 절차.",
18
+ "**스모크 매트릭스 9개 시나리오 추가** — flat × {none, sentry, next-intl, auth-jwt, all 3} + flat monorepo 1개 + FSD 회귀 가드 2개. 핵심 검증: flat 에 src/ 부재, 어떤 .ts/.tsx 파일에도 `@/src/...` import 누수 없음, 플러그인 산출물이 arch-correct 경로로 떨어짐."
19
+ ],
20
+ "url": "https://github.com/sanghyeonKim0201/sh-ui/releases/tag/v0.58.0"
21
+ },
22
+ {
23
+ "version": "0.57.0",
24
+ "date": "2026-05-04",
25
+ "title": "rename-app — monorepo 앱 이름 일괄 변경 명령어 + MCP 툴",
26
+ "type": "minor",
27
+ "highlights": [
28
+ "**`sh-ui rename-app <old> <new>`** — monorepo 의 앱 이름을 한 번에 변경. `apps/<old>/`, `packages/ui/ui-apps/ui-<old>/` 두 디렉토리 이동 + `@workspace/ui-<old>` / `apps/<old>` / `--filter <old>` / `--app <old>` 같은 컨텍스트 묶인 패턴만 치환. 사용자 코드, package.json name, tsconfig paths, Dockerfile WORKDIR, next.config transpilePackages, sh-ui.config aliases, README, `.github/workflows/*.yml`, 루트 package.json scripts 까지 자동 갱신.",
29
+ "**MCP 툴 `sh_ui_rename_app`** — IDE-내 AI 가 \"apps/web 을 dashboard 로 바꿔줘\" 류 요청 받으면 자동 호출. `dryRun: true` 로 변경 매트릭스 미리보기 후 실행 권장. instructions 블록에도 가이드 추가.",
30
+ "**옵션** — `--dry-run` (변경 매트릭스만 출력) / `--yes` (대화형 확인 생략) / `--skip-install` (마지막 `pnpm install` 생략).",
31
+ "**false-positive 방지** — bare 단어(예: `web`)는 절대 치환하지 않음. 컨텍스트(슬래시, 따옴표, 백틱, 공백, 개행) 로 묶인 패턴만 매치 — `core-web-vitals` (ESLint) / `safari-web-extension` (Sentry) 같은 생태계 상수는 보존.",
32
+ "**lockfile 자동 재생성** — 마지막 단계에서 `pnpm install` 실행해 workspace 링크 갱신. 실패해도 friendly 안내 메시지로 사용자가 수동 실행 가능.",
33
+ "**fix — Next 16 호환: `app/layout.tsx` html/body 누락 에러 해소** — next-intl 플러그인이 `app/layout.tsx` 를 패스스루(`return children`) 로 남기던 방식이 Next 16 의 \"Missing <html> and <body> tags in the root layout\" 런타임 에러를 일으키던 문제 수정. 이제 `app/layout.tsx` 를 통째로 `app/[locale]/layout.tsx` 로 이동(globals.css side-effect import 보존)하고 본체만 locale-aware 버전으로 교체 — `app/layout.tsx` 가 사라지면서 `[locale]/layout.tsx` 가 Next 의 root layout 으로 인식되고 그 안의 `RootLayout` (html/body 보유) 이 정상 적용. smoke matrix 에 회귀 가드 추가."
34
+ ],
35
+ "url": "https://github.com/sanghyeonKim0201/sh-ui/releases/tag/v0.57.0"
36
+ },
5
37
  {
6
38
  "version": "0.56.4",
7
39
  "date": "2026-05-04",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sh-ui-cli",
3
- "version": "0.56.4",
3
+ "version": "0.58.0",
4
4
  "description": "sh-ui CLI — 프로젝트 스캐폴드(create) + 컴포넌트 추가(add/list/remove) + IDE-내 AI용 MCP 서버",
5
5
  "license": "MIT",
6
6
  "repository": {
package/src/api.d.ts CHANGED
@@ -56,6 +56,40 @@ export type PluginManifest = {
56
56
 
57
57
  export const allPlugins: readonly PluginManifest[];
58
58
 
59
+ /* ─────── 아키텍처 (arch) ─────── */
60
+
61
+ /** 논리 키 셋 — 모든 arch 디스크립터가 동일하게 노출하는 카테고리. */
62
+ export type ArchPathKey =
63
+ | 'layouts'
64
+ | 'providers'
65
+ | 'api'
66
+ | 'config'
67
+ | 'hooks'
68
+ | 'utils'
69
+ | 'ui'
70
+ | 'test';
71
+
72
+ export type ArchManifest = {
73
+ /** kebab-case 식별자 (예: "fsd", "flat"). */
74
+ name: string;
75
+ label: string;
76
+ description: string;
77
+ /** 이 arch 가 적용 가능한 플랫폼들. */
78
+ platforms: readonly CreatePlatform[];
79
+ /** 논리 키 → 파일시스템 경로 (앱 루트 기준). */
80
+ paths: Record<ArchPathKey, string>;
81
+ /** 논리 키 → import alias prefix (TS 코드 emit 시). */
82
+ aliases: Record<ArchPathKey, string>;
83
+ /** tsconfig.json 의 paths 블록에 그대로 들어갈 객체. */
84
+ tsconfigPaths: Record<string, string[]>;
85
+ };
86
+
87
+ export const allArchitectures: readonly ArchManifest[];
88
+ export const DEFAULT_ARCH: 'fsd';
89
+ export function getArchByName(name: string): ArchManifest;
90
+ export function getArchesForPlatform(platform: CreatePlatform): readonly ArchManifest[];
91
+ export function isKnownArch(name: string): boolean;
92
+
59
93
  /* ─────── 테마 프리셋 ─────── */
60
94
 
61
95
  export type ThemePresetName = 'neutral' | 'slate' | 'rose' | 'emerald' | 'violet';
package/src/api.js CHANGED
@@ -23,4 +23,11 @@ export {
23
23
  } from './constants.js';
24
24
 
25
25
  export { allPlugins } from './create/plugins/index.js';
26
+ export {
27
+ allArchitectures,
28
+ DEFAULT_ARCH,
29
+ getArchByName,
30
+ getArchesForPlatform,
31
+ isKnownArch,
32
+ } from './create/architectures/index.js';
26
33
  export { THEME_PRESETS, THEME_PRESET_NAMES } from './create/theme/presets.js';
@@ -0,0 +1,69 @@
1
+ import { z } from 'zod';
2
+
3
+ /**
4
+ * 아키텍처 디스크립터 스키마.
5
+ * architectures/index.js 로딩 시 모든 디스크립터를 이 스키마로 validate 한다.
6
+ *
7
+ * arch 는 플러그인과 별개의 1급 개념이다 — 프로젝트의 *모양* (폴더 구조,
8
+ * import alias 컨벤션) 을 정의하고, 플러그인은 이 디스크립터의 논리 키
9
+ * (`paths.api`, `aliases.providers` 등) 를 조회해 자신의 산출물을 emit 한다.
10
+ *
11
+ * 디자인 원칙:
12
+ * - 논리 키는 arch 중립적 — `layouts`, `providers`, `api`, `config`, `hooks`,
13
+ * `utils`, `ui`, `test`. FSD 의 `shared/*` 같은 슬라이스 명칭이 키에 누수되지 않게.
14
+ * - 모든 arch 는 **동일한 키 셋** 을 노출해야 함 (플러그인이 안전하게 조회).
15
+ * 누락 시 스키마가 즉시 거부.
16
+ * - `paths` 는 fs 경로 (앱 루트 기준 상대), `aliases` 는 import 시 사용할 prefix.
17
+ * 둘은 1:1 대응 (paths.foo 디렉토리 = aliases.foo 가 가리키는 곳).
18
+ * - `tsconfigPaths` 는 tsconfig.json 의 paths 블록에 그대로 들어가는 객체.
19
+ * arch 마다 `@/*` 한 줄일 수도, 더 세분화될 수도 있음.
20
+ */
21
+
22
+ const PathKeys = z.object({
23
+ layouts: z.string().min(1),
24
+ providers: z.string().min(1),
25
+ api: z.string().min(1),
26
+ config: z.string().min(1),
27
+ hooks: z.string().min(1),
28
+ utils: z.string().min(1),
29
+ ui: z.string().min(1),
30
+ test: z.string().min(1),
31
+ });
32
+
33
+ export const ArchSchema = z.object({
34
+ name: z.string().regex(/^[a-z][a-z0-9-]*$/, {
35
+ message: 'Architecture name must be lowercase kebab-case (e.g., "fsd", "flat")',
36
+ }),
37
+ label: z.string().min(1),
38
+ description: z.string().min(1),
39
+
40
+ // 이 arch 가 적용 가능한 플랫폼들. CLI 에서 platform/arch 조합이 호환되는지 검증.
41
+ // 예: fsd/flat 은 ['next'], 미래에 추가될 flutter-clean 은 ['flutter'].
42
+ // 같은 이름의 arch 가 두 플랫폼 모두 지원하는 케이스도 가능 (예: 'flat' 가 양쪽).
43
+ platforms: z.array(z.enum(['next', 'flutter'])).min(1),
44
+
45
+ // 논리 키 → 파일시스템 경로 (앱 루트 기준 상대)
46
+ paths: PathKeys,
47
+
48
+ // 논리 키 → import alias prefix (TS 코드에서 emit 할 때 사용)
49
+ aliases: PathKeys,
50
+
51
+ // tsconfig.json 의 paths 필드에 그대로 들어가는 객체
52
+ // 예: FSD 는 { '@/*': ['./*'] }, flat 은 더 세분화
53
+ // Flutter arch 의 경우 이 필드는 사실상 무시됨 (Dart 는 tsconfig 가 없음).
54
+ tsconfigPaths: z.record(z.string(), z.array(z.string())),
55
+ });
56
+
57
+ export function validateArchitectures(architectures) {
58
+ for (const arch of architectures) {
59
+ const result = ArchSchema.safeParse(arch);
60
+ if (!result.success) {
61
+ const issues = result.error.issues
62
+ .map((i) => ` - ${i.path.join('.')}: ${i.message}`)
63
+ .join('\n');
64
+ throw new Error(
65
+ `Invalid architecture descriptor "${arch?.name ?? '(unknown)'}":\n${issues}`,
66
+ );
67
+ }
68
+ }
69
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Flat 아키텍처 디스크립터.
3
+ *
4
+ * 슬라이스 없이 vanilla Next.js 관용에 가까운 폴더 구조 — `lib/` (유틸/설정),
5
+ * `components/` (UI/레이아웃/프로바이더). 토이 프로젝트, 데모, 일회성 도구처럼
6
+ * FSD 의 6 레이어가 과한 경우에 적합.
7
+ *
8
+ * tsconfig 의 `paths` 는 FSD 의 전역 `@/*` 와 다르게 *카테고리별 scoped alias* 를
9
+ * 쓴다 (`@/lib/*`, `@/components/*`, `@/app/*`). 이유는 두 가지:
10
+ *
11
+ * 1. flat 은 import 가 짧은 게 강점 — `@/lib/api/x` 처럼 카테고리가 alias 에 박혀
12
+ * 있으면 한 번 보고 어디 파일인지 식별. 전역 `@/*` 보다 가독성 ↑.
13
+ * 2. 미래에 다른 arch 를 추가할 때 alias 컨벤션 차이가 arch 정체성의 일부가
14
+ * 될 수 있도록 — clean/MVC 등은 또 자기만의 alias 패턴을 가질 수 있다.
15
+ */
16
+ export const flatArch = {
17
+ name: 'flat',
18
+ label: 'Flat',
19
+ description:
20
+ '슬라이스 없는 관용적 Next.js 구조 (lib/components/app). 작은 프로젝트, 데모, 일회성 도구에 적합.',
21
+ platforms: ['next'],
22
+
23
+ paths: {
24
+ layouts: 'components/layouts',
25
+ providers: 'components/providers',
26
+ api: 'lib/api',
27
+ config: 'lib/config',
28
+ hooks: 'lib/hooks',
29
+ utils: 'lib/utils',
30
+ ui: 'components/common',
31
+ test: 'lib/test',
32
+ },
33
+
34
+ aliases: {
35
+ layouts: '@/components/layouts',
36
+ providers: '@/components/providers',
37
+ api: '@/lib/api',
38
+ config: '@/lib/config',
39
+ hooks: '@/lib/hooks',
40
+ utils: '@/lib/utils',
41
+ ui: '@/components/common',
42
+ test: '@/lib/test',
43
+ },
44
+
45
+ // Scoped alias — 카테고리별로 명시. FSD 의 catch-all `@/*` 와 의도적으로 다름.
46
+ tsconfigPaths: {
47
+ '@/lib/*': ['./lib/*'],
48
+ '@/components/*': ['./components/*'],
49
+ '@/app/*': ['./app/*'],
50
+ },
51
+ };
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Feature-Sliced Design 아키텍처 디스크립터.
3
+ *
4
+ * sh-ui 의 디폴트 arch — 슬라이스 (entities, features, widgets, views, shared, app) 기반.
5
+ * 플러그인이 emit 하는 산출물의 fs 경로/import alias 를 결정한다.
6
+ *
7
+ * 본 디스크립터의 값은 sh-ui v0.57 까지의 하드코딩된 경로와 1:1 일치 —
8
+ * 즉 v0.58 에서 FSD 사용자 입장 변화 0 이어야 한다 (회귀 가드는 smoke matrix 가).
9
+ */
10
+ export const fsdArch = {
11
+ name: 'fsd',
12
+ label: 'Feature-Sliced Design',
13
+ description:
14
+ '슬라이스 기반 폴더 구조 (entities/features/widgets/views/shared/app). 도메인이 큰 프로젝트에 적합.',
15
+ platforms: ['next'],
16
+
17
+ paths: {
18
+ layouts: 'src/app/layouts',
19
+ providers: 'src/app/providers',
20
+ api: 'src/shared/api',
21
+ config: 'src/shared/config',
22
+ hooks: 'src/shared/hooks',
23
+ utils: 'src/shared/lib',
24
+ ui: 'src/shared/ui',
25
+ test: 'src/shared/test',
26
+ },
27
+
28
+ aliases: {
29
+ layouts: '@/src/app/layouts',
30
+ providers: '@/src/app/providers',
31
+ api: '@/src/shared/api',
32
+ config: '@/src/shared/config',
33
+ hooks: '@/src/shared/hooks',
34
+ utils: '@/src/shared/lib',
35
+ ui: '@/src/shared/ui',
36
+ test: '@/src/shared/test',
37
+ },
38
+
39
+ tsconfigPaths: {
40
+ '@/*': ['./*'],
41
+ },
42
+ };
@@ -0,0 +1,55 @@
1
+ import { fsdArch } from './fsd.js';
2
+ import { flatArch } from './flat.js';
3
+ import { validateArchitectures } from './archSchema.js';
4
+
5
+ export const allArchitectures = [fsdArch, flatArch];
6
+
7
+ // 모듈 로드 시점에 모든 arch 디스크립터를 schema 로 검증.
8
+ // 누락된 키, 잘못된 형태가 있으면 즉시 에러.
9
+ validateArchitectures(allArchitectures);
10
+
11
+ export const DEFAULT_ARCH = 'fsd';
12
+
13
+ export function getArchChoices() {
14
+ return allArchitectures.map((a) => ({
15
+ name: `${a.label} — ${a.description}`,
16
+ value: a.name,
17
+ }));
18
+ }
19
+
20
+ export function getArchByName(name) {
21
+ const arch = allArchitectures.find((a) => a.name === name);
22
+ if (!arch) {
23
+ const known = allArchitectures.map((a) => a.name).join(', ');
24
+ throw new Error(`Unknown architecture "${name}". Available: ${known}`);
25
+ }
26
+ return arch;
27
+ }
28
+
29
+ export function isKnownArch(name) {
30
+ return allArchitectures.some((a) => a.name === name);
31
+ }
32
+
33
+ /**
34
+ * platform 에서 사용 가능한 arch 만 필터링.
35
+ * 예: 'next' → fsd, flat / 'flutter' → (현재 없음, 향후 flutter-* 추가 시 노출).
36
+ */
37
+ export function getArchesForPlatform(platform) {
38
+ return allArchitectures.filter((a) => a.platforms.includes(platform));
39
+ }
40
+
41
+ /**
42
+ * 주어진 arch 가 platform 과 호환되는지 검증. 호환 안 되면 친절한 에러.
43
+ * generator/cli-args 양쪽에서 호출.
44
+ */
45
+ export function assertArchPlatformCompat(archName, platform) {
46
+ const arch = getArchByName(archName);
47
+ if (!arch.platforms.includes(platform)) {
48
+ const supported = getArchesForPlatform(platform).map((a) => a.name).join(', ') || '(없음)';
49
+ throw new Error(
50
+ `--arch=${archName} 는 platform=${platform} 와 호환되지 않습니다. ` +
51
+ `${platform} 에서 사용 가능: ${supported}`,
52
+ );
53
+ }
54
+ return arch;
55
+ }
@@ -5,12 +5,14 @@ import {
5
5
  CSS_FRAMEWORKS_PLANNED,
6
6
  } from '../constants.js';
7
7
  import { allPlugins } from './plugins/index.js';
8
+ import { allArchitectures } from './architectures/index.js';
8
9
 
9
10
  const VALID_PLATFORMS = CREATE_PLATFORMS;
10
11
  const VALID_STRUCTURES = CREATE_STRUCTURES;
11
12
  const VALID_PLUGINS = allPlugins.map((p) => p.name);
13
+ const VALID_ARCHES = allArchitectures.map((a) => a.name);
12
14
 
13
- const VALUE_FLAGS = ['platform', 'structure', 'plugins', 'theme', 'app', 'css'];
15
+ const VALUE_FLAGS = ['platform', 'structure', 'plugins', 'theme', 'app', 'css', 'arch'];
14
16
  const BOOL_FLAGS = ['yes', 'help', 'dry-run'];
15
17
 
16
18
  const SUBCOMMANDS = ['add-app', 'add-component'];
@@ -68,6 +70,11 @@ export const parseArgs = (argv) => {
68
70
  if (name === 'structure' && !VALID_STRUCTURES.includes(value)) {
69
71
  throw new Error(`--structure 는 ${VALID_STRUCTURES.join('/')} 중 하나여야 함 (받은 값: ${value})`);
70
72
  }
73
+ if (name === 'arch' && !VALID_ARCHES.includes(value)) {
74
+ throw new Error(
75
+ `--arch 는 ${VALID_ARCHES.join('/')} 중 하나여야 함 (받은 값: ${value})`,
76
+ );
77
+ }
71
78
  if (name === 'css' && !CSS_FRAMEWORKS_SUPPORTED.includes(value)) {
72
79
  // planned 값은 '곧 옵니다' 신호로 분기 — 사용자 의도가 더 명확히 전달.
73
80
  if (CSS_FRAMEWORKS_PLANNED.includes(value)) {