spec-to-ui 0.1.0 → 0.2.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 (143) hide show
  1. package/LICENSE +2 -0
  2. package/README.ko.md +129 -0
  3. package/README.md +96 -29
  4. package/RELEASE.md +26 -0
  5. package/THIRD-PARTY.md +14 -0
  6. package/bin/cli.mjs +70 -91
  7. package/bundle-manifest.json +140 -0
  8. package/package.json +40 -8
  9. package/plugin.json +16 -0
  10. package/skills/design-taste-frontend/LICENSE +21 -0
  11. package/skills/design-taste-frontend/README.md +81 -0
  12. package/skills/design-taste-frontend/SKILL.md +546 -0
  13. package/skills/design-taste-frontend/assets/app.css +216 -0
  14. package/skills/design-taste-frontend/assets/base.css +105 -0
  15. package/skills/design-taste-frontend/assets/base.js +37 -0
  16. package/skills/design-taste-frontend/assets/charts.js +619 -0
  17. package/skills/design-taste-frontend/assets/scenes/stage.css +28 -0
  18. package/skills/design-taste-frontend/assets/scenes/stage.js +134 -0
  19. package/skills/design-taste-frontend/evals/evals.json +76 -0
  20. package/skills/design-taste-frontend/library/app.json +1327 -0
  21. package/skills/design-taste-frontend/library/directions.json +1078 -0
  22. package/skills/design-taste-frontend/library/heroes.json +1086 -0
  23. package/skills/design-taste-frontend/library/motions.json +92 -0
  24. package/skills/design-taste-frontend/library/palettes.json +1485 -0
  25. package/skills/design-taste-frontend/library/photos.json +7240 -0
  26. package/skills/design-taste-frontend/library/sections.json +4429 -0
  27. package/skills/design-taste-frontend/library/sites.json +8350 -0
  28. package/skills/design-taste-frontend/library/type.json +2024 -0
  29. package/skills/design-taste-frontend/references/app-screens.md +1199 -0
  30. package/skills/design-taste-frontend/references/colour.md +141 -0
  31. package/skills/design-taste-frontend/references/copy.md +177 -0
  32. package/skills/design-taste-frontend/references/critique.md +182 -0
  33. package/skills/design-taste-frontend/references/imagery.md +791 -0
  34. package/skills/design-taste-frontend/references/layout.md +292 -0
  35. package/skills/design-taste-frontend/references/motion.md +980 -0
  36. package/skills/design-taste-frontend/references/research.md +356 -0
  37. package/skills/design-taste-frontend/references/scenes.md +174 -0
  38. package/skills/design-taste-frontend/references/tells.md +115 -0
  39. package/skills/design-taste-frontend/references/type.md +170 -0
  40. package/skills/design-taste-frontend/scripts/assets.mjs +865 -0
  41. package/skills/design-taste-frontend/scripts/draw.mjs +584 -0
  42. package/skills/design-taste-frontend/scripts/inspect.mjs +308 -0
  43. package/skills/design-taste-frontend/scripts/lib/app-draw.mjs +203 -0
  44. package/skills/design-taste-frontend/scripts/lib/cdp.mjs +248 -0
  45. package/skills/design-taste-frontend/scripts/lib/inspection-files.mjs +9 -0
  46. package/skills/design-taste-frontend/scripts/lib/probe.mjs +929 -0
  47. package/skills/design-taste-frontend/scripts/lib/rules.mjs +243 -0
  48. package/skills/design-taste-frontend/scripts/lint.mjs +119 -0
  49. package/skills/design-taste-frontend/scripts/scenes.mjs +124 -0
  50. package/skills/spec-to-ui/SKILL.md +61 -0
  51. package/{skill → skills/spec-to-ui}/references/composition-and-validation.md +6 -0
  52. package/{skill → skills/spec-to-ui}/references/drafts-and-feedback.md +10 -0
  53. package/skills/spec-to-ui/references/requirements-supplement.md +21 -0
  54. package/{skill → skills/spec-to-ui}/references/review-and-handoff.md +9 -0
  55. package/skills/spec-to-ui/references/skill-composition.md +21 -0
  56. package/{skill → skills/spec-to-ui}/references/structure-and-decisions.md +4 -0
  57. package/skills/specframe/NOTICE.md +7 -0
  58. package/skills/specframe/SKILL.md +58 -0
  59. package/skills/specframe/agents/openai.yaml +3 -0
  60. package/skills/specframe/references/operating-contract.md +137 -0
  61. package/skills/specframe/references/visual.md +3 -0
  62. package/skills/specframe/references/web.md +15 -0
  63. package/skills/specframe/scripts/state.py +182 -0
  64. package/skills/ui-ux-pro-max/LICENSE +21 -0
  65. package/skills/ui-ux-pro-max/SKILL.md +218 -0
  66. package/skills/ui-ux-pro-max/data/app-interface.csv +33 -0
  67. package/skills/ui-ux-pro-max/data/catalog-summary.json +78 -0
  68. package/skills/ui-ux-pro-max/data/charts.csv +26 -0
  69. package/skills/ui-ux-pro-max/data/colors.csv +193 -0
  70. package/skills/ui-ux-pro-max/data/data-provenance.json +1348 -0
  71. package/skills/ui-ux-pro-max/data/google-font-licenses.json +20185 -0
  72. package/skills/ui-ux-pro-max/data/google-fonts.csv +1935 -0
  73. package/skills/ui-ux-pro-max/data/icons.csv +106 -0
  74. package/skills/ui-ux-pro-max/data/landing.csv +35 -0
  75. package/skills/ui-ux-pro-max/data/motion.csv +18 -0
  76. package/skills/ui-ux-pro-max/data/phosphor-icons-upstream.json +32582 -0
  77. package/skills/ui-ux-pro-max/data/products.csv +193 -0
  78. package/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
  79. package/skills/ui-ux-pro-max/data/stacks/angular.csv +51 -0
  80. package/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
  81. package/skills/ui-ux-pro-max/data/stacks/avalonia.csv +57 -0
  82. package/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  83. package/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +60 -0
  84. package/skills/ui-ux-pro-max/data/stacks/javafx.csv +76 -0
  85. package/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  86. package/skills/ui-ux-pro-max/data/stacks/laravel.csv +51 -0
  87. package/skills/ui-ux-pro-max/data/stacks/nextjs.csv +62 -0
  88. package/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +84 -0
  89. package/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +68 -0
  90. package/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  91. package/skills/ui-ux-pro-max/data/stacks/react.csv +67 -0
  92. package/skills/ui-ux-pro-max/data/stacks/shadcn.csv +69 -0
  93. package/skills/ui-ux-pro-max/data/stacks/svelte.csv +56 -0
  94. package/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  95. package/skills/ui-ux-pro-max/data/stacks/threejs.csv +54 -0
  96. package/skills/ui-ux-pro-max/data/stacks/uno.csv +60 -0
  97. package/skills/ui-ux-pro-max/data/stacks/uwp.csv +56 -0
  98. package/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  99. package/skills/ui-ux-pro-max/data/stacks/winui.csv +60 -0
  100. package/skills/ui-ux-pro-max/data/stacks/wpf.csv +57 -0
  101. package/skills/ui-ux-pro-max/data/styles.csv +89 -0
  102. package/skills/ui-ux-pro-max/data/typography.csv +75 -0
  103. package/skills/ui-ux-pro-max/data/ui-reasoning.csv +193 -0
  104. package/skills/ui-ux-pro-max/data/ux-guidelines.csv +120 -0
  105. package/skills/ui-ux-pro-max/references/pro-rules.md +117 -0
  106. package/skills/ui-ux-pro-max/references/quick-reference.md +256 -0
  107. package/skills/ui-ux-pro-max/scripts/core.py +993 -0
  108. package/skills/ui-ux-pro-max/scripts/design_system.py +1668 -0
  109. package/skills/ui-ux-pro-max/scripts/reasoning_contract.py +123 -0
  110. package/skills/ui-ux-pro-max/scripts/search.py +184 -0
  111. package/skills/ui-ux-pro-max/scripts/tests/fixtures/catalogs/google-api.json +36 -0
  112. package/skills/ui-ux-pro-max/scripts/tests/fixtures/catalogs/google-catalog.json +33 -0
  113. package/skills/ui-ux-pro-max/scripts/tests/fixtures/catalogs/google-existing.csv +3 -0
  114. package/skills/ui-ux-pro-max/scripts/tests/fixtures/catalogs/google-metadata.json +17 -0
  115. package/skills/ui-ux-pro-max/scripts/tests/fixtures/catalogs/google-overrides.json +7 -0
  116. package/skills/ui-ux-pro-max/scripts/tests/fixtures/catalogs/icons-curated.csv +4 -0
  117. package/skills/ui-ux-pro-max/scripts/tests/fixtures/catalogs/phosphor-core.json +23 -0
  118. package/skills/ui-ux-pro-max/scripts/tests/fixtures/catalogs/phosphor-package.json +13 -0
  119. package/skills/ui-ux-pro-max/scripts/tests/fixtures/catalogs/phosphor-react-exports.json +4 -0
  120. package/skills/ui-ux-pro-max/scripts/tests/fixtures/catalogs/phosphor-react-package.json +5 -0
  121. package/skills/ui-ux-pro-max/scripts/tests/fixtures/relevance-baseline.json +3366 -0
  122. package/skills/ui-ux-pro-max/scripts/tests/fixtures/relevance-cases.json +111 -0
  123. package/skills/ui-ux-pro-max/scripts/tests/fixtures/relevance-thresholds.json +100 -0
  124. package/skills/ui-ux-pro-max/scripts/tests/test_catalog_refresh.py +354 -0
  125. package/skills/ui-ux-pro-max/scripts/tests/test_catalog_summary_line_endings.py +78 -0
  126. package/skills/ui-ux-pro-max/scripts/tests/test_core.py +343 -0
  127. package/skills/ui-ux-pro-max/scripts/tests/test_core_data_quality.py +198 -0
  128. package/skills/ui-ux-pro-max/scripts/tests/test_data_contracts.py +421 -0
  129. package/skills/ui-ux-pro-max/scripts/tests/test_design_system_mode.py +233 -0
  130. package/skills/ui-ux-pro-max/scripts/tests/test_design_system_stack.py +61 -0
  131. package/skills/ui-ux-pro-max/scripts/tests/test_native_desktop_stack_freshness.py +179 -0
  132. package/skills/ui-ux-pro-max/scripts/tests/test_relevance_evaluator.py +182 -0
  133. package/skills/ui-ux-pro-max/scripts/tests/test_skill_script_paths.py +82 -0
  134. package/skills/ui-ux-pro-max/scripts/tests/test_style_taxonomy.py +175 -0
  135. package/skills/ui-ux-pro-max/scripts/tests/test_text_layout_resilience.py +120 -0
  136. package/skills/ui-ux-pro-max/scripts/tests/test_web_stack_freshness.py +191 -0
  137. package/skills/ui-ux-pro-max/scripts/validate_data.py +1095 -0
  138. package/sources.json +58 -0
  139. package/skill/SKILL.md +0 -68
  140. package/skill-manifest.json +0 -10
  141. /package/{skill → skills/spec-to-ui}/LICENSE +0 -0
  142. /package/{skill → skills/spec-to-ui}/agents/openai.yaml +0 -0
  143. /package/{skill → skills/spec-to-ui}/references/reference-selection.md +0 -0
