@kood/claude-code 0.1.2 → 0.1.3

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 (61) hide show
  1. package/dist/index.js +12 -3
  2. package/package.json +2 -2
  3. package/templates/hono/CLAUDE.md +20 -2
  4. package/templates/hono/docs/architecture/architecture.md +909 -0
  5. package/templates/hono/docs/deployment/cloudflare.md +537 -190
  6. package/templates/hono/docs/deployment/docker.md +517 -0
  7. package/templates/hono/docs/deployment/index.md +181 -213
  8. package/templates/hono/docs/deployment/railway.md +416 -0
  9. package/templates/hono/docs/deployment/vercel.md +572 -0
  10. package/templates/hono/docs/git/git.md +285 -0
  11. package/templates/hono/docs/library/ai-sdk/index.md +427 -0
  12. package/templates/hono/docs/library/ai-sdk/openrouter.md +479 -0
  13. package/templates/hono/docs/library/ai-sdk/providers.md +468 -0
  14. package/templates/hono/docs/library/ai-sdk/streaming.md +447 -0
  15. package/templates/hono/docs/library/ai-sdk/structured-output.md +493 -0
  16. package/templates/hono/docs/library/ai-sdk/tools.md +513 -0
  17. package/templates/hono/docs/library/hono/env-setup.md +458 -0
  18. package/templates/hono/docs/library/hono/index.md +1 -0
  19. package/templates/hono/docs/library/pino/index.md +437 -0
  20. package/templates/hono/docs/library/prisma/cloudflare-d1.md +503 -0
  21. package/templates/hono/docs/library/prisma/config.md +362 -0
  22. package/templates/hono/docs/library/prisma/index.md +86 -13
  23. package/templates/hono/docs/skills/gemini-review/SKILL.md +116 -116
  24. package/templates/hono/docs/skills/gemini-review/references/checklists.md +125 -125
  25. package/templates/hono/docs/skills/gemini-review/references/prompt-templates.md +191 -191
  26. package/templates/npx/CLAUDE.md +309 -0
  27. package/templates/npx/docs/git/git.md +307 -0
  28. package/templates/npx/docs/library/commander/index.md +164 -0
  29. package/templates/npx/docs/library/fs-extra/index.md +171 -0
  30. package/templates/npx/docs/library/prompts/index.md +253 -0
  31. package/templates/npx/docs/mcp/index.md +60 -0
  32. package/templates/npx/docs/skills/gemini-review/SKILL.md +220 -0
  33. package/templates/npx/docs/skills/gemini-review/references/checklists.md +134 -0
  34. package/templates/npx/docs/skills/gemini-review/references/prompt-templates.md +301 -0
  35. package/templates/tanstack-start/CLAUDE.md +43 -5
  36. package/templates/tanstack-start/docs/architecture/architecture.md +134 -4
  37. package/templates/tanstack-start/docs/deployment/cloudflare.md +234 -51
  38. package/templates/tanstack-start/docs/deployment/index.md +322 -32
  39. package/templates/tanstack-start/docs/deployment/nitro.md +201 -20
  40. package/templates/tanstack-start/docs/deployment/railway.md +305 -153
  41. package/templates/tanstack-start/docs/deployment/vercel.md +353 -78
  42. package/templates/tanstack-start/docs/git/{index.md → git.md} +81 -7
  43. package/templates/tanstack-start/docs/guides/best-practices.md +203 -1
  44. package/templates/tanstack-start/docs/guides/env-setup.md +450 -0
  45. package/templates/tanstack-start/docs/library/ai-sdk/hooks.md +472 -0
  46. package/templates/tanstack-start/docs/library/ai-sdk/index.md +264 -0
  47. package/templates/tanstack-start/docs/library/ai-sdk/openrouter.md +371 -0
  48. package/templates/tanstack-start/docs/library/ai-sdk/providers.md +403 -0
  49. package/templates/tanstack-start/docs/library/ai-sdk/streaming.md +320 -0
  50. package/templates/tanstack-start/docs/library/ai-sdk/structured-output.md +454 -0
  51. package/templates/tanstack-start/docs/library/ai-sdk/tools.md +473 -0
  52. package/templates/tanstack-start/docs/library/pino/index.md +320 -0
  53. package/templates/tanstack-start/docs/library/prisma/cloudflare-d1.md +404 -0
  54. package/templates/tanstack-start/docs/library/prisma/config.md +377 -0
  55. package/templates/tanstack-start/docs/library/prisma/index.md +3 -1
  56. package/templates/tanstack-start/docs/library/prisma/schema.md +123 -25
  57. package/templates/tanstack-start/docs/library/tanstack-start/server-functions.md +80 -2
  58. package/templates/tanstack-start/docs/skills/gemini-review/SKILL.md +116 -116
  59. package/templates/tanstack-start/docs/skills/gemini-review/references/checklists.md +138 -144
  60. package/templates/tanstack-start/docs/skills/gemini-review/references/prompt-templates.md +186 -187
  61. package/templates/hono/docs/git/index.md +0 -180
