@kood/claude-code 0.1.3 → 0.1.5

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/dist/index.js CHANGED
@@ -183,6 +183,58 @@ var checkExistingSkills = async (targetDir, skills) => {
183
183
  }
184
184
  return existingSkills;
185
185
  };
186
+ var getCommandsPath = (template) => {
187
+ return path.join(getTemplatePath(template), "docs", "commands");
188
+ };
189
+ var listAvailableCommands = async (template) => {
190
+ const commandsPath = getCommandsPath(template);
191
+ if (!await fs.pathExists(commandsPath)) {
192
+ return [];
193
+ }
194
+ const items = await fs.readdir(commandsPath);
195
+ const commands = [];
196
+ for (const item of items) {
197
+ const itemPath = path.join(commandsPath, item);
198
+ const stat = await fs.stat(itemPath);
199
+ if (stat.isFile() && item.endsWith(".md")) {
200
+ commands.push(item.replace(".md", ""));
201
+ }
202
+ }
203
+ return commands;
204
+ };
205
+ var copyCommands = async (template, targetDir) => {
206
+ const commandsPath = getCommandsPath(template);
207
+ const targetCommandsDir = path.join(targetDir, ".claude", "commands");
208
+ if (!await fs.pathExists(commandsPath)) {
209
+ return { files: 0, commands: [] };
210
+ }
211
+ let files = 0;
212
+ const installedCommands = [];
213
+ await fs.ensureDir(targetCommandsDir);
214
+ const items = await fs.readdir(commandsPath);
215
+ for (const item of items) {
216
+ const srcPath = path.join(commandsPath, item);
217
+ const stat = await fs.stat(srcPath);
218
+ if (stat.isFile() && item.endsWith(".md")) {
219
+ const destPath = path.join(targetCommandsDir, item);
220
+ await fs.copy(srcPath, destPath);
221
+ files++;
222
+ installedCommands.push(item.replace(".md", ""));
223
+ }
224
+ }
225
+ return { files, commands: installedCommands };
226
+ };
227
+ var checkExistingCommands = async (targetDir, commands) => {
228
+ const existingCommands = [];
229
+ const targetCommandsDir = path.join(targetDir, ".claude", "commands");
230
+ for (const command of commands) {
231
+ const commandPath = path.join(targetCommandsDir, `${command}.md`);
232
+ if (await fs.pathExists(commandPath)) {
233
+ existingCommands.push(command);
234
+ }
235
+ }
236
+ return existingCommands;
237
+ };
186
238
 
187
239
  // src/commands/init.ts
188
240
  var TEMPLATE_DESCRIPTIONS = {
@@ -245,6 +297,7 @@ var init = async (options) => {
245
297
  let totalFiles = 0;
246
298
  let totalDirectories = 0;
247
299
  const allSkills = [];
300
+ const allCommands = [];
248
301
  logger.blank();
249
302
  try {
250
303
  if (isSingleTemplate) {
@@ -258,6 +311,8 @@ var init = async (options) => {
258
311
  logger.success(`${template}: ${result.files} files copied`);
259
312
  const availableSkills = await listAvailableSkills(template);
260
313
  allSkills.push(...availableSkills);
314
+ const availableCommands = await listAvailableCommands(template);
315
+ allCommands.push(...availableCommands);
261
316
  } else {
262
317
  logger.info(`Installing ${templates.length} templates...`);
263
318
  logger.step(`Target: ${targetDir}/docs/`);
@@ -273,6 +328,12 @@ var init = async (options) => {
273
328
  allSkills.push(skill);
274
329
  }
275
330
  }
331
+ const availableCommands = await listAvailableCommands(template);
332
+ for (const command of availableCommands) {
333
+ if (!allCommands.includes(command)) {
334
+ allCommands.push(command);
335
+ }
336
+ }
276
337
  }
277
338
  }
278
339
  } catch (error) {
@@ -318,6 +379,44 @@ var init = async (options) => {
318
379
  logger.blank();
319
380
  }
320
381
  }
382
+ if (allCommands.length > 0) {
383
+ let installCommands = options.commands;
384
+ if (installCommands === void 0) {
385
+ logger.blank();
386
+ const response = await prompts({
387
+ type: "confirm",
388
+ name: "installCommands",
389
+ message: `Install Claude Code commands? (${allCommands.join(", ")})`,
390
+ initial: true
391
+ });
392
+ installCommands = response.installCommands;
393
+ }
394
+ if (installCommands) {
395
+ const existingCommands = await checkExistingCommands(
396
+ targetDir,
397
+ allCommands
398
+ );
399
+ if (existingCommands.length > 0 && !options.force) {
400
+ logger.warn("The following commands already exist:");
401
+ existingCommands.forEach((c) => logger.step(c));
402
+ logger.blank();
403
+ const response = await prompts({
404
+ type: "confirm",
405
+ name: "overwrite",
406
+ message: "Overwrite existing commands?",
407
+ initial: false
408
+ });
409
+ if (!response.overwrite) {
410
+ logger.info("Skipping commands installation.");
411
+ } else {
412
+ await installAllCommands(templates, targetDir);
413
+ }
414
+ } else {
415
+ await installAllCommands(templates, targetDir);
416
+ }
417
+ logger.blank();
418
+ }
419
+ }
321
420
  logger.success("Claude Code documentation installed!");
322
421
  logger.blank();
323
422
  logger.info("Installed templates:");
@@ -343,14 +442,29 @@ var installAllSkills = async (templates, targetDir) => {
343
442
  logger.step(`Location: .claude/skills/`);
344
443
  }
345
444
  };