package/LICENSE CHANGED
@@ -1,3 +1,5 @@
1
+ The MIT license below applies to original spec-to-ui instructions and installer code. Third-party components retain their respective terms; see THIRD-PARTY.md and the LICENSE files within skills/. SpecFrame is included by redistribution permission, not relicensed under MIT.
2
+
1
3
  MIT License
2
4
 
3
5
  Copyright (c) 2026 spec-to-ui contributors
package/README.ko.md ADDED
@@ -0,0 +1,129 @@
1
+ # spec-to-ui
2
+
3
+ 요구사항 명세서를 화면 구조와 실제로 볼 수 있는 UI 초안으로 발전시키는 Codex 플러그인입니다. 디자인이나 CSS를 몰라도 예시와 미리보기를 보며 방향을 고를 수 있도록 돕습니다.
4
+
5
+ ## 주요 기능
6
+
7
+ - 요구사항을 페이지, 정보 우선순위, 핵심 행동과 필요한 상태로 옮깁니다.
8
+ - 실제 참고 자료와 대표 화면으로 디자인을 비교하고 선택한 방향을 확장합니다.
9
+ - 명세서의 중요한 누락은 SpecFrame으로 보충하고, 승인된 범위에서 UI 작업으로 돌아옵니다.
10
+ - 정렬, 단어 단위 줄바꿈, 입력 요소와 열린 달력, 반응형, 초점, 모션을 함께 검토합니다.
11
+ - 채택한 디자인 규칙은 프로젝트의 `DESIGN.md`에 기록합니다.
12
+
13
+ ## 포함된 스킬
14
+
15
+ | 구성 | 사용하는 시점 |
16
+ |---|---|
17
+ | spec-to-ui | 전체 UI 설계를 진행하고 필요한 보조 역할을 연결합니다. |
18
+ | SpecFrame | 기능·권한·업무 규칙의 누락이나 충돌이 화면 설계를 막을 때 사용합니다. |
19
+ | design-taste-frontend | 시각 방향, 이미지, 타이포그래피, 구성과 모션을 보강합니다. |
20
+ | ui-ux-pro-max | 폼, 반응형, 접근성, 탐색과 상호작용의 구체적인 문제를 검토합니다. |
21
+
22
+ `spec-to-ui`가 필요한 보조 스킬의 지침을 같은 묶음에서 읽도록 구성했습니다. 매번 네 스킬을 모두 실행하지는 않습니다. `DESIGN.md`는 프로젝트 문서이며, Refero Styles 등은 실제 참고 자료를 찾는 외부 서비스입니다. 별도 스킬이나 서비스 이용권이 포함된 것은 아닙니다.
23
+
24
+ ## 필수 프로그램
25
+
26
+ - 플러그인을 지원하는 Codex와 `codex` 명령
27
+ - Node.js 22 이상과 npm
28
+ - 보조 검색 도구를 사용하는 경우 Python 3
29
+
30
+ 브라우저, 이미지 제작, 외부 디자인 자료의 열람은 사용 중인 환경의 도구와 권한에 따릅니다. 필요한 도구를 사용할 수 없으면 가능한 대안과 미검증 범위를 안내하도록 되어 있습니다.
31
+
32
+ ## 설치
33
+
34
+ 다음 명령은 GitHub의 마켓플레이스를 등록한 뒤 npm의 `0.2.0` 패키지를 Codex 플러그인으로 설치합니다.
35
+
36
+ ```sh
37
+ npx --yes spec-to-ui@0.2.0 install
38
+ ```
39
+
40
+ 실행할 설치 명령만 확인하려면 다음을 사용합니다.
41
+
42
+ ```sh
43
+ npx --yes spec-to-ui@0.2.0 install --dry-run
44
+ ```
45
+
46
+ npm 설치 도우미 없이 Codex에서 직접 설치할 수도 있습니다.
47
+
48
+ ```sh
49
+ codex plugin marketplace add suminkim8203/spec-to-ui
50
+ codex plugin add spec-to-ui@spec-to-ui
51
+ ```
52
+
53
+ 설치 후 새 Codex 세션에서 호출합니다. 앱을 계속 열어 두었다면 새 채팅에서도 목록이 갱신되었는지 확인해 주십시오.
54
+
55
+ ```text
56
+ $spec-to-ui:spec-to-ui
57
+ ```
58
+
59
+ 플러그인 이름과 표시명은 모두 `spec-to-ui`입니다.
60
+
61
+ ## 사용법
62
+
63
+ ### 명세서로 시작하기
64
+
65
+ ```text
66
+ $spec-to-ui:spec-to-ui
67
+ 요구사항은 ./REQUIREMENTS.md에 있습니다.
68
+ 신규 사이트를 설계해 주세요. 참고 방향을 미리보기로 추천하고,
69
+ 대표 화면을 확인한 뒤 전체 화면으로 확장하겠습니다.
70
+ ```
71
+
72
+ 이미 승인한 명세서는 재사용합니다. UI 구조에 영향을 주는 누락이 있으면 보충할 항목을 짚고 SpecFrame으로 연결합니다. 요구사항 보충에는 전체 명세서 확인과 사용자 승인이 필요합니다.
73
+
74
+ ### 기존 설계를 이어가기
75
+
76
+ ```text
77
+ $spec-to-ui:spec-to-ui
78
+ 현재 프로젝트의 REQUIREMENTS.md와 DESIGN.md를 읽고 이어서 진행해 주세요.
79
+ 목록 구성은 유지하고 상세 화면과 신청 흐름을 확장해 주세요.
80
+ ```
81
+
82
+ 기존 결정과 승인 범위를 유지하며 필요한 부분부터 작업합니다. 목적·기능·권한·주요 흐름을 바꾸는 선택과 별도 설치·유료 연결·배포는 승인 범위를 확인합니다.
83
+
84
+ ## 작동 구조
85
+
86
+ 명세서와 기존 결정을 읽고 화면 구조를 정리합니다. 실제 참고 자료와 표현 방법을 제안한 뒤, 핵심 작업을 보여 주는 대표 화면을 만듭니다. 사용자 선택이나 위임 범위에 따라 확장하고 실제 브라우저에서 해당 흐름을 검증합니다.
87
+
88
+ 디자인 제안에는 쉬운 설명과 볼 수 있는 예시를 함께 제시하도록 했습니다. 모션이 선택의 핵심이라면 재생하거나 조작할 수 있는 미리보기를 사용합니다. 참고 화면을 열어 보지 못했거나 실제 조작을 하지 못한 경우에는 확인했다고 보고하지 않습니다.
89
+
90
+ ## 결정과 작업 기록
91
+
92
+ 프로젝트의 `DESIGN.md`에 채택한 배치, 글꼴, 색, 간격, 컴포넌트와 상태 규칙을 기록합니다. AI 제안, 사용자 선택, 위임에 따른 결정, 미정 사항을 구분합니다. 요구사항은 기존 명세서에서 관리하며 두 개의 최신 명세서를 만들지 않습니다.
93
+
94
+ 검증 결과에는 실제 확인한 화면·상태·조작과 미검증 사항을 남깁니다. 개발 설명이나 예시 데이터 안내를 제품 화면의 문구로 자동 삽입하지 않습니다.
95
+
96
+ ## 0.1.0에서 바뀐 점
97
+
98
+ `0.1.0`은 단일 스킬을 복사하는 설치 도구입니다. `0.2.0`은 네 스킬을 묶은 플러그인으로, 호출명은 `$spec-to-ui:spec-to-ui`입니다. 이전 설치용 `--target`, `--project`, `--replace` 옵션은 지원하지 않습니다.
99
+
100
+ 설치 도우미는 기존 단일 스킬과 개발용 플러그인을 자동으로 삭제하거나 교체하지 않습니다. 이전 단일 스킬의 `$spec-to-ui` 호출도 별도로 남을 수 있습니다. 개발용 플러그인과 공개 플러그인은 이름이 같으므로, 공개본으로 전환할 때는 Codex의 플러그인 관리 화면에서 사용할 쪽을 활성화해 주십시오.
101
+
102
+ ## 고급 사용과 문제 해결
103
+
104
+ 다른 위치에 있는 Codex 실행 파일을 사용하려면 경로를 지정합니다.
105
+
106
+ ```sh
107
+ npx --yes spec-to-ui@0.2.0 install --codex /absolute/path/to/codex
108
+ ```
109
+
110
+ 설치 상태와 패키지 파일 검사는 각각 다음과 같습니다.
111
+
112
+ ```sh
113
+ npx --yes spec-to-ui@0.2.0 status
114
+ npx --yes spec-to-ui@0.2.0 check
115
+ ```
116
+
117
+ `codex`를 찾지 못하면 Codex CLI의 설치와 경로를 확인해 주십시오. 플러그인 명령 자체가 없으면 사용 중인 Codex가 플러그인을 지원하는지 확인해야 합니다. 이 도구는 Codex나 Node.js를 자동으로 설치·업데이트하지 않습니다.
118
+
119
+ 명령어 지원과 마켓플레이스 구조는 [공식 플러그인 문서](https://developers.openai.com/plugins/build/plugins)를 참고해 주십시오.
120
+
121
+ ## 개발 및 검증
122
+
123
+ 배포 구성과 확인 범위는 [RELEASE.md](RELEASE.md)에 기록했습니다. 설치·검색 검증과 예제 UI 검증은 서로 구분합니다. 특정 사례의 성공이 모든 프로젝트의 디자인 품질이나 실제 서비스 연동을 보장하지는 않습니다.
124
+
125
+ ## 라이선스와 출처
126
+
127
+ 직접 작성한 spec-to-ui 지침과 설치 도구에는 MIT 라이선스가 적용됩니다. 동봉한 외부 구성 요소의 조건과 수정 범위는 [THIRD-PARTY.md](THIRD-PARTY.md), 고정 원본은 [sources.json](sources.json)에 기록했습니다. 각 폴더의 LICENSE도 확인해 주십시오.
128
+
129
+ SpecFrame은 제작자의 재배포 허가에 따라 포함했으며 MIT로 재허가하지 않았습니다.
package/README.md CHANGED
@@ -1,62 +1,129 @@
1
1
  # spec-to-ui
2
2
 
3
- 요구사항 명세서를 바탕으로 UI 구조와 대표 화면을 설계하는 Codex 스킬입니다. 디자인 경험이 적은 사용자가 실제 참고 사례와 초안을 보며 화면 구성을 정하고 수정하도록 돕습니다.
3
+ 요구사항 명세서를 화면 구조와 실제로 볼 수 있는 UI 초안으로 발전시키는 Codex 플러그인입니다. 디자인이나 CSS를 몰라도 예시와 미리보기를 보며 방향을 고를 수 있도록 돕습니다.
4
+
5
+ ## 주요 기능
6
+
7
+ - 요구사항을 페이지, 정보 우선순위, 핵심 행동과 필요한 상태로 옮깁니다.
8
+ - 실제 참고 자료와 대표 화면으로 디자인을 비교하고 선택한 방향을 확장합니다.
9
+ - 명세서의 중요한 누락은 SpecFrame으로 보충하고, 승인된 범위에서 UI 작업으로 돌아옵니다.
10
+ - 정렬, 단어 단위 줄바꿈, 입력 요소와 열린 달력, 반응형, 초점, 모션을 함께 검토합니다.
11
+ - 채택한 디자인 규칙은 프로젝트의 `DESIGN.md`에 기록합니다.
12
+
13
+ ## 포함된 스킬
14
+
15
+ | 구성 | 사용하는 시점 |
16
+ |---|---|
17
+ | spec-to-ui | 전체 UI 설계를 진행하고 필요한 보조 역할을 연결합니다. |
18
+ | SpecFrame | 기능·권한·업무 규칙의 누락이나 충돌이 화면 설계를 막을 때 사용합니다. |
19
+ | design-taste-frontend | 시각 방향, 이미지, 타이포그래피, 구성과 모션을 보강합니다. |
20
+ | ui-ux-pro-max | 폼, 반응형, 접근성, 탐색과 상호작용의 구체적인 문제를 검토합니다. |
21
+
22
+ `spec-to-ui`가 필요한 보조 스킬의 지침을 같은 묶음에서 읽도록 구성했습니다. 매번 네 스킬을 모두 실행하지는 않습니다. `DESIGN.md`는 프로젝트 문서이며, Refero Styles 등은 실제 참고 자료를 찾는 외부 서비스입니다. 별도 스킬이나 서비스 이용권이 포함된 것은 아닙니다.
23
+
24
+ ## 필수 프로그램
25
+
26
+ - 플러그인을 지원하는 Codex와 `codex` 명령
27
+ - Node.js 22 이상과 npm
28
+ - 보조 검색 도구를 사용하는 경우 Python 3
29
+
30
+ 브라우저, 이미지 제작, 외부 디자인 자료의 열람은 사용 중인 환경의 도구와 권한에 따릅니다. 필요한 도구를 사용할 수 없으면 가능한 대안과 미검증 범위를 안내하도록 되어 있습니다.
4
31
 
5
32
  ## 설치
6
33
 
7
- Node.js 20 이상과 npm, Codex가 필요합니다.
34
+ 다음 명령은 GitHub의 마켓플레이스를 등록한 뒤 npm의 `0.2.0` 패키지를 Codex 플러그인으로 설치합니다.
8
35
 
9
36
  ```sh
10
- npx spec-to-ui install
37
+ npx --yes spec-to-ui@0.2.0 install
11
38
  ```
12
39
 
13
- 사용자 스킬 폴더인 `~/.agents/skills/spec-to-ui`에 설치합니다. 설치한 스킬은 여러 프로젝트에서 사용할 수 있습니다.
40
+ 실행할 설치 명령만 확인하려면 다음을 사용합니다.
14
41
 
15
- 현재 프로젝트에서만 사용하려면 프로젝트 폴더에서 실행합니다.
42
+ ```sh
43
+ npx --yes spec-to-ui@0.2.0 install --dry-run
44
+ ```
45
+
46
+ npm 설치 도우미 없이 Codex에서 직접 설치할 수도 있습니다.
16
47
 
17
48
  ```sh
18
- npx spec-to-ui install --project
49
+ codex plugin marketplace add suminkim8203/spec-to-ui
50
+ codex plugin add spec-to-ui@spec-to-ui
19
51
  ```
20
52
 
21
- 설치 후 Codex에 요구사항 명세서를 제공하고 다음처럼 요청합니다.
53
+ 설치 후 새 Codex 세션에서 호출합니다. 앱을 계속 열어 두었다면 새 채팅에서도 목록이 갱신되었는지 확인해 주십시오.
22
54
 
23
55
  ```text
24
- $spec-to-ui 이 명세서를 바탕으로 화면 구조와 대표 화면을 설계해 주세요.
56
+ $spec-to-ui:spec-to-ui
25
57
  ```
26
58
 
27
- 스킬이 목록에 나타나지 않으면 Codex를 다시 시작해 주세요. 이 명령은 스킬 파일을 설치합니다. 화면 생성과 실제 검토는 Codex에서 진행합니다.
59
+ 플러그인 이름과 표시명은 모두 `spec-to-ui`입니다.
28
60
 
29
- ## 업데이트와 설치 상태
61
+ ## 사용법
30
62
 
31
- ```sh
32
- npx spec-to-ui@latest status
33
- npx spec-to-ui@latest install --replace
63
+ ### 명세서로 시작하기
64
+
65
+ ```text
66
+ $spec-to-ui:spec-to-ui
67
+ 요구사항은 ./REQUIREMENTS.md에 있습니다.
68
+ 신규 사이트를 설계해 주세요. 참고 방향을 미리보기로 추천하고,
69
+ 대표 화면을 확인한 뒤 전체 화면으로 확장하겠습니다.
34
70
  ```
35
71
 
36
- 같은 파일이 설치되어 있으면 변경하지 않습니다. 다른 내용이 있는 폴더는 기본적으로 덮어쓰지 않습니다. `--replace`를 사용하면 이전 폴더를 백업한 뒤 교체합니다. 사용자 설치의 백업은 `~/.agents/skill-backups/spec-to-ui/`에, 프로젝트 설치의 백업은 `.agents/skill-backups/spec-to-ui/`에 남깁니다. 심볼릭 링크로 설치된 기존 폴더는 자동 교체하지 않습니다.
72
+ 이미 승인한 명세서는 재사용합니다. UI 구조에 영향을 주는 누락이 있으면 보충할 항목을 짚고 SpecFrame으로 연결합니다. 요구사항 보충에는 전체 명세서 확인과 사용자 승인이 필요합니다.
73
+
74
+ ### 기존 설계를 이어가기
75
+
76
+ ```text
77
+ $spec-to-ui:spec-to-ui
78
+ 현재 프로젝트의 REQUIREMENTS.md와 DESIGN.md를 읽고 이어서 진행해 주세요.
79
+ 목록 구성은 유지하고 상세 화면과 신청 흐름을 확장해 주세요.
80
+ ```
81
+
82
+ 기존 결정과 승인 범위를 유지하며 필요한 부분부터 작업합니다. 목적·기능·권한·주요 흐름을 바꾸는 선택과 별도 설치·유료 연결·배포는 승인 범위를 확인합니다.
83
+
84
+ ## 작동 구조
85
+
86
+ 명세서와 기존 결정을 읽고 화면 구조를 정리합니다. 실제 참고 자료와 표현 방법을 제안한 뒤, 핵심 작업을 보여 주는 대표 화면을 만듭니다. 사용자 선택이나 위임 범위에 따라 확장하고 실제 브라우저에서 해당 흐름을 검증합니다.
87
+
88
+ 디자인 제안에는 쉬운 설명과 볼 수 있는 예시를 함께 제시하도록 했습니다. 모션이 선택의 핵심이라면 재생하거나 조작할 수 있는 미리보기를 사용합니다. 참고 화면을 열어 보지 못했거나 실제 조작을 하지 못한 경우에는 확인했다고 보고하지 않습니다.
89
+
90
+ ## 결정과 작업 기록
91
+
92
+ 프로젝트의 `DESIGN.md`에 채택한 배치, 글꼴, 색, 간격, 컴포넌트와 상태 규칙을 기록합니다. AI 제안, 사용자 선택, 위임에 따른 결정, 미정 사항을 구분합니다. 요구사항은 기존 명세서에서 관리하며 두 개의 최신 명세서를 만들지 않습니다.
93
+
94
+ 검증 결과에는 실제 확인한 화면·상태·조작과 미검증 사항을 남깁니다. 개발 설명이나 예시 데이터 안내를 제품 화면의 문구로 자동 삽입하지 않습니다.
95
+
96
+ ## 0.1.0에서 바뀐 점
97
+
98
+ `0.1.0`은 단일 스킬을 복사하는 설치 도구입니다. `0.2.0`은 네 스킬을 묶은 플러그인으로, 호출명은 `$spec-to-ui:spec-to-ui`입니다. 이전 설치용 `--target`, `--project`, `--replace` 옵션은 지원하지 않습니다.
99
+
100
+ 설치 도우미는 기존 단일 스킬과 개발용 플러그인을 자동으로 삭제하거나 교체하지 않습니다. 이전 단일 스킬의 `$spec-to-ui` 호출도 별도로 남을 수 있습니다. 개발용 플러그인과 공개 플러그인은 이름이 같으므로, 공개본으로 전환할 때는 Codex의 플러그인 관리 화면에서 사용할 쪽을 활성화해 주십시오.
101
+
102
+ ## 고급 사용과 문제 해결
103
+
104
+ 다른 위치에 있는 Codex 실행 파일을 사용하려면 경로를 지정합니다.
105
+
106
+ ```sh
107
+ npx --yes spec-to-ui@0.2.0 install --codex /absolute/path/to/codex
108
+ ```
37
109
 
38
- 다른 위치가 필요하면 스킬 폴더 경로를 직접 지정합니다.
110
+ 설치 상태와 패키지 파일 검사는 각각 다음과 같습니다.
39
111
 
40
112
  ```sh
41
- npx spec-to-ui install --target /path/to/spec-to-ui
113
+ npx --yes spec-to-ui@0.2.0 status
114
+ npx --yes spec-to-ui@0.2.0 check
42
115
  ```
43
116
 
44
- 직접 지정한 경로는 Codex가 읽는 스킬 위치에 있어야 합니다. 사용자 지정 경로의 백업은 대상 폴더의 두 단계 위에 있는 `skill-backups/spec-to-ui/` 아래에 남깁니다.
117
+ `codex`를 찾지 못하면 Codex CLI의 설치와 경로를 확인해 주십시오. 플러그인 명령 자체가 없으면 사용 중인 Codex가 플러그인을 지원하는지 확인해야 합니다. 이 도구는 Codex나 Node.js를 자동으로 설치·업데이트하지 않습니다.
45
118
 
46
- ## 포함된 기준
119
+ 명령어 지원과 마켓플레이스 구조는 [공식 플러그인 문서](https://developers.openai.com/plugins/build/plugins)를 참고해 주십시오.
47
120
 
48
- 명세서에서 사용자와 권한, 페이지와 흐름, 정보 우선순위를 정리합니다. 실제 참고 사례를 확인하고 대표 화면을 만든 뒤 피드백을 반영합니다. 다음 항목도 검토합니다.
121
+ ## 개발 및 검증
49
122
 
50
- - 글자와 버튼의 정렬, 단어 사이 줄바꿈
51
- - 목록과 상세 사이의 이동, 선택과 초점, 원래 위치로의 복귀
52
- - 작은 상호작용의 반응과 애니메이션, 동작 감소 환경
53
- - 입력 필드와 버튼뿐 아니라 펼친 달력과 선택 목록의 디자인
54
- - 기존 입력 방식과 선택 범위의 보존
55
- - 불필요한 정보 반복과 개발 설명 문구
56
- - 모호한 요구의 해석과 실제 사용에서 예상되는 불편
123
+ 배포 구성과 확인 범위는 [RELEASE.md](RELEASE.md)에 기록했습니다. 설치·검색 검증과 예제 UI 검증은 서로 구분합니다. 특정 사례의 성공이 모든 프로젝트의 디자인 품질이나 실제 서비스 연동을 보장하지는 않습니다.
57
124
 
58
- 스킬 문서 7개와 MIT 라이선스, 설치 도구가 포함되어 있습니다. 설치한 스킬 폴더에도 라이선스 사본을 남깁니다. npm 설치만으로 웹사이트가 생성되거나 서버가 배포되지는 않습니다. 결과물의 품질과 요구사항 충족 여부는 실제 화면과 조작으로 확인해야 합니다.
125
+ ## 라이선스와 출처
59
126
 
60
- ## 라이선스
127
+ 직접 작성한 spec-to-ui 지침과 설치 도구에는 MIT 라이선스가 적용됩니다. 동봉한 외부 구성 요소의 조건과 수정 범위는 [THIRD-PARTY.md](THIRD-PARTY.md), 고정 원본은 [sources.json](sources.json)에 기록했습니다. 각 폴더의 LICENSE도 확인해 주십시오.
61
128
 
62
- MIT
129
+ SpecFrame은 제작자의 재배포 허가에 따라 포함했으며 MIT로 재허가하지 않았습니다.
package/RELEASE.md ADDED
@@ -0,0 +1,26 @@
1
+ # spec-to-ui 0.2.0
2
+
3
+ 네 스킬을 포함하는 Codex 플러그인으로 배포합니다. 기존 0.1.0의 단일 스킬 복사 방식에서 공식 마켓플레이스 등록과 플러그인 설치 방식으로 바뀌었습니다.
4
+
5
+ ## 배포 구성
6
+
7
+ - spec-to-ui: 명세서에서 UI 구조·참고·대표 화면·피드백으로 이어지는 설계
8
+ - SpecFrame: 구조에 영향을 주는 요구 누락·충돌의 보충과 승인 후 복귀
9
+ - design-taste-frontend: 채택한 구조를 보존하는 시각 표현 보강
10
+ - ui-ux-pro-max: 구체적인 폼·접근성·반응형·상호작용 지침 검색
11
+
12
+ 기존 개발본의 지침과 보조 자료를 사용합니다. 설치 도구와 공개용 문서·메타데이터를 추가했으며 외부 서비스 이용권, 검증용 사이트, 사용자 대화와 제출 자료는 포함하지 않습니다.
13
+
14
+ ## 검증 범위
15
+
16
+ - 검증한 개발본과 배포본의 스킬 파일 131개가 일치합니다.
17
+ - 실제 npm 압축 파일을 풀어 별도 Codex 설정 폴더에서 설치했습니다. 플러그인 버전 0.2.0과 네 스킬의 활성화·이름·설치 경로를 확인했습니다.
18
+ - 설치 도구의 12개 검사를 실행했습니다. 정상 검사, 설치 예정 명령 표시, 잘못된 옵션 거부, 공백을 포함한 인수 전달, 설치 오류 시 중단, 파일 변조 감지와 복원 후 검사를 포함합니다.
19
+ - 설치된 사본에서 SpecFrame 상태 도구, ui-ux-pro-max의 키보드·초점 검색, taste의 두 방향 제안을 실행했습니다.
20
+ - 기존 단일 스킬, 개발용 플러그인 설치본과 개발 원본은 변경하지 않았습니다.
21
+
22
+ 설치 검증에는 Codex CLI 0.166.0-alpha.5.1, Node.js 22 이상을 지원하는 로컬 실행 환경을 사용했습니다. 공개 레지스트리의 다운로드와 GitHub 경유 설치는 게시 후 별도로 확인합니다.
23
+
24
+ 첫 로컬 설치 시 마켓플레이스 폴더 밖을 가리킨 시험 경로가 거부되었습니다. 패키지를 포함한 마켓플레이스 안의 상대 경로로 수정해 통과했습니다. taste 도구는 `--help` 옵션을 지원하지 않아, 문서의 `--sector`와 `--options` 방식으로 다시 실행했습니다. 원본 도구를 수정하지 않았습니다.
25
+
26
+ 개발 플러그인에서 확인한 실제 호출과 문화공간·회의실 예약 예제의 UI 검증은 같은 지침 파일에 대한 이전 근거입니다. 이번 배포 검사는 새로운 주제의 UI 제작, 모든 운영체제·실기기·화면 읽기 도구 시험이나 모든 프로젝트의 품질 보장을 뜻하지 않습니다. npm·GitHub 배포는 OpenAI 공개 플러그인 디렉터리의 등록·승인과도 별개입니다.
package/THIRD-PARTY.md ADDED
@@ -0,0 +1,14 @@
1
+ # 원본과 조정 범위
2
+
3
+ spec-to-ui 0.2.0은 아래 네 스킬을 포함하는 공개 플러그인입니다. `skills/spec-to-ui/SKILL.md`가 진입점이며 형제 폴더의 보조 스킬을 연결합니다.
4
+
5
+ | 구성 | 고정 원본 | 고지와 조정 |
6
+ |---|---|---|
7
+ | spec-to-ui | 기존 0.1.0 배포 소스 | 기존 MIT 고지를 보존합니다. 조합·보충·추천 미리보기·DESIGN.md·완료 기준을 반영했습니다. |
8
+ | SpecFrame | ProjectFrame-skills/SpecFrame `0a9c555672384f27713184d55724155798af252e` | 제작자 재배포 허가를 사용자에게 확인받았습니다. MIT로 재허가하지 않습니다. 이 사본에서 승인 후 복귀 대상을 spec-to-ui로 변경했으며 요구 검토·승인·상태 소유 규칙을 유지합니다. |
9
+ | design-taste-frontend | Leonxlnx/taste-skill `12af471f627f866411b4bd785b19646d3672784c`, `skills/taste-skill` | 원본 LICENSE를 보존합니다. 조합의 승인된 구조와 디자인, 보조 범위·검증 도구 제약을 진입 지침에 명시했습니다. |
10
+ | ui-ux-pro-max | nextlevelbuilder/ui-ux-pro-max-skill `3a13158ff1f32156cbe144108de48eda2ecaed9a`, 2.15.0 | 원본 LICENSE를 보존합니다. Codex에서 실제 스킬 경로를 쓰도록 실행 예시를 조정하고 기존 DESIGN.md와 충돌하는 디자인 시스템 재생성을 제한했습니다. |
11
+
12
+ 외부 참고 사이트의 사진·영상·글꼴·브랜드 자료는 포함하지 않았습니다. 원본 위치와 조회한 파일은 sources.json에 기록했습니다. 개인 대화나 허가 확인 전문은 배포 대상이 아닙니다.
13
+
14
+ SpecFrame은 제작자에게 확인받은 재배포 허가에 따라 포함했습니다. 해당 허가를 MIT 라이선스나 모든 이용자에게 적용되는 별도 재허가로 확대 해석하지 않습니다. SpecFrame을 별도로 재배포하거나 이용 범위를 확인하려면 원 제작자의 조건을 확인해 주십시오. 다른 구성 요소는 각각의 LICENSE와 고지를 따릅니다.
package/bin/cli.mjs CHANGED
@@ -1,110 +1,89 @@
1
1
  #!/usr/bin/env node
2
- import { readFile, readdir, mkdir, lstat, mkdtemp, cp, rename, rm } from 'node:fs/promises';
3
- import { createHash, randomUUID } from 'node:crypto';
4
- import { homedir } from 'node:os';
5
- import { dirname, join, resolve, isAbsolute } from 'node:path';
2
+ import { readFileSync, readdirSync, lstatSync } from 'node:fs';
3
+ import { createHash } from 'node:crypto';
4
+ import { dirname, join, relative, isAbsolute } from 'node:path';
6
5
  import { fileURLToPath } from 'node:url';
6
+ import { spawnSync } from 'node:child_process';
7
7
 
8
- const root = fileURLToPath(new URL('../', import.meta.url));
9
- const source = join(root, 'skill');
10
- const pkg = JSON.parse(await readFile(join(root, 'package.json'), 'utf8'));
11
- const manifest = JSON.parse(await readFile(join(root, 'skill-manifest.json'), 'utf8'));
12
- const digest = bytes => createHash('sha256').update(bytes).digest('hex');
8
+ const root = dirname(dirname(fileURLToPath(import.meta.url)));
9
+ const version = '0.2.0';
10
+ const defaultMarketplace = 'suminkim8203/spec-to-ui';
11
+ const installId = 'spec-to-ui@spec-to-ui';
13
12
 
14
- async function exists(path) {
15
- try { return await lstat(path); }
16
- catch (error) { if (error.code === 'ENOENT') return null; throw error; }
17
- }
18
- async function files(path, prefix = '') {
13
+ function files(folder) {
19
14
  const result = [];
20
- for (const entry of await readdir(path, { withFileTypes: true })) {
21
- const name = prefix ? `${prefix}/${entry.name}` : entry.name;
22
- if (entry.isSymbolicLink()) throw new Error(`심볼릭 링크가 포함되어 있습니다: ${name}`);
23
- if (entry.isDirectory()) result.push(...await files(join(path, entry.name), name));
24
- else if (entry.isFile()) result.push(name);
25
- else throw new Error(`지원하지 않는 파일 형식입니다: ${name}`);
15
+ for (const name of readdirSync(folder).sort()) {
16
+ const path = join(folder, name);
17
+ const stat = lstatSync(path);
18
+ if (stat.isSymbolicLink()) throw new Error('Symbolic link is not permitted: ' + relative(root, path));
19
+ if (stat.isDirectory()) result.push(...files(path));
20
+ else if (stat.isFile()) result.push(relative(root, path).split('\\').join('/'));
21
+ else throw new Error('Unexpected file type: ' + relative(root, path));
26
22
  }
27
23
  return result.sort();
28
24
  }
29
- async function matches(path) {
30
- const entries = await files(path);
31
- if (JSON.stringify(entries) !== JSON.stringify(Object.keys(manifest).sort())) return false;
32
- for (const name of entries) if (digest(await readFile(join(path, name))) !== manifest[name]) return false;
33
- return true;
34
- }
35
- async function validatePackage() {
36
- for (const name of Object.keys(manifest)) {
37
- if (isAbsolute(name) || name.split(/[\\/]/).includes('..')) throw new Error('잘못된 스킬 파일 경로입니다.');
25
+
26
+ function checkBundle() {
27
+ const manifest = JSON.parse(readFileSync(join(root, 'bundle-manifest.json'), 'utf8'));
28
+ if (manifest.version !== version || !manifest.sha256 || typeof manifest.sha256 !== 'object') {
29
+ throw new Error('Invalid bundle manifest');
38
30
  }
39
- if (!await matches(source)) throw new Error('패키지의 스킬 파일이 검증 목록과 일치하지 않습니다. 다시 내려받아 주세요.');
31
+ for (const [path, expected] of Object.entries(manifest.sha256)) {
32
+ if (!path || isAbsolute(path) || path.includes('\\') || path.split('/').some(p => p === '..' || p === '.')) {
33
+ throw new Error('Invalid manifest path');
34
+ }
35
+ const full = join(root, path);
36
+ if (!lstatSync(full).isFile() || lstatSync(full).isSymbolicLink()) throw new Error('Invalid bundle file: ' + path);
37
+ const actual = createHash('sha256').update(readFileSync(full)).digest('hex');
38
+ if (actual !== expected) throw new Error('Bundle file differs: ' + path);
39
+ }
40
+ const listed = Object.keys(manifest.sha256).filter(p => p.startsWith('skills/')).sort();
41
+ const actual = files(join(root, 'skills'));
42
+ if (JSON.stringify(listed) !== JSON.stringify(actual)) throw new Error('Skill file list differs');
43
+ const plugin = JSON.parse(readFileSync(join(root, 'plugin.json'), 'utf8'));
44
+ if (plugin.name !== 'spec-to-ui' || plugin.version !== version) throw new Error('Plugin identity differs');
45
+ return { version, skillFiles: listed.length, skills: readdirSync(join(root, 'skills')).sort() };
40
46
  }
41
- function help() {
42
- console.log(`spec-to-ui ${pkg.version}
43
47
 
44
- 사용법:
45
- npx spec-to-ui install 사용자 스킬 폴더에 설치
46
- npx spec-to-ui install --project 현재 프로젝트에 설치
47
- npx spec-to-ui status 설치 상태 확인
48
- npx spec-to-ui install --replace 기존 설치를 백업하고 교체
49
-
50
- 옵션:
51
- --project 현재 폴더의 .agents/skills/spec-to-ui 사용
52
- --target <directory> 지정한 스킬 폴더 사용
53
- --replace 기존 폴더를 백업한 뒤 교체
54
- --help 도움말
55
- --version 버전
56
-
57
- 설치 후 Codex에서 $spec-to-ui를 호출합니다.
58
- 스킬이 목록에 나타나지 않으면 Codex를 다시 시작해 주세요.`);
48
+ function help() {
49
+ process.stdout.write(`spec-to-ui ${version}\n\nUsage:\n spec-to-ui install [--codex <binary>] [--marketplace <source>] [--dry-run]\n spec-to-ui status [--codex <binary>] [--dry-run]\n spec-to-ui check\n spec-to-ui --version\n\nInstall registers the marketplace and installs the spec-to-ui plugin through Codex.\nThe existing standalone skill and development plugin are not replaced.\nDefault marketplace: ${defaultMarketplace}\nRequires Node.js 22+ and a Codex version with plugin support.\nPython 3 is needed for companion search tools.\nOld --target, --project and --replace options are not supported by the plugin installer.\n`);
59
50
  }
60
- async function main() {
51
+
52
+ function main() {
61
53
  const args = process.argv.slice(2);
62
- if (args.length === 0 || args.includes('--help') || args.includes('-h')) { help(); return; }
63
- if (args.length === 1 && args[0] === '--version') { console.log(pkg.version); return; }
54
+ if (!args.length || (args.length === 1 && ['--help', '-h', 'help'].includes(args[0]))) return help();
55
+ if (args.length === 1 && ['--version', '-v'].includes(args[0])) return process.stdout.write(version + '\n');
64
56
  const command = args.shift();
65
- if (!['install', 'status'].includes(command)) throw new Error('install 또는 status 명령을 사용해 주세요.');
66
- let project = false, replace = false, custom = null;
57
+ if (!['install', 'status', 'check'].includes(command)) throw new Error('Unknown command: ' + command);
58
+ const options = { codex: 'codex', marketplace: defaultMarketplace, dryRun: false };
59
+ const seen = new Set();
67
60
  while (args.length) {
68
61
  const flag = args.shift();
69
- if (flag === '--project' && !project) project = true;
70
- else if (flag === '--replace' && !replace) replace = true;
71
- else if (flag === '--target' && custom === null) {
72
- custom = args.shift();
73
- if (!custom || custom.startsWith('--')) throw new Error('--target 뒤에 폴더 경로가 필요합니다.');
74
- } else throw new Error(`지원하지 않거나 중복된 옵션입니다: ${flag}`);
62
+ if (seen.has(flag)) throw new Error('Duplicate option: ' + flag);
63
+ seen.add(flag);
64
+ if (flag === '--dry-run' && command !== 'check') options.dryRun = true;
65
+ else if (flag === '--codex' && command !== 'check') {
66
+ const value = args.shift();
67
+ if (!value || value.startsWith('--')) throw new Error('Missing value: ' + flag);
68
+ options.codex = value;
69
+ } else if (flag === '--marketplace' && command === 'install') {
70
+ const value = args.shift();
71
+ if (!value || value.startsWith('--')) throw new Error('Missing value: ' + flag);
72
+ options.marketplace = value;
73
+ } else throw new Error('Unsupported option for ' + command + ': ' + flag);
75
74
  }
76
- if (custom && project) throw new Error('--target과 --project는 함께 사용할 수 없습니다.');
77
- if (command === 'status' && replace) throw new Error('--replace는 install에만 사용할 수 있습니다.');
78
- const target = custom ? resolve(custom) : join(project ? process.cwd() : homedir(), '.agents', 'skills', 'spec-to-ui');
79
- if (target === source || source.startsWith(target + '/') || target.startsWith(source + '/')) throw new Error('패키지 내부를 설치 대상으로 사용할 수 없습니다.');
80
- await validatePackage();
81
- const current = await exists(target);
82
- if (current && (!current.isDirectory() || current.isSymbolicLink())) throw new Error(`설치 대상이 일반 폴더가 아닙니다. 기존 경로를 유지합니다: ${target}`);
83
- if (command === 'status') {
84
- console.log(!current ? `설치되지 않았습니다: ${target}` : await matches(target) ? `동일한 스킬이 설치되어 있습니다: ${target}` : `다른 내용의 스킬이 설치되어 있습니다: ${target}`);
85
- return;
75
+ const bundle = checkBundle();
76
+ if (command === 'check') return process.stdout.write(JSON.stringify({ ok: true, ...bundle }, null, 2) + '\n');
77
+ const commands = command === 'install'
78
+ ? [['plugin', 'marketplace', 'add', options.marketplace, '--json'], ['plugin', 'add', installId, '--json']]
79
+ : [['plugin', 'list', '--json']];
80
+ if (options.dryRun) return process.stdout.write(JSON.stringify({ dryRun: true, commands: commands.map(args => ({ executable: options.codex, args })) }, null, 2) + '\n');
81
+ for (const args of commands) {
82
+ const result = spawnSync(options.codex, args, { stdio: 'inherit', shell: false });
83
+ if (result.error) throw new Error('Could not start Codex: ' + result.error.message + '. Use --codex <binary> to select your CLI.');
84
+ if (result.status !== 0) throw new Error('Codex command failed' + (result.signal ? ' (' + result.signal + ')' : ' with exit code ' + result.status));
86
85
  }
87
- if (current && await matches(target)) { console.log(`동일한 스킬이 이미 설치되어 있습니다: ${target}`); return; }
88
- if (current && !replace) throw new Error(`기존 스킬을 유지했습니다: ${target}\n백업 후 교체하려면 --replace를 사용해 주세요.`);
89
- await mkdir(dirname(target), { recursive: true });
90
- const stage = await mkdtemp(join(dirname(target), '.spec-to-ui-install-'));
91
- let backup = null;
92
- try {
93
- await cp(source, stage, { recursive: true, force: false });
94
- if (!await matches(stage)) throw new Error('복사한 파일의 검증에 실패했습니다.');
95
- if (current) {
96
- // Backups stay outside the skills directory to avoid duplicate discovery.
97
- const backupRoot = join(dirname(dirname(target)), 'skill-backups', 'spec-to-ui');
98
- await mkdir(backupRoot, { recursive: true });
99
- backup = join(backupRoot, `${new Date().toISOString().replace(/[:.]/g, '-')}-${randomUUID()}`);
100
- await rename(target, backup);
101
- } else if (await exists(target)) throw new Error('설치 대상이 작업 중 생성되어 설치를 중단했습니다.');
102
- try { await rename(stage, target); }
103
- catch (error) { if (backup) await rename(backup, target); throw error; }
104
- } finally { await rm(stage, { recursive: true, force: true }); }
105
- console.log(`spec-to-ui ${pkg.version} 설치 완료: ${target}`);
106
- if (backup) console.log(`이전 스킬 백업: ${backup}`);
107
- console.log('Codex에서 $spec-to-ui를 호출해 주세요.');
86
+ if (command === 'install') process.stdout.write('Installed via Codex. Start a new session and call $spec-to-ui:spec-to-ui.\n');
108
87
  }
109
- try { await main(); }
110
- catch (error) { console.error(`spec-to-ui: ${error.message}`); process.exitCode = 1; }
88
+
89
+ try { main(); } catch (error) { process.stderr.write('spec-to-ui: ' + error.message + '\n'); process.exitCode = 1; }