@no-k/hermes 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,285 +1,217 @@
1
- # @no-k/hermes
1
+ # Hermes
2
2
 
3
- Hermes CLI로 필요한 TypeScript 유틸 소스를 지정한 디렉터리에 생성합니다.
4
- 선택한 유틸과 필요한 보조 파일은 npm 패키지에 함께 들어 있습니다.
5
- CLI는 터미널 화면에 Ink·React, 버전 범위 검사에 `semver`를 사용합니다.
3
+ 필요한 TypeScript 코드를 골라 프로젝트에 추가하세요.
6
4
 
7
- ## 사용
5
+ Hermes는 유틸리티, Tailwind CSS 헬퍼, FSD lint 규칙의 소스를 생성하는 CLI입니다.
6
+ 생성된 파일을 직접 읽고 수정하면서 프로젝트 코드와 함께 관리할 수 있습니다.
7
+ 선택한 항목에 필요한 보조 코드도 함께 가져옵니다.
8
8
 
9
- Node.js 22 이상이 필요합니다. 아래 명령은 공개 배포 후 사용할 수 있습니다.
9
+ [시작하기](#시작하기) · [Tailwind CSS](#tailwind-css-헬퍼) · [FSD lint](#fsd-lint-규칙) · [명령어](#명령어)
10
+
11
+ ## 시작하기
12
+
13
+ **Node.js 22 이상**이 필요합니다. 코드를 추가할 프로젝트 디렉터리의 터미널에서 실행하세요.
10
14
 
11
15
  ```bash
12
16
  npx @no-k/hermes@latest
13
- # 최신 카탈로그를 탐색하고 설치 계획만 확인
14
- npx @no-k/hermes@latest --dry-run
15
- # 특정 항목을 미리 체크한 상태로 시작
16
- npx @no-k/hermes@0.1.0 add array-chunk
17
17
  ```
18
18
 
19
- 터미널에서 Core / 라이브러리 → 하위 분류 → 유틸을 여러 개 선택합니다.
20
- 각 대분류의 경로와 기존 파일 처리를 검토한 뒤, 최종 목록에서 Yes를 선택하고 Enter를 누르면 설치합니다.
21
- No는 선택·경로·파일별 교체 결정을 유지하고 수정 화면으로 돌아갑니다.
22
- ↑↓로 이동하고 Space로 체크합니다. `/`로 이름·한국어 설명을 검색한 뒤 Enter로 검색 입력을
23
- 끝내고, Space로 항목을 체크합니다. 다시 Enter를 누르면 다음 단계로 이동합니다.
24
- Tab으로 설명·의존성을 자세히 보고 Esc로 돌아갈 수 있습니다.
25
- 기본 경로로 array-chunk를 설치하면 다음 파일과 실행 디렉터리의 `no-k.config.ts`를 생성합니다.
19
+ 1. **코드 선택** — Core 또는 라이브러리에서 필요한 항목을 고릅니다.
20
+ 2. **경로 확인** — 파일을 생성할 위치와 기존 파일의 처리 방법을 확인합니다.
21
+ 3. **생성** — 최종 목록에서 `Yes`를 선택하고 Enter를 누릅니다.
22
+
23
+ `No`를 선택하면 앞 화면으로 돌아가 선택 내용을 수정할 수 있습니다.
24
+
25
+ | 키 | 동작 |
26
+ | --- | --- |
27
+ | ↑ / ↓ | 목록 이동 |
28
+ | Space | 항목 선택·해제 |
29
+ | Enter | 검색 입력 완료 또는 다음 단계 |
30
+ | / | 이름이나 한국어 설명으로 검색 |
31
+ | Tab | 항목 설명·의존성 또는 파일 차이 보기 |
32
+ | Esc | 상세 보기 닫기 또는 이전 단계 |
33
+ | Ctrl+C | 취소 |
34
+
35
+ ### 첫 번째 유틸 추가하기
36
+
37
+ 배열을 일정한 크기로 나누는 `array-chunk`를 추가해 보세요.
38
+
39
+ ```bash
40
+ npx @no-k/hermes@latest add array-chunk
41
+ ```
42
+
43
+ 해당 항목이 선택된 상태로 시작합니다. 기본 경로를 사용하고 생성을 확인하면 다음 파일이 생깁니다.
26
44
 
27
45
  ```text
28
46
  src/utils/hermes/
29
- ├── array/chunk/chunk.ts
30
- ├── array/chunk/index.ts
47
+ ├── array/chunk/
48
+ │ ├── chunk.ts
49
+ │ └── index.ts
50
+ ├── index.ts
31
51
  └── LICENSE
52
+
53
+ no-k.config.ts
32
54
  ```
33
55
 
34
- 프로젝트의 실제 경로에 맞춰 import합니다.
56
+ 설치 루트의 `index.ts` 배럴에서 생성한 유틸을 바로 import해 사용하세요.
57
+ 이후 항목을 추가하면 기존 export와 주석을 보존하면서 필요한 export를 추가합니다.
35
58
 
36
59
  ```ts
37
- import { chunk } from "./utils/hermes/array/chunk";
60
+ // src/example.ts
61
+ import { chunk } from "./utils/hermes";
38
62
 
39
- chunk([1, 2, 3, 4, 5], 2); // [[1, 2], [3, 4], [5]]
63
+ chunk([1, 2, 3, 4, 5], 2);
64
+ // [[1, 2], [3, 4], [5]]
40
65
  ```
41
66
 
42
- `--dir`는 현재 작업 디렉터리 기준 상대 경로나 절대 경로를 받습니다.
43
- 공백이 있는 경로는 따옴표로 감싸세요. 그 아래에는 유틸의 카테고리/이름 구조를 유지합니다.
44
- 경로 입력의 초기값은 `--dir` → 현재 디렉터리의 저장된 설정 → 기본 경로 순서입니다.
45
- Core 기본값은 `src/utils/hermes`, 라이브러리는 `libs/hermes`이고 화면에서 각각 수정할 수 있습니다.
46
- 경로 입력에서 Enter는 현재 값 사용, Ctrl+U는 전체 지우기, Esc는 이전 화면입니다.
47
- 모노레포에서는 설치할 앱·패키지 디렉터리에서 실행합니다. 상위 디렉터리의 설정은 찾지 않습니다.
48
- 설정 파일이나 package.json이 없는 디렉터리에서도 파일 생성이 가능합니다.
49
- 설치가 성공하면 `no-k.config.ts`에 목적지, 선택한 유틸과 함께 복사한 모듈의 버전·해시를 저장합니다.
50
- 기존 설정이 있으면 읽어서 갱신하며, `--dry-run`에서는 설정도 쓰지 않습니다.
67
+ `no-k.config.ts`는 설치 위치와 항목의 버전을 기억합니다.
68
+ 생성된 소스와 이 설정 파일을 함께 커밋하면 팀에서도 같은 상태를 공유할 수 있습니다.
51
69
 
52
- ## 명령과 옵션
70
+ ## 추가할 수 있는 코드
53
71
 
54
- | 명령/옵션 | 동작 |
55
- | --- | --- |
56
- | list | 설치 가능한 항목과 설명 출력 |
57
- | 명령 없음 / add | 카탈로그 선택 설치 시작 |
58
- | add <항목...> | 해당 항목을 미리 선택한 상태로 시작 |
59
- | --dir <경로> | 선택한 대분류들의 경로 초기값 |
60
- | --dry-run | 동일한 선택·검토 과정 후 예정 내역만 출력. 설정도 쓰지 않음 |
61
- | --overwrite | 첫 충돌 화면의 교체 후보를 미리 체크. 개별 해제와 최종 확인 가능 |
62
- | --help | 도움말 |
63
- | --version | CLI 버전 |
72
+ | 분류 | 예시 | 용도 |
73
+ | --- | --- | --- |
74
+ | Core | `array-chunk`, `object-pick`, `number-lcm` | 배열·객체·문자열·숫자 처리 |
75
+ | 알고리즘·자료구조 | `algorithm-graph-dijkstra`, `data-structure-heap` | 그래프 탐색, 힙, 큐 등 |
76
+ | Tailwind CSS | `tailwindcss-cn`, `tailwindcss-create-tailwind-css` | 조건부 클래스와 variant 구성 |
77
+ | FSD lint | `lint-fsd` | 레이어 의존성과 디렉터리 구조 검사 |
78
+
79
+ 전체 목록은 다음 명령으로 확인할 수 있습니다.
64
80
 
65
81
  ```bash
66
- npx @no-k/hermes@0.1.0 add array-chunk number-lcm --dir src/shared/hermes
67
- npx @no-k/hermes@0.1.0 add array-chunk --dir "src/my utilities" --dry-run
82
+ npx @no-k/hermes@latest list
68
83
  ```
69
84
 
70
- 내용이 같은 기존 파일은 유지합니다. 내용이 다른 파일도 기본은 유지이며,
71
- 파일 검토 화면에서 Space로 교체할 파일만 체크하고 Tab으로 차이를 확인합니다.
72
- 최종 목록에서 ↑↓로 스크롤하고, `y`/`n` 또는 Tab으로 Yes/No를 선택한 뒤 Enter로 확정합니다.
73
- 기본 선택은 No이며 인자나 옵션을 모두 전달해도 최종 확인을 건너뛰지 않습니다.
74
- 검토 중 파일이나 설정이 바뀌면 다시 계획하고 확인받습니다. 변경된 충돌에는 이전 교체 승인을 적용하지 않습니다.
75
- 목적지의 심볼릭 링크를 통해 파일을 쓰지 않습니다.
85
+ Core 유틸에는 외부 런타임 npm 의존성이 없습니다.
86
+ 라이브러리 항목에 필요한 npm 패키지는 설치 결과에 안내되므로, 사용하는 패키지 매니저로 추가하세요.
87
+ Hermes는 프로젝트의 `package.json`과 lockfile을 자동으로 수정하지 않습니다.
76
88
 
77
- 설치는 입력·출력이 모두 연결된 대화형 터미널에서 실행합니다. 파이프·리다이렉트 환경의
78
- `add`는 오류 안내로 끝나고, 명령 없이 실행하면 도움말을 출력합니다. `list`, `--help`, `--version`은
79
- 항상 바로 출력합니다. `q` 취소는 종료 코드 0, Ctrl+C는 130, 입력 종료는 1입니다.
80
- 경로·검색 입력 중에는 `q`도 입력 문자이므로 Ctrl+C로 중단합니다.
89
+ `string-uuid`처럼 Node.js가 필요한 항목과 `dom-*`처럼 브라우저 DOM이 필요한 항목은
90
+ 선택 화면에서 실행 환경을 확인할 수 있습니다.
81
91
 
82
- ## 항목과 의존성
92
+ ### Tailwind CSS 헬퍼
83
93
 
84
- 항목 이름은 core 디렉터리 경로를 하이픈으로 연결한 이름입니다.
85
- 라이브러리 항목에는 tailwindcss-, lint-처럼 라이브러리 이름 접두어를 붙입니다.
94
+ `tailwindcss-cn`은 조건부 클래스를 합치고, 충돌하는 Tailwind 클래스를 정리합니다.
86
95
 
87
- | 항목 | 포함하는 소스 |
88
- | --- | --- |
89
- | array-chunk | 배열 분할 |
90
- | number-lcm | 최소공배수와 필요한 gcd |
91
- | algorithm-graph-dijkstra | 최단 거리와 필요한 우선순위 큐·힙 |
92
- | tailwindcss-cn | 클래스 병합과 설정 |
93
- | tailwindcss-create-tailwind-css | 설정을 공유하는 cn·classVariant |
94
- | string-uuid | Node.js UUID 생성 |
95
- | lint-fsd | 수정 가능한 FSD ESLint 플러그인 소스 |
96
-
97
- core 항목은 외부 런타임 npm 의존성이 없습니다.
98
- Tailwind 항목은 clsx, tailwind-merge, tailwind-variants 중 해당 항목이 참조하는 패키지가 필요합니다.
99
- CLI가 필요한 설치 명령을 출력합니다. 소비 프로젝트의 패키지 매니저에 맞춰 직접 설치하세요.
100
- CLI는 package.json이나 lockfile을 수정하지 않습니다.
101
- 설치 결과의 `npm install ...`은 같은 패키지·버전 범위를 유지한 채 `pnpm add ...`, `yarn add ...`,
102
- `bun add ...`로 바꿔 실행할 수 있습니다. `npm install --save-dev ...`는 각 도구의 `add -D ...`에
103
- 해당합니다. 이 명령도 유틸을 사용하는 앱·패키지 디렉터리에서 실행하고 lockfile을 함께 관리하세요.
104
- 내부 모듈의 필수 `dependencies`와 필수 `peerDependencies`는 소스로 함께 복사합니다.
105
- 서로 import하는 모듈의 버전 범위, 기존 설치 기록, 설치된 외부 패키지의 peer 범위를 검사하고
106
- 알려진 충돌이 있으면 파일을 쓰기 전에 중단합니다. 아직 없는 외부 패키지는 설치 명령으로 안내합니다.
107
-
108
- Node용 항목은 @types/node 설치 명령도 안내합니다.
109
- string-uuid는 정적 node:crypto import 때문에 Node.js에서 사용해야 합니다.
110
- dom-* 항목에는 DOM API와 DOM TypeScript 타입이 필요합니다.
111
- 현재 Tailwind 헬퍼의 병합 규칙은 Tailwind CSS 4 기준입니다.
112
-
113
- ## FSD lint 소스 생성
114
-
115
- 이 항목이 포함된 Hermes 릴리스 배포 후 사용합니다.
96
+ ```bash
97
+ npx @no-k/hermes@latest add tailwindcss-cn
98
+ npm install "clsx@^2.1.1" "tailwind-merge@^3.6.0"
99
+ ```
100
+
101
+ ```ts
102
+ // styles.ts — 프로젝트 루트의 파일
103
+ import { cn } from "./libs/hermes/tailwindcss/cn";
104
+
105
+ cn("px-2 py-1", "px-4"); // "py-1 px-4"
106
+ cn("text-sm", { "font-bold": true }); // "text-sm font-bold"
107
+ ```
108
+
109
+ 기본 병합 규칙은 Tailwind CSS 4를 기준으로 합니다.
110
+ 프로젝트에 맞춘 병합 설정을 `cn`과 `classVariant`에서 함께 사용하려면
111
+ `tailwindcss-create-tailwind-css`를 선택하세요.
112
+
113
+ ### FSD lint 규칙
114
+
115
+ `lint-fsd`는 Feature-Sliced Design의 레이어 의존 방향, 같은 레이어의 slice 간 참조,
116
+ public API 사용과 디렉터리 구조를 검사합니다. **Hermes 0.3.0 이상**에서 사용할 수 있습니다.
116
117
 
117
118
  ```bash
118
119
  npx @no-k/hermes@latest add lint-fsd
119
- pnpm add -D eslint typescript-eslint typescript jiti@^2.2.0 @types/node@^22
120
+ npm install --save-dev eslint typescript-eslint typescript "jiti@^2.2.0" "@types/node@^22"
120
121
  ```
121
122
 
122
- `libs/hermes/lint/fsd/`에 TypeScript 플러그인 진입점, 옵션, 경로 정책과 세 규칙을 생성합니다.
123
- 기존 프로젝트에 저장된 라이브러리 경로가 있으면 그 값을 사용합니다.
124
- 이번 설치 경로를 지정하려면 `--dir libs/hermes`를 붙이세요.
125
- 레이어 의존 방향·slice 격리, public API와 디렉터리 구조를 검사합니다.
126
- 생성된 파일은 직접 수정할 수 있으며, 다시 설치할 때도 변경된 파일은 기본 유지합니다.
127
- ESLint 설정과 package.json은 직접 연결합니다.
123
+ 기본 위치는 **`libs/hermes/lint/fsd`**입니다.
124
+ ESLint 9 또는 10의 flat config에 생성된 플러그인을 연결하세요.
125
+ 다음은 TypeScript 프로젝트의 설정 예시입니다.
128
126
 
129
127
  ```ts
130
128
  // eslint.config.ts
131
129
  import tseslint from "typescript-eslint";
132
130
  import fsd from "./libs/hermes/lint/fsd";
133
131
 
134
- export default [...tseslint.configs.recommended, fsd.configs.recommended];
132
+ export default [
133
+ ...tseslint.configs.recommended,
134
+ fsd.configs.recommended,
135
+ ];
135
136
  ```
136
137
 
137
- 기존 flat config에는 `fsd.configs.recommended`를 추가하세요.
138
- 기본 FSD 루트는 린터 실행 위치 기준 `src`이고 alias는 `@`·`~`입니다.
139
- 다른 구조는 `createConfig({ roots, aliases, layers })`로 지정할 수 있습니다.
140
- TypeScript 설정 로딩에 사용하는 `jiti`는 2.2 이상이 필요합니다.
141
- 옵션, 규칙 수정 위치와 같은 소스를 사용하는 Oxlint 설정은
142
- [FSD 모듈 문서](https://github.com/no-k/hermes/tree/main/packages/lib/lint/fsd)를 참고하세요.
138
+ ```bash
139
+ npx eslint src
140
+ ```
141
+
142
+ 기존 ESLint 설정이 있다면 그 설정에 `fsd.configs.recommended`를 추가하세요.
143
+ 위 예시는 `jiti`로 읽는 `eslint.config.ts`용입니다. `.js`·`.mjs` 설정에 연결하는 방법은
144
+ [FSD 사용 가이드](https://github.com/no-k/hermes/tree/main/packages/lib/lint/fsd#설치와-eslint-설정)를 참고하세요.
143
145
 
144
- ## 업데이트와 라이선스
146
+ 기본 검사 루트는 `src`, 경로 별칭은 `@`와 `~`입니다.
147
+ `createConfig`로 프로젝트 구조에 맞춰 설정하거나, 생성된 `options.ts`와 `rules/`를 직접 수정할 수 있습니다.
148
+ [프로젝트별 옵션과 Oxlint 사용법](https://github.com/no-k/hermes/tree/main/packages/lib/lint/fsd)도 제공합니다.
145
149
 
146
- 새 유틸을 찾을 때는 `@latest`로 최신 CLI에 포함된 카탈로그를 탐색합니다.
147
- 카탈로그는 실행 중 원격에서 갱신되지 않으므로 이전 버전으로 실행하면 그 버전의 항목만 보입니다.
148
- 같은 카탈로그와 템플릿을 다시 사용하려면 CLI 버전을 확인하고 명령에서 고정하세요.
150
+ ## 생성 위치 지정하기
151
+
152
+ | 분류 | 기본 경로 |
153
+ | --- | --- |
154
+ | Core | `src/utils/hermes` |
155
+ | 라이브러리 — Tailwind CSS, lint | `libs/hermes` |
156
+
157
+ 경로는 선택 화면에서 바꾸거나 `--dir`로 지정할 수 있습니다.
158
+ 그 아래에는 `array/chunk`, `lint/fsd`처럼 항목별 폴더 구조가 만들어집니다.
149
159
 
150
160
  ```bash
151
- npx @no-k/hermes@latest --version
152
- # 확인한 버전이 0.1.0인 경우
153
- npx @no-k/hermes@0.1.0
154
- npx @no-k/hermes@0.1.0 list
161
+ npx @no-k/hermes@latest add array-chunk number-lcm --dir src/shared/hermes
162
+ npx @no-k/hermes@latest add lint-fsd --dir libs/hermes
155
163
  ```
156
164
 
157
- 설치할 때는 `@latest`와 버전 고정 모두 선택 화면과 최종 Yes/No를 거칩니다.
158
- 안내하는 외부 의존성은 버전 범위를 사용하므로 정확한 설치 버전은 소비 프로젝트 lockfile로 관리합니다.
159
- 이미 복사한 소스는 새 버전이 나와도 자동 변경되지 않습니다.
160
- 업데이트할 때는 직접 수정한 부분과 공통 보조 파일의 호환성을 함께 검토하세요.
161
- 유틸별 버전은 CLI 배포 버전과 별개입니다. 예를 들어 `number-lcm@1.0.0`과
162
- `number-gcd@1.2.0`을 같은 CLI가 포함할 수 있습니다. 한 설치 위치에는 모듈별 한 버전만 둡니다.
163
- 과거 버전을 별도로 내려받아 조합하는 기능은 제공하지 않습니다.
165
+ 경로는 **`--dir` → 현재 프로젝트에 저장된 설정 → 기본값** 순서로 적용합니다.
166
+ 이전에 다른 경로를 사용했다면 `--dir libs/hermes`로 라이브러리 위치를 다시 지정할 수 있습니다.
167
+ 공백이 포함된 경로는 따옴표로 감싸세요.
164
168
 
165
- 각 유틸의 `package.json`과 `CHANGELOG.md`, Changesets 사용법은
166
- [독립 버전 관리](docs/module-versioning.md)를 참고하세요.
169
+ 모노레포에서는 코드를 사용할 앱이나 패키지 디렉터리에서 실행하세요.
170
+ 각 디렉터리의 `no-k.config.ts`로 설치 위치를 관리합니다.
167
171
 
168
- ISC 라이선스입니다. 함께 생성되는 LICENSE의 저작권·허가 문구를 유지해 주세요.
172
+ ## 수정한 코드와 업데이트
169
173
 
170
- ## 개발 및 검증
174
+ 생성된 소스는 자유롭게 수정할 수 있습니다. Hermes의 새 버전이 나와도 기존 파일이 자동으로 바뀌지 않습니다.
171
175
 
172
- CLI 소스와 관련 도구는 이 패키지 안에서 TypeScript로 관리합니다.
176
+ 업데이트하려면 최신 CLI에서 같은 항목을 다시 선택하세요.
177
+ 내용이 다른 파일은 기본적으로 유지되며, 차이를 확인한 뒤 교체할 파일만 고를 수 있습니다.
178
+ 실제 파일을 쓰기 전에 확인하려면 `--dry-run`을 사용하세요.
173
179
 
174
- | 경로 | 역할 |
175
- | --- | --- |
176
- | `src/bin/hermes.ts` | 대화형 설치 진입, 조회 명령과 결과 출력 |
177
- | `src/lib/install-wizard.ts` | 선택·경로·충돌·최종 확인 연결과 변경된 계획 재검토 |
178
- | `src/lib/install-prompts.ts`, `install-view.tsx` | 경로 입력과 스크롤 가능한 최종 Yes/No 화면 |
179
- | `src/lib/install.ts` | 쓰기 없는 설치 계획 계산, 파일별 충돌 결정, 설정 저장을 포함한 실행 |
180
- | `src/lib/files.ts` | 목적지 검사, 계획 이후 변경 감지와 파일 쓰기 |
181
- | `src/lib/project-config.ts`, `config-syntax.ts` | 설정 읽기·검증, 원문 보존 갱신과 설치 목록 병합 |
182
- | `src/lib/report.ts` | 파일 결과·외부 의존성·실행 환경 안내 |
183
- | `src/lib/catalog.ts`, `json.ts` | catalog 타입과 JSON 검증 |
184
- | `src/lib/catalog-query.ts` | 분류 계층 조회, 이름·전체 설명 검색, 분류 필터와 표시용 요약 |
185
- | `src/lib/catalog-selection.ts` | 대분류·하위 분류·유틸 선택과 이전 선택 복원 |
186
- | `src/lib/picker.ts`, `picker-view.tsx`, `terminal.ts`, `terminal-text.ts` | 선택 상태, Ink 화면, 터미널 입력과 화면 복원 |
187
- | `src/lib/installation-status.ts` | 설치 기록과 실제 파일을 대조한 상태·위치 표시 |
188
- | `src/lib/conflict-selection.ts` | 기존 파일과 생성 후보의 차이 검토, 파일별 유지·교체 선택 |
189
- | `src/lib/versions.ts` | 실제 파일 해시와 설치 버전 대조, 내부 의존성·외부 peer 호환성 검사 |
190
- | `scripts/catalog-lib.ts` | 유틸 탐색, 의존성 추적과 README 설명 추출 |
191
- | `scripts/build-cli.ts` | catalog와 템플릿 생성 |
192
- | `scripts/version-modules.ts` | Changesets 버전 적용 시 유틸 변경을 CLI 배포 버전에 연결 |
193
- | `scripts/preview-ui.ts` | 파일을 생성하지 않는 개발용 선택 화면 미리보기 |
194
- | `scripts/pack-cli.ts` | npm tarball 생성 및 포함 파일 검증 |
195
- | `scripts/smoke-cli.ts` | tarball을 설치해 소비 프로젝트에서 컴파일·실행 |
196
- | `scripts/smoke-fsd.ts` | CLI로 lint 소스를 생성하고 ESLint·Oxlint 실행, 직접 수정과 재설치 보존 검증 |
197
- | `test/*.test.ts` | CLI·catalog·패키징 회귀 테스트 |
198
-
199
- 대화형 설치는 [단계별 계약](docs/interactive-install.md)에 따라 설치 엔진·설정, 카탈로그 탐색,
200
- 독립 모듈 버전 관리와 터미널 선택 화면을 연결합니다.
201
- `hermes`와 `hermes add` 모두 대화형 선택 후 최종 Yes에서 설치하고 설정과 버전을 저장합니다.
202
-
203
- 개발용 화면은 다음 명령으로 확인할 수 있습니다. 선택 결과를 표시하고 파일을 생성하지 않습니다.
204
- 매번 전체 빌드하지 않고 `tsx`로 화면 소스를 실행합니다. 기존 catalog.json을 읽으며,
205
- 아직 빌드하지 않았다면 소스에서 카탈로그를 메모리에 구성합니다.
180
+ ```bash
181
+ npx @no-k/hermes@latest add lint-fsd --dry-run
182
+ ```
183
+
184
+ 팀에서 같은 CLI 버전을 사용하려면 버전을 명시할 수 있습니다.
206
185
 
207
186
  ```bash
208
- pnpm --filter @no-k/hermes run preview:ui
209
- # 초기 선택값을 넣어 탐색할 수도 있습니다.
210
- pnpm --filter @no-k/hermes run preview:ui number-lcm tailwindcss-cn
211
- # 결과와 재진입 상태를 JSON으로 확인합니다.
212
- pnpm --filter @no-k/hermes run preview:ui --json number-lcm
187
+ npx @no-k/hermes@0.3.0 add lint-fsd
213
188
  ```
214
189
 
215
- 현재 단계, 포커스, 체크 수를 색상으로 구분합니다. 101열 이상에서는 목록 옆에 상세 패널이
216
- 표시되고, 좁거나 낮은 터미널에서는 한 열로 전환합니다. 키 입력을 즉시 선택 상태에 반영하고
217
- 연속 입력의 화면 갱신을 합칩니다. 바뀐 줄만 최대 60fps로 출력해 전체 화면 지우기를 줄입니다.
190
+ 항목에 필요한 공통 보조 파일도 함께 생성됩니다.
191
+ 여러 유틸이 공유하는 코드를 수정했다면 업데이트할 때 함께 사용하는 유틸도 확인하세요.
218
192
 
219
- 대분류 → 각 대분류의 하위 분류 → 각 하위 분류의 유틸 순서로 선택합니다.
220
- ↑↓로 이동하고 Space로 체크하며 Enter로 다음 화면에 갑니다. `/`로 검색을 시작하고
221
- Enter로 검색 입력을 끝냅니다. Tab은 상세 보기, Esc는 상세·검색 닫기 또는 이전 화면,
222
- `a`는 검색 결과 전체 선택·해제, Ctrl+L은 검색 초기화, `q`는 취소입니다.
223
- 목록에서는 `j`/`k`로도 이동할 수 있습니다. 한국어 검색과 bracketed paste를 지원합니다.
224
- 분류를 해제하면 그 안의 선택은 설치 대상에서 제외되고, 다시 선택하면 체크가 복원됩니다.
225
- 상세 화면에서 함께 생성되는 의존 모듈, npm·peer 의존성, 실행 환경과 기존 설치 위치를 확인할 수 있습니다.
193
+ ## 명령어
226
194
 
227
- 미리보기는 실행 디렉터리의 설정을 읽습니다. 위 pnpm 명령의 실행 디렉터리는 `packages/cli`입니다.
228
- 다른 앱의 설치 기록을 보려면 CLI를 빌드한 뒤 그 앱 디렉터리에서
229
- `node /저장소/절대경로/packages/cli/.build/scripts/preview-ui.js`를 실행합니다.
195
+ | 명령·옵션 | 설명 |
196
+ | --- | --- |
197
+ | `npx @no-k/hermes@latest` | 전체 목록에서 선택 시작 |
198
+ | `add <항목...>` | 지정한 항목을 미리 선택 |
199
+ | `list` | 전체 항목과 설명 출력 |
200
+ | `--dir <경로>` | 생성 위치 지정 |
201
+ | `--dry-run` | 파일과 설정을 쓰지 않고 설치 계획 확인 |
202
+ | `--overwrite` | 파일 검토 화면에서 교체 후보를 미리 선택 |
203
+ | `--help` | 도움말 |
204
+ | `--version` | CLI 버전 확인 |
230
205
 
231
- 설정 모듈은 `export default { ... }`의 데이터 리터럴, 주석, 마지막 쉼표, 최상위 `as const`를
232
- 지원합니다. 사용자 필드와 주석을 보존하기 위해 필요한 값만 수정하며, import·함수·변수 참조 등
233
- 지원하지 않는 구문은 위치를 안내하고 원본을 유지합니다. 설정 파일의 코드를 실행하지 않습니다.
206
+ 설치는 대화형 터미널에서 진행합니다. `--overwrite`를 사용해도 최종 확인 단계는 유지됩니다.
234
207
 
235
- 저장소 루트에서 실행합니다.
208
+ ## 더 알아보기
236
209
 
237
- ```bash
238
- pnpm install --frozen-lockfile
239
- pnpm run build:cli
240
- pnpm run check
241
- pnpm run test:run
242
- pnpm run check:cli
243
- pnpm run pack:cli
244
- pnpm run test:cli-install
245
- pnpm run test:fsd-install
246
- ```
210
+ - [FSD 설정과 Oxlint 사용법](https://github.com/no-k/hermes/tree/main/packages/lib/lint/fsd)
211
+ - [변경 이력](https://github.com/no-k/hermes/blob/main/packages/cli/CHANGELOG.md)
212
+ - [문제 신고·기능 제안](https://github.com/no-k/hermes/issues)
213
+ - [기여자를 위한 개발 가이드](https://github.com/no-k/hermes/blob/main/packages/cli/docs/development.md)
214
+
215
+ ## 라이선스
247
216
 
248
- `tsconfig.json`은 런타임·도구·테스트 전체를 strict 및 `noUncheckedIndexedAccess`로 검사합니다.
249
- NodeNext 모듈 해석과 ES2022 출력을 사용하며, 상대 import에는 `.js` 확장자를 씁니다.
250
- `tsconfig.build.json`은 런타임을 `dist/`에, `tsconfig.tools.json`은 도구·테스트와 참조하는
251
- 공유 모듈을 `.build/`에 컴파일합니다. 타입 오류가 있으면 해당 컴파일의 출력은 생성되지 않습니다.
252
-
253
- 빌드는 이전 `dist/`, `.build/`, `templates/`, `catalog.json`을 지우고 컴파일한 다음
254
- catalog와 템플릿을 생성합니다. 항목 설명은 각 유틸 README의 첫 문단 전체에서 가져옵니다.
255
- 생성된 catalog를 직접 수정하지 말고 해당 README를 수정하세요.
256
-
257
- 카탈로그 스키마 3은 각 항목의 `scope`(core/lib)와 `category`(core/array, lib/tailwindcss 등)를
258
- 원본 진입 경로에서 생성합니다. Core의 하위 분류와 `packages/lib`의 라이브러리 패키지를 빌드 시
259
- 탐색하므로 새 유틸이나 분류가 자동 반영됩니다. 유틸 ID나 보조 파일 순서로 소속을 추측하지 않습니다.
260
- 각 모듈의 manifest에서 패키지 이름·버전·의존 범위를 읽고, 함께 복사하는 모듈과 파일 해시를 기록합니다.
261
- 프로젝트 설정인 no-k.config.ts의 스키마는 1을 유지하며 기존 위치 기록도 읽을 수 있습니다.
262
-
263
- `filterCatalog`는 이름·소개 문단 전체 검색과 대분류·하위 분류 필터를 조합합니다.
264
- 검색어를 공백으로 나누어 모두 일치하는 항목을 찾으며, 한글 Unicode 표현과 영문 대소문자를
265
- 정규화합니다. `getCatalogGroups`는 같은 결과를 대분류별 하위 분류와 유틸 목록으로 구성합니다.
266
- 필터 생략은 전체 조회, 빈 배열은 선택 없음입니다. `summarizeDescription`의 화면용 요약은
267
- 전체 설명이나 검색 결과를 바꾸지 않습니다. 선택 화면은 이 조회 함수들을 재사용하며,
268
- 검색 결과 밖에 있는 체크도 유지하고 전체 선택 수를 표시합니다.
269
-
270
- `check:cli`, `pack:cli`, `test:cli-install`은 빌드부터 실행합니다.
271
- 직접 `npm pack` 또는 `pnpm pack`을 실행해도 `prepack`이 같은 빌드를 수행합니다.
272
- 검증 스크립트 내부의 `npm pack --ignore-scripts`는 이미 빌드한 결과를 포장해 중복 빌드를 피합니다.
273
-
274
- npm 패키지에는 `dist/`의 ESM JavaScript, `catalog.json`, `templates/`의 TypeScript 유틸,
275
- README·LICENSE·package.json이 포함됩니다. CLI TypeScript 소스, 개발 도구와 테스트는 포함하지 않습니다.
276
- TypeScript·타입 패키지·tsx는 개발 의존성이며, 배포된 CLI의 직접 런타임 의존성은
277
- `ink`, `react`, `semver`입니다. 소비 프로젝트에 생성되는 유틸에는 UI 의존성을 추가하지 않습니다.
278
- `pack:cli`는 `artifacts/`에 tarball을 만들고 파일 목록·실행 권한을 검사합니다.
279
- `test:cli-install`은 그 tarball을 별도 소비 프로젝트에서 검증하며 npm에 배포하지 않습니다.
280
- 첫 `npx` 실행은 런타임 의존성을 내려받고, 다음 실행은 같은 캐시에서 오프라인으로 검증합니다.
281
- PTY 통합 검증은 macOS·Linux의 `script` 명령으로 실제 터미널 입력을 전달합니다.
282
- 한국어 탐색과 여러 분류 선택, 별도 경로 저장·재사용, No 이후 파일별 결정 유지,
283
- 최종 확인 중 파일 변경, 화면별 취소, 입력·출력 각각의 리다이렉트를 검사합니다.
284
- 최종 Yes 이전에는 파일이 생기거나 기존 파일이 바뀌지 않는지도 실제 실행 중 확인합니다.
285
- Windows에서는 이 PTY 테스트를 건너뛰며 `test:cli-install`은 macOS·Linux에서 실행합니다.
217
+ [ISC](https://github.com/no-k/hermes/blob/main/packages/cli/LICENSE). 생성된 코드와 함께 제공되는 `LICENSE`의 저작권·허가 문구를 유지해 주세요.
package/catalog.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 3,
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "items": [
5
5
  {
6
6
  "name": "algorithm-geometry-ccw",
@@ -0,0 +1,77 @@
1
+ import path from "node:path";
2
+ import { readDestination } from "./files.js";
3
+ import { resolveInstallDirectory } from "./project-config.js";
4
+ function appendExports(existing, entries) {
5
+ // 이미 내보내는 경로를 확인하되 주석과 문자열 안의 예시는 export로 취급하지 않는다.
6
+ const exports = new Set([
7
+ ...existing.matchAll(/\/\/[^\r\n]*|\/\*[\s\S]*?\*\/|"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`(?:\\.|[^`\\])*`|\bexport\s*\*\s*from\s*["']([^"'\r\n]+)["']/g),
8
+ ].flatMap((match) => (match[1] ? [match[1].replace(/\/index(?:\.[jt]s)?$/, "")] : [])));
9
+ const missing = entries.filter((entry) => !exports.has(entry));
10
+ if (!missing.length)
11
+ return existing;
12
+ const newline = existing.includes("\r\n") ? "\r\n" : "\n";
13
+ return (existing +
14
+ (existing && !existing.endsWith("\n") ? newline : "") +
15
+ missing.map((entry) => "export * from " + JSON.stringify(entry) + ";" + newline).join(""));
16
+ }
17
+ export function planBarrelFiles(request, catalog, sourceFiles, compatibilityGuards) {
18
+ const cwd = path.dirname(request.config.destination);
19
+ const roots = new Map();
20
+ const planned = new Set(sourceFiles.map((file) => file.destination));
21
+ const guards = new Map(compatibilityGuards.map((file) => [file.destination, file]));
22
+ for (const target of request.targets) {
23
+ const directory = resolveInstallDirectory(target.directory, cwd);
24
+ const entries = roots.get(directory) ?? new Set();
25
+ for (const item of catalog.items.filter((item) => target.names.includes(item.name)))
26
+ for (const file of item.files)
27
+ if (file.endsWith("/index.ts"))
28
+ entries.add(file);
29
+ roots.set(directory, entries);
30
+ }
31
+ // 배럴을 만들지 않던 버전의 설치도 복원한다. 다른 목적지의 설치 기록은 섞지 않는다.
32
+ for (const record of request.config.status === "loaded" ? request.config.value.installed : []) {
33
+ const entries = roots.get(resolveInstallDirectory(record.directory, cwd));
34
+ if (entries) {
35
+ const files = record.source
36
+ ? record.source.modules.flatMap((module) => Object.keys(module.files))
37
+ : (catalog.items.find((item) => item.name === record.name && item.scope === record.scope)?.files ?? []);
38
+ for (const file of files)
39
+ if (file.endsWith("/index.ts"))
40
+ entries.add(file);
41
+ }
42
+ }
43
+ const files = [];
44
+ for (const [directory, candidates] of roots) {
45
+ const entries = [...candidates].sort().flatMap((file) => {
46
+ const destination = path.join(directory, file);
47
+ if (!planned.has(destination)) {
48
+ let guard = guards.get(destination);
49
+ if (!guard) {
50
+ const existingContent = readDestination(destination);
51
+ guard = {
52
+ file,
53
+ destination,
54
+ existingContent,
55
+ content: existingContent ?? "",
56
+ action: existingContent === null ? "create" : "keep",
57
+ };
58
+ guards.set(destination, guard);
59
+ }
60
+ if (guard.existingContent === null)
61
+ return [];
62
+ }
63
+ return ["./" + file.slice(0, -"/index.ts".length)];
64
+ });
65
+ const destination = path.join(directory, "index.ts");
66
+ const existingContent = readDestination(destination);
67
+ const content = appendExports(existingContent ?? "", entries);
68
+ files.push({
69
+ file: "index.ts",
70
+ destination,
71
+ content,
72
+ existingContent,
73
+ action: existingContent === null ? "create" : existingContent === content ? "keep" : "replace",
74
+ });
75
+ }
76
+ return { files, compatibilityGuards: [...guards.values()] };
77
+ }
@@ -1,5 +1,6 @@
1
1
  import { readFileSync, realpathSync } from "node:fs";
2
2
  import path from "node:path";
3
+ import { planBarrelFiles } from "./barrel.js";
3
4
  import { isSourcePath, isUtilityScope } from "./catalog.js";
4
5
  import { readDestination, validateFilePlans, writePlannedFile } from "./files.js";
5
6
  import { PROJECT_CONFIG_FILENAME, mergeProjectConfig, planProjectConfig, resolveInstallDirectory, } from "./project-config.js";
@@ -126,15 +127,19 @@ export function createInstallPlan(request, catalog, packageDirectory) {
126
127
  const utilities = planUtilityFiles(targets, packageDirectory, request.fileDecisions);
127
128
  const { config, dependencies, compatibilityErrors, compatibilityGuards } = planInstalledVersions(mergeProjectConfig(request.config, request.targets), targets, utilities.files, catalog, cwd);
128
129
  const configFile = planProjectConfig(request.config, config);
129
- validateFilePlans([...utilities.files, configFile]);
130
+ const barrels = planBarrelFiles(request, catalog, utilities.files, compatibilityGuards);
131
+ const files = [...utilities.files, ...barrels.files].sort((a, b) => a.destination.localeCompare(b.destination));
132
+ validateFilePlans(barrels.compatibilityGuards);
133
+ validateFilePlans([...files, configFile]);
130
134
  return {
131
135
  ...request,
132
136
  ...utilities,
137
+ files,
133
138
  dependencies,
134
139
  configFile,
135
140
  nextConfig: config,
136
141
  compatibilityErrors,
137
- compatibilityGuards,
142
+ compatibilityGuards: barrels.compatibilityGuards,
138
143
  };
139
144
  }
140
145
  export class InstallExecutionError extends Error {
@@ -0,0 +1,176 @@
1
+ # Hermes 개발 가이드
2
+
3
+ Hermes의 코드를 수정하거나 새 항목을 추가하는 기여자를 위한 문서입니다.
4
+ 설치와 사용 방법은 [사용 가이드](../README.md)를 참고하세요.
5
+
6
+ ## CLI 구조
7
+
8
+ CLI 소스와 관련 도구는 `packages/cli` 안에서 TypeScript로 관리합니다.
9
+ 터미널 UI는 Ink·React, 버전 범위 검사는 `semver`를 사용합니다.
10
+
11
+ | 경로 | 역할 |
12
+ | --- | --- |
13
+ | `src/bin/hermes.ts` | 대화형 설치 진입, 조회 명령과 결과 출력 |
14
+ | `src/lib/install-wizard.ts` | 선택·경로·충돌·최종 확인 연결과 변경된 계획 재검토 |
15
+ | `src/lib/install-prompts.ts`, `install-view.tsx` | 경로 입력과 스크롤 가능한 최종 Yes/No 화면 |
16
+ | `src/lib/install.ts` | 쓰기 없는 설치 계획 계산, 파일별 충돌 결정, 설정 저장을 포함한 실행 |
17
+ | `src/lib/barrel.ts` | 설치 루트별 배럴 생성, 기존 export 보존과 누락된 export 추가 |
18
+ | `src/lib/files.ts` | 목적지 검사, 계획 이후 변경 감지와 파일 쓰기 |
19
+ | `src/lib/project-config.ts`, `config-syntax.ts` | 설정 읽기·검증, 원문 보존 갱신과 설치 목록 병합 |
20
+ | `src/lib/report.ts` | 파일 결과·외부 의존성·실행 환경 안내 |
21
+ | `src/lib/catalog.ts`, `json.ts` | catalog 타입과 JSON 검증 |
22
+ | `src/lib/catalog-query.ts` | 분류 계층 조회, 이름·전체 설명 검색, 분류 필터와 표시용 요약 |
23
+ | `src/lib/catalog-selection.ts` | 대분류·하위 분류·유틸 선택과 이전 선택 복원 |
24
+ | `src/lib/picker.ts`, `picker-view.tsx`, `terminal.ts`, `terminal-text.ts` | 선택 상태, Ink 화면, 터미널 입력과 화면 복원 |
25
+ | `src/lib/installation-status.ts` | 설치 기록과 실제 파일을 대조한 상태·위치 표시 |
26
+ | `src/lib/conflict-selection.ts` | 기존 파일과 생성 후보의 차이 검토, 파일별 유지·교체 선택 |
27
+ | `src/lib/versions.ts` | 실제 파일 해시와 설치 버전 대조, 내부 의존성·외부 peer 호환성 검사 |
28
+ | `scripts/catalog-lib.ts` | 유틸 탐색, 의존성 추적과 README 설명 추출 |
29
+ | `scripts/build-cli.ts` | catalog와 템플릿 생성 |
30
+ | `scripts/version-modules.ts` | Changesets 버전 적용 시 유틸 변경을 CLI 배포 버전에 연결 |
31
+ | `scripts/preview-ui.ts` | 파일을 생성하지 않는 개발용 선택 화면 미리보기 |
32
+ | `scripts/pack-cli.ts` | npm tarball 생성 및 포함 파일 검증 |
33
+ | `scripts/smoke-cli.ts` | tarball을 설치해 소비 프로젝트에서 컴파일·실행 |
34
+ | `scripts/smoke-fsd.ts` | CLI로 lint 소스를 생성하고 ESLint·Oxlint 실행, 직접 수정과 재설치 보존 검증 |
35
+ | `test/*.test.ts` | CLI·catalog·패키징 회귀 테스트 |
36
+
37
+ 대화형 설치는 [단계별 계약](interactive-install.md)에 따라 설치 엔진·설정, 카탈로그 탐색,
38
+ 독립 모듈 버전 관리와 터미널 선택 화면을 연결합니다.
39
+ `hermes`와 `hermes add` 모두 대화형 선택 후 최종 Yes에서 설치하고 설정과 버전을 저장합니다.
40
+
41
+ ## 화면 개발
42
+
43
+ 개발용 화면은 다음 명령으로 확인할 수 있습니다. 선택 결과를 표시하고 파일을 생성하지 않습니다.
44
+ 매번 전체 빌드하지 않고 `tsx`로 화면 소스를 실행합니다. 기존 catalog.json을 읽으며,
45
+ 아직 빌드하지 않았다면 소스에서 카탈로그를 메모리에 구성합니다.
46
+
47
+ ```bash
48
+ pnpm --filter @no-k/hermes run preview:ui
49
+ # 초기 선택값을 넣어 탐색할 수도 있습니다.
50
+ pnpm --filter @no-k/hermes run preview:ui number-lcm tailwindcss-cn
51
+ # 결과와 재진입 상태를 JSON으로 확인합니다.
52
+ pnpm --filter @no-k/hermes run preview:ui --json number-lcm
53
+ ```
54
+
55
+ 현재 단계, 포커스, 체크 수를 색상으로 구분합니다. 101열 이상에서는 목록 옆에 상세 패널이
56
+ 표시되고, 좁거나 낮은 터미널에서는 한 열로 전환합니다. 키 입력을 즉시 선택 상태에 반영하고
57
+ 연속 입력의 화면 갱신을 합칩니다. 바뀐 줄만 최대 60fps로 출력해 전체 화면 지우기를 줄입니다.
58
+
59
+ 대분류 → 각 대분류의 하위 분류 → 각 하위 분류의 유틸 순서로 선택합니다.
60
+ ↑↓로 이동하고 Space로 체크하며 Enter로 다음 화면에 갑니다. `/`로 검색을 시작하고
61
+ Enter로 검색 입력을 끝냅니다. Tab은 상세 보기, Esc는 상세·검색 닫기 또는 이전 화면,
62
+ `a`는 검색 결과 전체 선택·해제, Ctrl+L은 검색 초기화, `q`는 취소입니다.
63
+ 목록에서는 `j`/`k`로도 이동할 수 있습니다. 한국어 검색과 bracketed paste를 지원합니다.
64
+ 분류를 해제하면 그 안의 선택은 설치 대상에서 제외되고, 다시 선택하면 체크가 복원됩니다.
65
+ 상세 화면에서 함께 생성되는 의존 모듈, npm·peer 의존성, 실행 환경과 기존 설치 위치를 확인할 수 있습니다.
66
+
67
+ 미리보기는 실행 디렉터리의 설정을 읽습니다. 위 pnpm 명령의 실행 디렉터리는 `packages/cli`입니다.
68
+ 다른 앱의 설치 기록을 보려면 CLI를 빌드한 뒤 그 앱 디렉터리에서
69
+ `node /저장소/절대경로/packages/cli/.build/scripts/preview-ui.js`를 실행합니다.
70
+
71
+ 설정 모듈은 `export default { ... }`의 데이터 리터럴, 주석, 마지막 쉼표, 최상위 `as const`를
72
+ 지원합니다. 사용자 필드와 주석을 보존하기 위해 필요한 값만 수정하며, import·함수·변수 참조 등
73
+ 지원하지 않는 구문은 위치를 안내하고 원본을 유지합니다. 설정 파일의 코드를 실행하지 않습니다.
74
+
75
+ ## 검증 명령
76
+
77
+ 저장소 루트에서 실행합니다.
78
+
79
+ ```bash
80
+ pnpm install --frozen-lockfile
81
+ pnpm run build:cli
82
+ pnpm run check
83
+ pnpm run test:run
84
+ pnpm run check:cli
85
+ pnpm run pack:cli
86
+ pnpm run test:cli-install
87
+ pnpm run test:fsd-install
88
+ ```
89
+
90
+ ## 빌드와 카탈로그
91
+
92
+ `tsconfig.json`은 런타임·도구·테스트 전체를 strict 및 `noUncheckedIndexedAccess`로 검사합니다.
93
+ NodeNext 모듈 해석과 ES2022 출력을 사용하며, 상대 import에는 `.js` 확장자를 씁니다.
94
+ `tsconfig.build.json`은 런타임을 `dist/`에, `tsconfig.tools.json`은 도구·테스트와 참조하는
95
+ 공유 모듈을 `.build/`에 컴파일합니다. 타입 오류가 있으면 해당 컴파일의 출력은 생성되지 않습니다.
96
+
97
+ 빌드는 이전 `dist/`, `.build/`, `templates/`, `catalog.json`을 지우고 컴파일한 다음
98
+ catalog와 템플릿을 생성합니다. 항목 설명은 각 유틸 README의 첫 문단 전체에서 가져옵니다.
99
+ 생성된 catalog를 직접 수정하지 말고 해당 README를 수정하세요.
100
+
101
+ 카탈로그 스키마 3은 각 항목의 `scope`(core/lib)와 `category`(core/array, lib/tailwindcss 등)를
102
+ 원본 진입 경로에서 생성합니다. Core의 하위 분류와 `packages/lib`의 라이브러리 패키지를 빌드 시
103
+ 탐색하므로 새 유틸이나 분류가 자동 반영됩니다. 유틸 ID나 보조 파일 순서로 소속을 추측하지 않습니다.
104
+ 각 모듈의 manifest에서 패키지 이름·버전·의존 범위를 읽고, 함께 복사하는 모듈과 파일 해시를 기록합니다.
105
+ 프로젝트 설정인 no-k.config.ts의 스키마는 1을 유지하며 기존 위치 기록도 읽을 수 있습니다.
106
+
107
+ `filterCatalog`는 이름·소개 문단 전체 검색과 대분류·하위 분류 필터를 조합합니다.
108
+ 검색어를 공백으로 나누어 모두 일치하는 항목을 찾으며, 한글 Unicode 표현과 영문 대소문자를
109
+ 정규화합니다. `getCatalogGroups`는 같은 결과를 대분류별 하위 분류와 유틸 목록으로 구성합니다.
110
+ 필터 생략은 전체 조회, 빈 배열은 선택 없음입니다. `summarizeDescription`의 화면용 요약은
111
+ 전체 설명이나 검색 결과를 바꾸지 않습니다. 선택 화면은 이 조회 함수들을 재사용하며,
112
+ 검색 결과 밖에 있는 체크도 유지하고 전체 선택 수를 표시합니다.
113
+
114
+ ## 배포 파일 검증
115
+
116
+ `check:cli`, `pack:cli`, `test:cli-install`은 빌드부터 실행합니다.
117
+ 직접 `npm pack` 또는 `pnpm pack`을 실행해도 `prepack`이 같은 빌드를 수행합니다.
118
+ 검증 스크립트 내부의 `npm pack --ignore-scripts`는 이미 빌드한 결과를 포장해 중복 빌드를 피합니다.
119
+
120
+ npm 패키지에는 `dist/`의 ESM JavaScript, `catalog.json`, `templates/`의 TypeScript 유틸,
121
+ README·LICENSE·package.json과 `docs/` 문서가 포함됩니다. CLI TypeScript 소스, 개발 도구와 테스트는 포함하지 않습니다.
122
+ TypeScript·타입 패키지·tsx는 개발 의존성이며, 배포된 CLI의 직접 런타임 의존성은
123
+ `ink`, `react`, `semver`입니다. 소비 프로젝트에 생성되는 유틸에는 UI 의존성을 추가하지 않습니다.
124
+ `pack:cli`는 `artifacts/`에 tarball을 만들고 파일 목록·실행 권한을 검사합니다.
125
+ `test:cli-install`은 그 tarball을 별도 소비 프로젝트에서 검증하며 npm에 배포하지 않습니다.
126
+ 첫 `npx` 실행은 런타임 의존성을 내려받고, 다음 실행은 같은 캐시에서 오프라인으로 검증합니다.
127
+ PTY 통합 검증은 macOS·Linux의 `script` 명령으로 실제 터미널 입력을 전달합니다.
128
+ 한국어 탐색과 여러 분류 선택, 별도 경로 저장·재사용, No 이후 파일별 결정 유지,
129
+ 최종 확인 중 파일 변경, 화면별 취소, 입력·출력 각각의 리다이렉트를 검사합니다.
130
+ 최종 Yes 이전에는 파일이 생기거나 기존 파일이 바뀌지 않는지도 실제 실행 중 확인합니다.
131
+ Windows에서는 이 PTY 테스트를 건너뛰며 `test:cli-install`은 macOS·Linux에서 실행합니다.
132
+
133
+ ## FSD lint 개발
134
+
135
+ ### 저장소 호환 preset
136
+
137
+ `configs/eslint`는 새 플러그인을 재노출하는 내부 패키지로 유지합니다.
138
+ 규칙 이름 세 개는 동일하고 저장소는 `plugin.configs.hermes`를 사용합니다.
139
+
140
+ ```js
141
+ // eslint.config.mjs — 저장소 루트
142
+ import hermes from "./configs/eslint/dist/index.js";
143
+
144
+ export default [hermes.configs.hermes];
145
+ ```
146
+
147
+ 이 preset은 `hermes/` namespace와 기존 `widgets` 이상 cross-slice 예외,
148
+ 선택적인 `processes` 레이어를 유지합니다.
149
+ 공개 `recommended` preset은 이 예외를 기본으로 켜지 않습니다.
150
+ 이전의 임의 경로에서 레이어 이름을 찾던 동작은 명시적인 `roots`로 바뀌었습니다.
151
+ `aliases: ["@"]` 형태는 `aliases: { "@": "." }`로 이전하세요.
152
+ `app`의 slice 없는 구조, Shared 하위 모듈의 public API와 `@x`는 공식 구조에 맞춰 보완했습니다.
153
+
154
+ ### FSD 검증과 버전 관리
155
+
156
+ 저장소 루트에서 실행합니다.
157
+
158
+ ```bash
159
+ pnpm run check:fsd
160
+ pnpm run pack:cli
161
+ pnpm run test:fsd-install
162
+ ```
163
+
164
+ 규칙은 `packages/lib/lint/fsd/rules`, 린터 API와 무관한 경로·정책 판정은 `packages/lib/lint/fsd/fsd.ts`,
165
+ AST 방문 연결은 `packages/lib/lint/fsd/import-listener.ts`에 있습니다.
166
+ 공통 fixture를 ESLint RuleTester와 실제 oxlint 프로세스에서 실행합니다.
167
+ 설치 검증은 Hermes CLI tarball로 임시 소비 프로젝트에 소스를 생성한 뒤
168
+ ESLint 9·10과 oxlint에서 실행하고, 생성 파일을 수정한 결과도 확인합니다. npm에 publish하지 않습니다.
169
+
170
+ `@hermes/lint-fsd`는 카탈로그의 버전·의존성 추적용 private 모듈 이름입니다.
171
+ 별도로 npm에 publish하지 않고 `@no-k/hermes`의 templates에 포함합니다.
172
+ 이후 규칙 변경은 `pnpm changeset`에서 이 모듈을 선택합니다.
173
+ `pnpm run version:modules`는 다른 유틸처럼 모듈 변경을 CLI 배포 버전에 연결합니다.
174
+ 저장소 내부 lint용 `packages/lib/lint/dist`는 개발 빌드 산출물이며 사용자에게 복사하지 않습니다.
175
+
176
+ 릴리스 준비와 Changesets 적용 절차는 [모듈 버전 관리](module-versioning.md)를 참고하세요.
@@ -80,16 +80,31 @@ codegen과 결과 확인이 성공한 뒤에만 설정을 저장한다. dry-run
80
80
  현재 카탈로그에는 모듈당 한 버전만 있다. 이전 버전을 다운로드하거나 서로 다른 버전을 같은
81
81
  설치 경로에 공존시키는 기능은 제공하지 않는다.
82
82
 
83
- ## 개발자의 버전 갱신 절차
83
+ ## 버전 갱신과 npm 배포
84
84
 
85
- 1. 모듈의 공개 API 변경에 맞춰 patch·minor·major를 결정하고 관련 사용처를 수정·검증한다.
86
- 2. `pnpm changeset`으로 변경한 모듈과 설명을 기록한다. 새 유틸에는 manifest와 changelog도 추가한다.
85
+ 1. CLI 또는 모듈의 공개 API 변경에 맞춰 patch·minor·major를 결정하고 관련 사용처를 수정·검증한다.
86
+ 2. `pnpm changeset`으로 변경한 패키지와 설명을 기록한다. CLI 변경은 `@no-k/hermes`,
87
+ 유틸 변경은 해당 모듈을 선택한다. 새 유틸에는 manifest와 changelog도 추가한다.
87
88
  3. `pnpm run version:status`로 예정된 버전과 영향받는 의존 모듈을 확인한다.
88
- 4. 릴리스를 준비할 때 `pnpm run version:modules`로 버전·내부 의존 범위·changelog를 갱신한다.
89
+ 4. 릴리스를 준비할 때 `pnpm run release:version`으로 버전·내부 의존 범위·changelog를 갱신한다.
89
90
  유틸만 변경한 경우에도 CLI patch changeset을 자동 추가해 번들 카탈로그 갱신을 포함한다.
90
- 기존 CLI changeset이 있으면 그 변경 수준을 사용한다.
91
- 5. `pnpm install --lockfile-only`, `pnpm run check`, `pnpm run test:run`, `pnpm run check:cli`,
91
+ 기존 CLI changeset이 있으면 그 변경 수준을 사용한다. lockfile 갱신과 CLI 재빌드까지 실행해
92
+ package.json과 catalog.json의 버전을 맞춘다. 적용할 changeset이 없으면 오류로 중단한다.
93
+ 기존 `pnpm run version:modules`도 같은 명령을 실행한다.
94
+ 5. `pnpm run check`, `pnpm run test:run`, `pnpm run check:cli`,
92
95
  `pnpm run test:cli-install`을 실행하고 결과와 변경 이력을 검토한다.
96
+ 6. `pnpm run release:plan`으로 npm에 아직 배포되지 않은 버전을 확인한다.
97
+ 버전 변경을 커밋한 뒤 `pnpm run release:publish`로 배포한다.
98
+
99
+ `release:publish`는 CLI를 빌드·테스트하고 `changeset publish`를 실행한다.
100
+ Changesets는 현재 package.json 버전의 npm 등록 여부를 확인해 미배포 버전만 배포한다.
101
+ 배포에 성공하면 `@no-k/hermes@N.N.N` 형태의 로컬 Git 태그를 생성한다.
102
+ commit·push는 자동 실행하지 않는다. `release:version`과 `release:plan`은 npm 배포와 태그 생성을 하지 않는다.
103
+
104
+ `ERR_PNPM_FAILED_TO_PUBLISH`와 `You cannot publish over the previously published versions`가
105
+ 나오면 오류에 표시된 버전을 확인한다. `artifacts/`에는 과거 버전의 tarball도 남으므로,
106
+ 예전 파일을 다시 배포해도 그 안의 package.json 버전은 바뀌지 않는다.
107
+ 새 changeset을 적용한 뒤 `release:plan`과 `release:publish`를 사용하면 현재 소스의 버전으로 배포한다.
93
108
 
94
109
  `fixed`·`linked` 그룹은 없다. Core·Tailwind·lint의 상위 집계 패키지와 개발 설정 패키지는 Changesets 대상에서
95
110
  제외한다. 내부 의존성 변경에 따른 dependent 버전 증가는 Changesets 규칙을 따른다.
@@ -97,9 +112,9 @@ codegen과 결과 확인이 성공한 뒤에만 설정을 저장한다. dry-run
97
112
  필요한 명시적 major changeset도 작성해야 한다.
98
113
 
99
114
  GitHub changelog 생성기는 프로젝트 설정을 유지하며 `@changesets/changelog-github`를 사용한다.
100
- 릴리스 환경에는 해당 도구가 요구하는 `GITHUB_TOKEN`을 제공한다. 위 명령들은 publish·push·tag를
101
- 실행하지 않는다. 이번 도입에서는 유틸 초기 버전만 등록하고 실제 릴리스 버전 증가는 하지 않는다.
115
+ 릴리스 환경에는 해당 도구가 요구하는 `GITHUB_TOKEN`을 제공한다.
102
116
 
103
117
  참고: [Changesets의 private 패키지 버전 관리](https://changesets.dev/guide/beyond-npm),
118
+ [Changesets CLI](https://changesets.dev/guide/cli),
104
119
  [pnpm workspace 범위](https://pnpm.io/workspaces),
105
120
  [npm peerDependencies](https://docs.npmjs.com/cli/v11/configuring-npm/package-json/#peerdependencies).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@no-k/hermes",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Add individual TypeScript utilities to your project with the Hermes CLI.",
5
5
  "license": "ISC",
6
6
  "files": ["dist", "templates", "catalog.json", "README.md", "LICENSE", "docs"],