445
+ var installAllCommands = async (templates, targetDir) => {
446
+ const installedCommands = [];
447
+ for (const template of templates) {
448
+ const commandsResult = await copyCommands(template, targetDir);
449
+ for (const command of commandsResult.commands) {
450
+ if (!installedCommands.includes(command)) {
451
+ installedCommands.push(command);
452
+ }
453
+ }
454
+ }
455
+ if (installedCommands.length > 0) {
456
+ logger.success(`Commands installed: ${installedCommands.join(", ")}`);
457
+ logger.step(`Location: .claude/commands/`);
458
+ }
459
+ };
346
460
 
347
461
  // src/index.ts
348
462
  var program = new Command();
349
- program.name("claude-code").description("Claude Code documentation installer for projects").version("0.1.2");
463
+ program.name("claude-code").description("Claude Code documentation installer for projects").version("0.1.5");
350
464
  program.option(
351
465
  "-t, --template <names>",
352
466
  "template names (comma-separated: tanstack-start,hono)"
353
- ).option("-f, --force", "overwrite existing files without prompting").option("-s, --skills", "install Claude Code skills").option("--no-skills", "skip skills installation").option("--cwd <path>", "target directory (default: current directory)").option("--list", "list available templates").action(async (options) => {
467
+ ).option("-f, --force", "overwrite existing files without prompting").option("-s, --skills", "install Claude Code skills").option("--no-skills", "skip skills installation").option("-c, --commands", "install Claude Code commands").option("--no-commands", "skip commands installation").option("--cwd <path>", "target directory (default: current directory)").option("--list", "list available templates").action(async (options) => {
354
468
  banner();
355
469
  if (options.list) {
356
470
  const templates = await listAvailableTemplates();
@@ -363,7 +477,8 @@ program.option(
363
477
  templates: options.template?.split(",").map((t) => t.trim()),
364
478
  force: options.force,
365
479
  cwd: options.cwd,
366
- skills: options.skills
480
+ skills: options.skills,
481
+ commands: options.commands
367
482
  });
368
483
  });
369
484
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kood/claude-code",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Claude Code documentation installer for projects",
5
5
  "type": "module",
6
6
  "bin": "./dist/index.js",
@@ -0,0 +1,145 @@
1
+ 현재 git 상태를 확인하고, 아래 규칙에 따라 작업을 진행해주세요.
2
+
3
+ **추가 지시사항**: $ARGUMENTS
4
+
5
+ ---
6
+
7
+ ## 실행 절차
8
+
9
+ ### 추가 지시사항이 없는 경우 (기본 동작)
10
+ 1. `git status`로 현재 상태 확인
11
+ 2. `git diff`로 변경 내용 분석
12
+ 3. 스테이징 안 된 변경사항 → `git add`
13
+ 4. 커밋 안 된 변경사항 → 논리적 단위로 분리하여 `git commit`
14
+ 5. 최종 `git status`로 완료 확인
15
+
16
+ ### 추가 지시사항이 있는 경우
17
+ - 사용자의 지시사항을 우선적으로 따름
18
+ - 예: `/git push` → push 진행, `/git 특정파일만 커밋` → 해당 파일만 처리
19
+
20
+ ---
21
+
22
+ ## ⛔ NEVER (절대 금지)
23
+
24
+ ```
25
+ ❌ 커밋에 "Generated with Claude Code" 포함
26
+ ❌ 커밋에 "🤖" 또는 AI 관련 이모지 포함
27
+ ❌ 커밋에 "Co-Authored-By:" 헤더 포함
28
+ ❌ 커밋에 AI/봇이 작성했다는 어떤 표시도 포함
29
+ ❌ 커밋 메시지 여러 줄 작성
30
+ ❌ 커밋 메시지에 이모지 사용
31
+ ❌ 커밋 메시지에 마침표(.) 사용
32
+ ❌ 여러 작업을 하나의 커밋으로 퉁치기
33
+ ```
34
+
35
+ ---
36
+
37
+ ## ✅ ALWAYS (필수)
38
+
39
+ ### 커밋 형식
40
+
41
+ ```
42
+ <prefix>: <설명>
43
+ ```
44
+
45
+ **한 줄로 간결하게** 작성합니다. 본문이나 푸터는 작성하지 않습니다.
46
+
47
+ ### ⭐ 커밋 분리 원칙
48
+
49
+ **하나의 커밋 = 하나의 논리적 변경 단위**
50
+
51
+ ```bash
52
+ # ❌ 잘못된 방식: 모든 작업을 하나로 퉁침
53
+ git add .
54
+ git commit -m "feat: 여러 기능 추가"
55
+
56
+ # ✅ 올바른 방식: 논리적 단위로 분리
57
+ git add src/auth/
58
+ git commit -m "feat: 사용자 인증 기능 추가"
59
+
60
+ git add src/users/
61
+ git commit -m "feat: 사용자 관리 기능 추가"
62
+
63
+ git add docs/
64
+ git commit -m "docs: API 문서 업데이트"
65
+ ```
66
+
67
+ ---
68
+
69
+ ## 🏷 Prefix 목록
70
+
71
+ | Prefix | 용도 | 예시 |
72
+ |--------|------|------|
73
+ | `feat` | 새로운 기능 | `feat: 사용자 인증 기능 추가` |
74
+ | `fix` | 버그 수정 | `fix: 토큰 검증 오류 수정` |
75
+ | `refactor` | 리팩토링 | `refactor: 인증 로직 분리` |
76
+ | `style` | 코드 스타일 | `style: prettier 적용` |
77
+ | `docs` | 문서 수정 | `docs: API 문서 업데이트` |
78
+ | `test` | 테스트 | `test: 인증 테스트 추가` |
79
+ | `chore` | 빌드/설정 | `chore: 의존성 업데이트` |
80
+ | `perf` | 성능 개선 | `perf: 쿼리 최적화` |
81
+ | `ci` | CI/CD | `ci: GitHub Actions 추가` |
82
+
83
+ ---
84
+
85
+ ## ✅ 올바른 예시
86
+
87
+ ```bash
88
+ feat: 사용자 로그인 기능 추가
89
+ fix: 세션 만료 오류 수정
90
+ refactor: 서비스 클래스 구조 개선
91
+ docs: README 설치 가이드 추가
92
+ chore: 의존성 버전 업그레이드
93
+ ```
94
+
95
+ ---
96
+
97
+ ## ❌ 잘못된 예시
98
+
99
+ ```bash
100
+ # prefix 없음
101
+ 사용자 인증 기능 추가함
102
+
103
+ # 마침표 불필요
104
+ feat: 사용자 인증 추가.
105
+
106
+ # 대문자 사용
107
+ FEAT: 사용자 인증 추가
108
+
109
+ # scope 불필요
110
+ feat(auth): 인증 추가
111
+
112
+ # AI 작성 표시 (절대 금지!)
113
+ feat: 로그인 기능 추가
114
+
115
+ 🤖 Generated with Claude Code
116
+
117
+ # Co-Author 표시 (절대 금지!)
118
+ feat: 로그인 기능 추가
119
+
120
+ Co-Authored-By: Claude <noreply@anthropic.com>
121
+
122
+ # 여러 줄 본문 (금지)
123
+ feat: 로그인 기능 추가
124
+
125
+ - 이메일 인증 추가
126
+ - 세션 관리 구현
127
+
128
+ # 여러 작업을 하나로 퉁침 (금지)
129
+ feat: 로그인, 회원가입, 프로필 기능 추가
130
+ ```
131
+
132
+ ---
133
+
134
+ ## 📦 커밋 분리 가이드
135
+
136
+ ### 언제 커밋을 분리해야 하나요?
137
+
138
+ | 상황 | 커밋 분리 |
139
+ |------|----------|
140
+ | 서로 다른 기능 구현 | ✅ 분리 |
141
+ | 버그 수정 + 새 기능 | ✅ 분리 |
142
+ | 코드 변경 + 문서 변경 | ✅ 분리 |
143
+ | 리팩토링 + 기능 추가 | ✅ 분리 |
144
+ | 동일 기능의 관련 파일들 | 🔄 묶어도 됨 |
145
+ | 동일 기능의 타입 + 구현 | 🔄 묶어도 됨 |
@@ -176,7 +176,7 @@ npm install -g wrangler
176
176
  wrangler login
177
177
 
178
178
  # 빌드
179
- pnpm build
179
+ yarn build
180
180
 
181
181
  # 배포
182
182
  wrangler deploy
@@ -195,7 +195,7 @@ wrangler dev
195
195
  2. **빌드 설정**
196
196
  ```
197
197
  Framework preset: None
198
- Build command: pnpm build
198
+ Build command: yarn build
199
199
  Build output directory: .output/public
200
200
  ```
201
201
 
@@ -463,22 +463,17 @@ jobs:
463
463
  steps:
464
464
  - uses: actions/checkout@v4
465
465
 
466
- - name: Setup pnpm
467
- uses: pnpm/action-setup@v2
468
- with:
469
- version: 8
470
-
471
466
  - name: Setup Node.js
472
467
  uses: actions/setup-node@v4
473
468
  with:
474
469
  node-version: "20"
475
- cache: "pnpm"
470
+ cache: "yarn"
476
471
 
477
472
  - name: Install dependencies
478
- run: pnpm install
473
+ run: yarn install --frozen-lockfile
479
474
 
480
475
  - name: Build
481
- run: pnpm build
476
+ run: yarn build
482
477
 
483
478
  - name: Deploy to Cloudflare Workers
484
479
  uses: cloudflare/wrangler-action@v3
@@ -503,22 +498,17 @@ jobs:
503
498
  steps:
504
499
  - uses: actions/checkout@v4
505
500
 
506
- - name: Setup pnpm
507
- uses: pnpm/action-setup@v2
508
- with:
509
- version: 8
510
-
511
501
  - name: Setup Node.js
512
502
  uses: actions/setup-node@v4
513
503
  with:
514
504
  node-version: "20"
515
- cache: "pnpm"
505
+ cache: "yarn"
516
506
 
517
507
  - name: Install dependencies
518
- run: pnpm install
508
+ run: yarn install --frozen-lockfile
519
509
 
520
510
  - name: Build
521
- run: pnpm build
511
+ run: yarn build
522
512
 
523
513
  - name: Deploy to Cloudflare Pages
524
514
  uses: cloudflare/pages-action@v1
@@ -14,14 +14,14 @@ FROM node:20-alpine AS base
14
14
 
15
15
  FROM base AS deps
16
16
  WORKDIR /app
17
- COPY package.json pnpm-lock.yaml ./
18
- RUN corepack enable pnpm && pnpm install --frozen-lockfile
17
+ COPY package.json yarn.lock ./
18
+ RUN yarn install --frozen-lockfile
19
19
 
20
20
  FROM base AS builder
21
21
  WORKDIR /app
22
22
  COPY --from=deps /app/node_modules ./node_modules
23
23
  COPY . .
24
- RUN corepack enable pnpm && pnpm run build
24
+ RUN yarn build
25
25
 
26
26
  FROM base AS runner
27
27
  WORKDIR /app
@@ -105,11 +105,10 @@ FROM base AS deps
105
105
  WORKDIR /app
106
106
 
107
107
  # 패키지 매니저 파일 복사
108
- COPY package.json pnpm-lock.yaml ./
108
+ COPY package.json yarn.lock ./
109
109
 
110
- # pnpm 활성화 및 의존성 설치
111
- RUN corepack enable pnpm && \
112
- pnpm install --frozen-lockfile --prod=false
110
+ # yarn 의존성 설치
111
+ RUN yarn install --frozen-lockfile
113
112
 
114
113
  # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
115
114
  # Stage 3: 빌드
@@ -124,9 +123,7 @@ COPY --from=deps /app/node_modules ./node_modules
124
123
  COPY . .
125
124
 
126
125
  # Nitro 빌드
127
- RUN corepack enable pnpm && \
128
- pnpm run build && \
129
- pnpm store prune
126
+ RUN yarn build
130
127
 
131
128
  # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
132
129
  # Stage 4: 프로덕션 러너
@@ -333,17 +330,17 @@ FROM node:20-alpine
333
330
  WORKDIR /app
334
331
 
335
332
  # 패키지 파일 복사
336
- COPY package.json pnpm-lock.yaml ./
333
+ COPY package.json yarn.lock ./
337
334
 
338
- # pnpm 설치 및 의존성 설치
339
- RUN corepack enable pnpm && pnpm install
335
+ # yarn 의존성 설치
336
+ RUN yarn install
340
337
 
341
338
  # 소스 코드는 볼륨으로 마운트됨
342
339
 
343
340
  EXPOSE 3000
344
341
 
345
342
  # 개발 서버 실행
346
- CMD ["pnpm", "run", "dev"]
343
+ CMD ["yarn", "dev"]
347
344
  ```
348
345
 
349
346
  ---
@@ -449,7 +446,7 @@ FROM node:20-alpine
449
446
  # 불필요한 파일 제외 (.dockerignore 활용)
450
447
 
451
448
  # 프로덕션 의존성만 설치
452
- RUN pnpm install --prod
449
+ RUN yarn install --production
453
450
 
454
451
  # 빌드 결과물만 최종 이미지에 포함
455
452
  COPY --from=builder /app/.output ./.output
@@ -459,12 +456,12 @@ COPY --from=builder /app/.output ./.output
459
456
 
460
457
  ```dockerfile
461
458
  # 의존성 파일 먼저 복사 (캐시 활용)
462
- COPY package.json pnpm-lock.yaml ./
463
- RUN pnpm install
459
+ COPY package.json yarn.lock ./
460
+ RUN yarn install --frozen-lockfile
464
461
 
465
462
  # 소스 코드는 나중에 복사
466
463
  COPY . .
467
- RUN pnpm build
464
+ RUN yarn build
468
465
  ```
469
466
 
470
467
  ### 보안 강화
@@ -58,9 +58,6 @@ npm install nitro@3
58
58
 
59
59
  # yarn
60
60
  yarn add nitro@3
61
-
62
- # pnpm
63
- pnpm add nitro@3
64
61
  ```
65
62
 
66
63
  ### 2. 기본 설정 파일
@@ -135,10 +135,10 @@ Railway는 Dockerfile을 자동으로 감지합니다.
135
135
  # Dockerfile
136
136
  FROM node:20-alpine AS builder
137
137
  WORKDIR /app
138
- COPY package.json pnpm-lock.yaml ./
139
- RUN corepack enable pnpm && pnpm install
138
+ COPY package.json yarn.lock ./
139
+ RUN yarn install --frozen-lockfile
140
140
  COPY . .
141
- RUN pnpm build
141
+ RUN yarn build
142
142
 
143
143
  FROM node:20-alpine
144
144
  WORKDIR /app
@@ -161,7 +161,7 @@ CMD ["node", ".output/server/index.mjs"]
161
161
  [build]
162
162
  # 빌드 명령어
163
163
  builder = "nixpacks"
164
- buildCommand = "pnpm install && pnpm build"
164
+ buildCommand = "yarn install && yarn build"
165
165
 
166
166
  [deploy]
167
167
  # 시작 명령어
@@ -387,7 +387,7 @@ Railway 대시보드에서 설정:
387
387
 
388
388
  | 문제 | 원인 | 해결 |
389
389
  |------|------|------|
390
- | 빌드 실패 | 의존성 오류 | `pnpm-lock.yaml` 커밋 확인 |
390
+ | 빌드 실패 | 의존성 오류 | `yarn.lock` 커밋 확인 |
391
391
  | 포트 바인딩 실패 | 하드코딩된 포트 | `process.env.PORT` 사용 |
392
392
  | 메모리 초과 | 메모리 누수 | 리소스 제한 증가 또는 코드 최적화 |
393
393
  | 헬스체크 실패 | 엔드포인트 없음 | `/health` 엔드포인트 추가 |
@@ -396,7 +396,7 @@ Railway 대시보드에서 설정:
396
396
 
397
397
  ```bash
398
398
  # 로컬에서 Railway 환경 시뮬레이션
399
- railway run pnpm dev
399
+ railway run yarn dev
400
400
 
401
401
  # 환경 변수 확인
402
402
  railway variables
@@ -102,10 +102,10 @@ export default defineNitroConfig({
102
102
  ```json
103
103
  {
104
104
  "$schema": "https://openapi.vercel.sh/vercel.json",
105
- "buildCommand": "pnpm build",
105
+ "buildCommand": "yarn build",
106
106
  "outputDirectory": ".vercel/output",
107
107
  "framework": null,
108
- "installCommand": "pnpm install"
108
+ "installCommand": "yarn install"
109
109
  }
110
110
  ```
111
111
 
@@ -114,7 +114,7 @@ export default defineNitroConfig({
114
114
  ```json
115
115
  {
116
116
  "$schema": "https://openapi.vercel.sh/vercel.json",
117
- "buildCommand": "pnpm build",
117
+ "buildCommand": "yarn build",
118
118
  "outputDirectory": ".vercel/output",
119
119
 
120
120
  "functions": {
@@ -152,9 +152,9 @@ export default defineNitroConfig({
152
152
 
153
153
  2. **빌드 설정**
154
154
  - Framework Preset: "Other"
155
- - Build Command: `pnpm build`
155
+ - Build Command: `yarn build`
156
156
  - Output Directory: `.vercel/output`
157
- - Install Command: `pnpm install`
157
+ - Install Command: `yarn install`
158
158
 
159
159
  3. **환경 변수 설정**
160
160
  - Vercel 대시보드에서 Settings → Environment Variables
@@ -452,22 +452,17 @@ jobs:
452
452
  steps:
453
453
  - uses: actions/checkout@v4
454
454
 
455
- - name: Setup pnpm
456
- uses: pnpm/action-setup@v2
457
- with:
458
- version: 8
459
-
460
455
  - name: Setup Node.js
461
456
  uses: actions/setup-node@v4
462
457
  with:
463
458
  node-version: "20"
464
- cache: "pnpm"
459
+ cache: "yarn"
465
460
 
466
461
  - name: Install dependencies
467
- run: pnpm install
462
+ run: yarn install --frozen-lockfile
468
463
 
469
464
  - name: Build
470
- run: pnpm build
465
+ run: yarn build
471
466
 
472
467
  - name: Deploy to Vercel
473
468
  uses: amondnet/vercel-action@v25
@@ -528,7 +523,7 @@ export default app;
528
523
  | Function Timeout | 실행 시간 초과 | `maxDuration` 증가 또는 최적화 |
529
524
  | Edge 호환성 오류 | Node.js API 사용 | Edge 호환 API로 변경 |
530
525
  | 환경 변수 누락 | 설정 안됨 | Vercel 대시보드에서 설정 |
531
- | 빌드 실패 | 의존성 문제 | `pnpm-lock.yaml` 확인 |
526
+ | 빌드 실패 | 의존성 문제 | `yarn.lock` 확인 |
532
527
 
533
528
  ### 디버깅
534
529
 
@@ -21,9 +21,6 @@ bun add hono
21
21
 
22
22
  # yarn
23
23
  yarn add hono
24
-
25
- # pnpm
26
- pnpm add hono
27
24
  ```
28
25
 
29
26
  ---