@@ -0,0 +1,513 @@
1
+ # AI SDK - Tool Calling (Hono)
2
+
3
+ > **상위 문서**: [AI SDK](./index.md)
4
+
5
+ ---
6
+
7
+ ## 개요
8
+
9
+ AI SDK의 도구(Tool)를 Hono에서 사용하면 AI 모델이 외부 함수를 호출할 수 있습니다. 날씨 조회, 데이터베이스 검색, API 호출 등 다양한 작업을 수행할 수 있습니다.
10
+
11
+ ---
12
+
13
+ ## 기본 도구 정의
14
+
15
+ ```typescript
16
+ import { Hono } from 'hono'
17
+ import { streamText, tool, convertToModelMessages } from 'ai'
18
+ import { openai } from '@ai-sdk/openai'
19
+ import { z } from 'zod'
20
+
21
+ const app = new Hono()
22
+
23
+ app.post('/api/chat', async (c) => {
24
+ const { messages } = await c.req.json()
25
+
26
+ const result = streamText({
27
+ model: openai('gpt-4o'),
28
+ messages: convertToModelMessages(messages),
29
+ tools: {
30
+ getWeather: tool({
31
+ description: 'Get the weather for a location',
32
+ inputSchema: z.object({
33
+ location: z.string().describe('The city name'),
34
+ }),
35
+ execute: async ({ location }) => {
36
+ // 실제 날씨 API 호출
37
+ return {
38
+ location,
39
+ temperature: 22,
40
+ condition: 'Sunny',
41
+ }
42
+ },
43
+ }),
44
+ },
45
+ })
46
+
47
+ return result.toUIMessageStreamResponse()
48
+ })
49
+ ```
50
+
51
+ ---
52
+
53
+ ## tool() 함수 구조
54
+
55
+ ```typescript
56
+ import { tool } from 'ai'
57
+ import { z } from 'zod'
58
+
59
+ const myTool = tool({
60
+ // 도구 설명 (모델이 언제 사용할지 결정하는데 사용)
61
+ description: 'Tool description for the AI model',
62
+
63
+ // 입력 스키마 (Zod로 정의)
64
+ inputSchema: z.object({
65
+ param1: z.string().describe('Parameter description'),
66
+ param2: z.number().optional(),
67
+ }),
68
+
69
+ // 실행 함수
70
+ execute: async (args, context) => {
71
+ // args: 스키마에 맞는 입력 값
72
+ // context: { toolCallId, messages } 등 컨텍스트 정보
73
+ return { result: 'success' }
74
+ },
75
+ })
76
+ ```
77
+
78
+ ---
79
+
80
+ ## 여러 도구 정의
81
+
82
+ ```typescript
83
+ import { Hono } from 'hono'
84
+ import { streamText, tool, convertToModelMessages } from 'ai'
85
+ import { openai } from '@ai-sdk/openai'
86
+ import { z } from 'zod'
87
+
88
+ const app = new Hono()
89
+
90
+ app.post('/api/assistant', async (c) => {
91
+ const { messages } = await c.req.json()
92
+
93
+ const result = streamText({
94
+ model: openai('gpt-4o'),
95
+ messages: convertToModelMessages(messages),
96
+ tools: {
97
+ getWeather: tool({
98
+ description: 'Get the weather for a location',
99
+ inputSchema: z.object({
100
+ location: z.string(),
101
+ }),
102
+ execute: async ({ location }) => ({
103
+ temperature: 22,
104
+ condition: 'Sunny',
105
+ }),
106
+ }),
107
+
108
+ getAttractions: tool({
109
+ description: 'Get tourist attractions for a city',
110
+ inputSchema: z.object({
111
+ city: z.string(),
112
+ }),
113
+ execute: async ({ city }) => ({
114
+ attractions: [
115
+ 'Gyeongbokgung Palace',
116
+ 'N Seoul Tower',
117
+ 'Bukchon Hanok Village',
118
+ ],
119
+ }),
120
+ }),
121
+
122
+ searchDatabase: tool({
123
+ description: 'Search the database for information',
124
+ inputSchema: z.object({
125
+ query: z.string(),
126
+ limit: z.number().optional().default(10),
127
+ }),
128
+ execute: async ({ query, limit }) => {
129
+ // 데이터베이스 검색 로직
130
+ return { results: [], total: 0 }
131
+ },
132
+ }),
133
+ },
134
+ })
135
+
136
+ return result.toUIMessageStreamResponse()
137
+ })
138
+ ```
139
+
140
+ ---
141
+
142
+ ## 도구와 외부 서비스 연동
143
+
144
+ ### 데이터베이스 연동
145
+
146
+ ```typescript
147
+ import { Hono } from 'hono'
148
+ import { streamText, tool, convertToModelMessages } from 'ai'
149
+ import { openai } from '@ai-sdk/openai'
150
+ import { z } from 'zod'
151
+ import { prisma } from '@/database/prisma'
152
+
153
+ const app = new Hono()
154
+
155
+ app.post('/api/chat', async (c) => {
156
+ const { messages } = await c.req.json()
157
+
158
+ const result = streamText({
159
+ model: openai('gpt-4o'),
160
+ messages: convertToModelMessages(messages),
161
+ tools: {
162
+ searchProducts: tool({
163
+ description: 'Search for products in the database',
164
+ inputSchema: z.object({
165
+ query: z.string().describe('Search query'),
166
+ category: z.string().optional().describe('Product category'),
167
+ maxPrice: z.number().optional().describe('Maximum price'),
168
+ }),
169
+ execute: async ({ query, category, maxPrice }) => {
170
+ const products = await prisma.product.findMany({
171
+ where: {
172
+ name: { contains: query },
173
+ ...(category && { category }),
174
+ ...(maxPrice && { price: { lte: maxPrice } }),
175
+ },
176
+ take: 10,
177
+ })
178
+ return { products }
179
+ },
180
+ }),
181
+
182
+ getOrderStatus: tool({
183
+ description: 'Get the status of an order',
184
+ inputSchema: z.object({
185
+ orderId: z.string().describe('The order ID'),
186
+ }),
187
+ execute: async ({ orderId }) => {
188
+ const order = await prisma.order.findUnique({
189
+ where: { id: orderId },
190
+ include: { items: true },
191
+ })
192
+ return order ?? { error: 'Order not found' }
193
+ },
194
+ }),
195
+ },
196
+ })
197
+
198
+ return result.toUIMessageStreamResponse()
199
+ })
200
+ ```
201
+
202
+ ### 외부 API 연동
203
+
204
+ ```typescript
205
+ import { Hono } from 'hono'
206
+ import { streamText, tool, convertToModelMessages } from 'ai'
207
+ import { openai } from '@ai-sdk/openai'
208
+ import { z } from 'zod'
209
+
210
+ type Bindings = {
211
+ WEATHER_API_KEY: string
212
+ }
213
+
214
+ const app = new Hono<{ Bindings: Bindings }>()
215
+
216
+ app.post('/api/chat', async (c) => {
217
+ const { messages } = await c.req.json()
218
+
219
+ const result = streamText({
220
+ model: openai('gpt-4o'),
221
+ messages: convertToModelMessages(messages),
222
+ tools: {
223
+ getWeather: tool({
224
+ description: 'Get real-time weather data',
225
+ inputSchema: z.object({
226
+ city: z.string(),
227
+ }),
228
+ execute: async ({ city }) => {
229
+ const response = await fetch(
230
+ `https://api.weather.com/v1/current?city=${city}&key=${c.env.WEATHER_API_KEY}`
231
+ )
232
+ return response.json()
233
+ },
234
+ }),
235
+
236
+ translateText: tool({
237
+ description: 'Translate text to another language',
238
+ inputSchema: z.object({
239
+ text: z.string(),
240
+ targetLanguage: z.string(),
241
+ }),
242
+ execute: async ({ text, targetLanguage }) => {
243
+ // 번역 API 호출
244
+ const response = await fetch('https://api.translate.com/v1/translate', {
245
+ method: 'POST',
246
+ headers: { 'Content-Type': 'application/json' },
247
+ body: JSON.stringify({ text, target: targetLanguage }),
248
+ })
249
+ return response.json()
250
+ },
251
+ }),
252
+ },
253
+ })
254
+
255
+ return result.toUIMessageStreamResponse()
256
+ })
257
+ ```
258
+
259
+ ---
260
+
261
+ ## 다중 단계 실행
262
+
263
+ AI가 여러 도구를 순차적으로 호출하도록 허용:
264
+
265
+ ```typescript
266
+ import { Hono } from 'hono'
267
+ import { generateText, tool, stepCountIs } from 'ai'
268
+ import { openai } from '@ai-sdk/openai'
269
+ import { z } from 'zod'
270
+
271
+ const app = new Hono()
272
+
273
+ app.post('/api/plan-trip', async (c) => {
274
+ const { destination } = await c.req.json()
275
+
276
+ const result = await generateText({
277
+ model: openai('gpt-4o'),
278
+ prompt: `Plan a trip to ${destination}`,
279
+ tools: {
280
+ getWeather: tool({
281
+ description: 'Get weather forecast',
282
+ inputSchema: z.object({ location: z.string() }),
283
+ execute: async ({ location }) => ({ forecast: 'Sunny, 22°C' }),
284
+ }),
285
+ getFlights: tool({
286
+ description: 'Search for flights',
287
+ inputSchema: z.object({ destination: z.string() }),
288
+ execute: async ({ destination }) => ({ flights: ['Flight A', 'Flight B'] }),
289
+ }),
290
+ getHotels: tool({
291
+ description: 'Search for hotels',
292
+ inputSchema: z.object({ location: z.string() }),
293
+ execute: async ({ location }) => ({ hotels: ['Hotel A', 'Hotel B'] }),
294
+ }),
295
+ },
296
+ stopWhen: stepCountIs(5), // 최대 5단계
297
+ })
298
+
299
+ return c.json({
300
+ plan: result.text,
301
+ steps: result.steps.map((step) => ({
302
+ toolCalls: step.toolCalls,
303
+ toolResults: step.toolResults,
304
+ })),
305
+ })
306
+ })
307
+ ```
308
+
309
+ ---
310
+
311
+ ## 도구 컨텍스트 활용
312
+
313
+ ```typescript
314
+ app.post('/api/chat', async (c) => {
315
+ const { messages } = await c.req.json()
316
+
317
+ const result = streamText({
318
+ model: openai('gpt-4o'),
319
+ messages: convertToModelMessages(messages),
320
+ tools: {
321
+ processRequest: tool({
322
+ description: 'Process a user request',
323
+ inputSchema: z.object({ request: z.string() }),
324
+ execute: async (args, context) => {
325
+ // 도구 호출 ID
326
+ console.log('Tool call ID:', context.toolCallId)
327
+
328
+ // 전체 메시지 히스토리 접근
329
+ console.log('Messages:', context.messages)
330
+
331
+ return { processed: true }
332
+ },
333
+ }),
334
+ },
335
+ })
336
+
337
+ return result.toUIMessageStreamResponse()
338
+ })
339
+ ```
340
+
341
+ ---
342
+
343
+ ## 도구 결과 스트리밍
344
+
345
+ ```typescript
346
+ import { Hono } from 'hono'
347
+ import { streamText, tool } from 'ai'
348
+ import { openai } from '@ai-sdk/openai'
349
+ import { z } from 'zod'
350
+ import { stream } from 'hono/streaming'
351
+
352
+ const app = new Hono()
353
+
354
+ app.post('/api/chat', async (c) => {
355
+ const { messages } = await c.req.json()
356
+
357
+ const result = streamText({
358
+ model: openai('gpt-4o'),
359
+ messages,
360
+ tools: {
361
+ getWeather: tool({
362
+ description: 'Get weather',
363
+ inputSchema: z.object({ location: z.string() }),
364
+ execute: async ({ location }) => ({ temp: 22 }),
365
+ }),
366
+ },
367
+ })
368
+
369
+ return stream(c, async (stream) => {
370
+ for await (const event of result.fullStream) {
371
+ switch (event.type) {
372
+ case 'text-delta':
373
+ await stream.write(
374
+ JSON.stringify({ type: 'text', content: event.textDelta }) + '\n'
375
+ )
376
+ break
377
+ case 'tool-call':
378
+ await stream.write(
379
+ JSON.stringify({
380
+ type: 'tool-call',
381
+ name: event.toolName,
382
+ args: event.args,
383
+ }) + '\n'
384
+ )
385
+ break
386
+ case 'tool-result':
387
+ await stream.write(
388
+ JSON.stringify({
389
+ type: 'tool-result',
390
+ name: event.toolName,
391
+ result: event.result,
392
+ }) + '\n'
393
+ )
394
+ break
395
+ }
396
+ }
397
+ })
398
+ })
399
+ ```
400
+
401
+ ---
402
+
403
+ ## 타입 안전한 도구
404
+
405
+ ```typescript
406
+ import { tool, TypedToolCall, TypedToolResult } from 'ai'
407
+ import { z } from 'zod'
408
+
409
+ const myToolSet = {
410
+ greet: tool({
411
+ description: 'Greet a user',
412
+ inputSchema: z.object({ name: z.string() }),
413
+ execute: async ({ name }) => `Hello, ${name}!`,
414
+ }),
415
+ calculate: tool({
416
+ description: 'Calculate sum',
417
+ inputSchema: z.object({
418
+ a: z.number(),
419
+ b: z.number(),
420
+ }),
421
+ execute: async ({ a, b }) => a + b,
422
+ }),
423
+ }
424
+
425
+ // 타입 추출
426
+ type MyToolCall = TypedToolCall<typeof myToolSet>
427
+ type MyToolResult = TypedToolResult<typeof myToolSet>
428
+
429
+ // Hono 라우트에서 사용
430
+ app.post('/api/typed-chat', async (c) => {
431
+ const { messages } = await c.req.json()
432
+
433
+ const result = await generateText({
434
+ model: openai('gpt-4o'),
435
+ tools: myToolSet,
436
+ messages,
437
+ })
438
+
439
+ // 타입 안전한 접근
440
+ const toolCalls: MyToolCall[] = result.toolCalls
441
+ const toolResults: MyToolResult[] = result.toolResults
442
+
443
+ return c.json({ toolCalls, toolResults })
444
+ })
445
+ ```
446
+
447
+ ---
448
+
449
+ ## 도구 에러 처리
450
+
451
+ ```typescript
452
+ app.post('/api/chat', async (c) => {
453
+ const { messages } = await c.req.json()
454
+
455
+ const result = streamText({
456
+ model: openai('gpt-4o'),
457
+ messages: convertToModelMessages(messages),
458
+ tools: {
459
+ riskyOperation: tool({
460
+ description: 'Perform a risky operation',
461
+ inputSchema: z.object({ data: z.string() }),
462
+ execute: async ({ data }) => {
463
+ try {
464
+ // 위험한 작업 수행
465
+ const result = await performRiskyTask(data)
466
+ return { success: true, result }
467
+ } catch (error) {
468
+ // 에러를 AI가 이해할 수 있는 형태로 반환
469
+ return {
470
+ success: false,
471
+ error: error instanceof Error ? error.message : 'Unknown error',
472
+ }
473
+ }
474
+ },
475
+ }),
476
+ },
477
+ })
478
+
479
+ return result.toUIMessageStreamResponse()
480
+ })
481
+ ```
482
+
483
+ ---
484
+
485
+ ## 도구 정의 팁
486
+
487
+ 1. **명확한 설명**: 모델이 언제 도구를 사용할지 이해하도록 상세히 작성
488
+ 2. **스키마 설명**: 각 파라미터에 `.describe()` 추가
489
+ 3. **에러 처리**: execute 함수에서 적절한 에러 처리
490
+ 4. **반환값**: 모델이 이해할 수 있는 구조화된 데이터 반환
491
+
492
+ ```typescript
493
+ const goodTool = tool({
494
+ description: `
495
+ Search for products in the database.
496
+ Use this when the user asks about available products,
497
+ product prices, or product availability.
498
+ `,
499
+ inputSchema: z.object({
500
+ query: z.string().describe('Search query for product name or category'),
501
+ maxResults: z.number().default(10).describe('Maximum number of results'),
502
+ category: z.string().optional().describe('Filter by category'),
503
+ }),
504
+ execute: async ({ query, maxResults, category }) => {
505
+ try {
506
+ const results = await db.products.search({ query, maxResults, category })
507
+ return { success: true, products: results }
508
+ } catch (error) {
509
+ return { success: false, error: 'Failed to search products' }
510
+ }
511
+ },
512
+ })
513
+ ```