@kood/claude-code 0.2.0 → 0.2.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.
Files changed (28) hide show
  1. package/dist/index.js +62 -18
  2. package/package.json +1 -1
  3. package/templates/.claude/agents/code-reviewer.md +31 -0
  4. package/templates/.claude/agents/debug-detective.md +37 -0
  5. package/templates/.claude/agents/refactor-advisor.md +44 -0
  6. package/templates/.claude/agents/test-writer.md +41 -0
  7. package/templates/.claude/skills/frontend-design/SKILL.md +310 -0
  8. package/templates/.claude/skills/frontend-design/references/animation-patterns.md +446 -0
  9. package/templates/.claude/skills/frontend-design/references/colors-2026.md +244 -0
  10. package/templates/.claude/skills/frontend-design/references/typography-2026.md +302 -0
  11. package/templates/.claude/skills/gemini-review/SKILL.md +1 -1
  12. package/templates/hono/docs/library/drizzle/cloudflare-d1.md +247 -0
  13. package/templates/hono/docs/library/drizzle/config.md +167 -0
  14. package/templates/hono/docs/library/drizzle/index.md +259 -0
  15. package/templates/tanstack-start/docs/library/drizzle/cloudflare-d1.md +146 -0
  16. package/templates/tanstack-start/docs/library/drizzle/config.md +118 -0
  17. package/templates/tanstack-start/docs/library/drizzle/crud.md +205 -0
  18. package/templates/tanstack-start/docs/library/drizzle/index.md +79 -0
  19. package/templates/tanstack-start/docs/library/drizzle/relations.md +202 -0
  20. package/templates/tanstack-start/docs/library/drizzle/schema.md +154 -0
  21. package/templates/tanstack-start/docs/library/drizzle/setup.md +95 -0
  22. package/templates/tanstack-start/docs/library/drizzle/transactions.md +127 -0
  23. package/templates/tanstack-start/docs/library/tanstack-router/error-handling.md +204 -0
  24. package/templates/tanstack-start/docs/library/tanstack-router/hooks.md +195 -0
  25. package/templates/tanstack-start/docs/library/tanstack-router/index.md +150 -0
  26. package/templates/tanstack-start/docs/library/tanstack-router/navigation.md +150 -0
  27. package/templates/tanstack-start/docs/library/tanstack-router/route-context.md +203 -0
  28. package/templates/tanstack-start/docs/library/tanstack-router/search-params.md +213 -0
@@ -0,0 +1,204 @@
1
+ # TanStack Router - Error Handling
2
+
3
+ errorComponent, notFoundComponent, 에러 경계.
4
+
5
+ ## errorComponent
6
+
7
+ loader/beforeLoad에서 에러 발생 시 표시.
8
+
9
+ ```tsx
10
+ import { createFileRoute, ErrorComponent } from '@tanstack/react-router'
11
+ import type { ErrorComponentProps } from '@tanstack/react-router'
12
+
13
+ export const Route = createFileRoute('/posts/$postId')({
14
+ loader: async ({ params }) => {
15
+ const post = await getPost(params.postId)
16
+ if (!post) throw new Error('Post not found')
17
+ return { post }
18
+ },
19
+ errorComponent: PostError,
20
+ component: PostPage,
21
+ })
22
+
23
+ function PostError({ error, reset }: ErrorComponentProps) {
24
+ return (
25
+ <div>
26
+ <h2>Error loading post</h2>
27
+ <p>{error.message}</p>
28
+ <button onClick={reset}>Retry</button>
29
+ </div>
30
+ )
31
+ }
32
+ ```
33
+
34
+ ## notFoundComponent
35
+
36
+ `notFound()` 호출 시 표시.
37
+
38
+ ```tsx
39
+ import { createFileRoute, notFound } from '@tanstack/react-router'
40
+
41
+ export const Route = createFileRoute('/posts/$postId')({
42
+ loader: async ({ params }) => {
43
+ const post = await getPost(params.postId)
44
+ if (!post) {
45
+ throw notFound() // notFoundComponent 렌더링
46
+ }
47
+ return { post }
48
+ },
49
+ notFoundComponent: PostNotFound,
50
+ component: PostPage,
51
+ })
52
+
53
+ function PostNotFound() {
54
+ const { postId } = Route.useParams()
55
+
56
+ return (
57
+ <div>
58
+ <h2>Post not found</h2>
59
+ <p>Post with ID "{postId}" does not exist.</p>
60
+ </div>
61
+ )
62
+ }
63
+ ```
64
+
65
+ ### notFound에 데이터 전달
66
+
67
+ ```tsx
68
+ export const Route = createFileRoute('/posts/$postId')({
69
+ loader: async ({ params }) => {
70
+ const post = await getPost(params.postId)
71
+ if (!post) {
72
+ throw notFound({
73
+ data: { searchedId: params.postId },
74
+ })
75
+ }
76
+ return { post }
77
+ },
78
+ notFoundComponent: ({ data }) => {
79
+ // data.searchedId 접근 가능
80
+ return <p>Post {data?.searchedId} not found</p>
81
+ },
82
+ })
83
+ ```
84
+
85
+ ## Root 404 페이지
86
+
87
+ ```tsx
88
+ // routes/__root.tsx
89
+ import { createRootRoute, Outlet } from '@tanstack/react-router'
90
+
91
+ export const Route = createRootRoute({
92
+ component: RootLayout,
93
+ notFoundComponent: GlobalNotFound,
94
+ })
95
+
96
+ function GlobalNotFound() {
97
+ return (
98
+ <div>
99
+ <h1>404</h1>
100
+ <p>Page not found</p>
101
+ <Link to="/">Go Home</Link>
102
+ </div>
103
+ )
104
+ }
105
+ ```
106
+
107
+ ## pendingComponent
108
+
109
+ loader 실행 중 표시.
110
+
111
+ ```tsx
112
+ export const Route = createFileRoute('/posts')({
113
+ loader: async () => {
114
+ await new Promise(r => setTimeout(r, 1000)) // 느린 로딩
115
+ return { posts: await getPosts() }
116
+ },
117
+ pendingComponent: () => <div>Loading posts...</div>,
118
+ component: PostsPage,
119
+ })
120
+ ```
121
+
122
+ ### pendingMs / pendingMinMs
123
+
124
+ ```tsx
125
+ export const Route = createFileRoute('/posts')({
126
+ loader: async () => fetchPosts(),
127
+ pendingComponent: () => <Spinner />,
128
+ pendingMs: 200, // 200ms 후에 pending 표시
129
+ pendingMinMs: 500, // 최소 500ms 동안 pending 유지 (깜빡임 방지)
130
+ component: PostsPage,
131
+ })
132
+ ```
133
+
134
+ ## Catch-all Route
135
+
136
+ ```tsx
137
+ // routes/$.tsx - 모든 매치되지 않는 경로
138
+ import { createFileRoute } from '@tanstack/react-router'
139
+
140
+ export const Route = createFileRoute('/$')({
141
+ component: CatchAllPage,
142
+ })
143
+
144
+ function CatchAllPage() {
145
+ const params = Route.useParams()
146
+ // params._splat에 전체 경로
147
+
148
+ return (
149
+ <div>
150
+ <h1>Page Not Found</h1>
151
+ <p>Path: /{params._splat}</p>
152
+ </div>
153
+ )
154
+ }
155
+ ```
156
+
157
+ ## 에러 타입 구분
158
+
159
+ ```tsx
160
+ function CustomError({ error, reset }: ErrorComponentProps) {
161
+ // 네트워크 에러
162
+ if (error instanceof TypeError && error.message.includes('fetch')) {
163
+ return (
164
+ <div>
165
+ <p>Network error. Check your connection.</p>
166
+ <button onClick={reset}>Retry</button>
167
+ </div>
168
+ )
169
+ }
170
+
171
+ // 인증 에러
172
+ if (error.message.includes('unauthorized')) {
173
+ return <Navigate to="/login" />
174
+ }
175
+
176
+ // 기본 에러
177
+ return (
178
+ <div>
179
+ <p>Something went wrong</p>
180
+ <button onClick={reset}>Retry</button>
181
+ </div>
182
+ )
183
+ }
184
+ ```
185
+
186
+ ## 컴포넌트 우선순위
187
+
188
+ | 우선순위 | 컴포넌트 | 조건 |
189
+ |---------|---------|------|
190
+ | 1 | `errorComponent` | loader/beforeLoad에서 Error throw |
191
+ | 2 | `notFoundComponent` | `notFound()` throw |
192
+ | 3 | `pendingComponent` | loader 실행 중 (pendingMs 이후) |
193
+ | 4 | `component` | 정상 렌더링 |
194
+
195
+ ## 에러 전파
196
+
197
+ 하위 라우트에서 처리 안 된 에러는 상위로 전파.
198
+
199
+ ```
200
+ __root.tsx (errorComponent: GlobalError)
201
+ └── posts.tsx (errorComponent: PostsError)
202
+ └── $postId.tsx (errorComponent 없음)
203
+ → 에러 발생 시 posts.tsx의 PostsError로 전파
204
+ ```
@@ -0,0 +1,195 @@
1
+ # TanStack Router - Hooks
2
+
3
+ 라우터 Hooks 레퍼런스.
4
+
5
+ ## Route-Scoped Hooks
6
+
7
+ 라우트 컴포넌트 내에서 사용. Type-safe.
8
+
9
+ ### Route.useLoaderData()
10
+
11
+ ```tsx
12
+ export const Route = createFileRoute('/posts/$postId')({
13
+ loader: async ({ params }) => {
14
+ const post = await getPost(params.postId)
15
+ return { post }
16
+ },
17
+ component: PostPage,
18
+ })
19
+
20
+ function PostPage() {
21
+ const { post } = Route.useLoaderData()
22
+ // ^? { post: Post }
23
+ return <h1>{post.title}</h1>
24
+ }
25
+ ```
26
+
27
+ ### Route.useParams()
28
+
29
+ ```tsx
30
+ export const Route = createFileRoute('/posts/$postId')({
31
+ component: PostPage,
32
+ })
33
+
34
+ function PostPage() {
35
+ const { postId } = Route.useParams()
36
+ // ^? string
37
+ return <div>Post ID: {postId}</div>
38
+ }
39
+ ```
40
+
41
+ ### Route.useSearch()
42
+
43
+ ```tsx
44
+ export const Route = createFileRoute('/products')({
45
+ validateSearch: z.object({
46
+ page: z.number().catch(1),
47
+ sort: z.string().catch('newest'),
48
+ }),
49
+ component: ProductsPage,
50
+ })
51
+
52
+ function ProductsPage() {
53
+ const { page, sort } = Route.useSearch()
54
+ // ^? number, string
55
+ return <div>Page: {page}, Sort: {sort}</div>
56
+ }
57
+ ```
58
+
59
+ ### Route.useRouteContext()
60
+
61
+ ```tsx
62
+ // _authed.tsx에서 beforeLoad로 user 전달 가정
63
+ export const Route = createFileRoute('/_authed/dashboard')({
64
+ component: DashboardPage,
65
+ })
66
+
67
+ function DashboardPage() {
68
+ const { user } = Route.useRouteContext()
69
+ return <h1>Welcome, {user.name}</h1>
70
+ }
71
+ ```
72
+
73
+ ## Global Hooks
74
+
75
+ 어디서든 사용 가능. 타입 안전성 직접 지정.
76
+
77
+ ### useNavigate()
78
+
79
+ ```tsx
80
+ import { useNavigate } from '@tanstack/react-router'
81
+
82
+ function Component() {
83
+ const navigate = useNavigate()
84
+
85
+ const handleClick = () => {
86
+ navigate({
87
+ to: '/posts/$postId',
88
+ params: { postId: '123' },
89
+ })
90
+ }
91
+ }
92
+ ```
93
+
94
+ ### useMatch()
95
+
96
+ 특정 라우트 매치 정보 접근.
97
+
98
+ ```tsx
99
+ import { useMatch } from '@tanstack/react-router'
100
+
101
+ function Breadcrumb() {
102
+ // 특정 라우트가 현재 매치되면 정보 반환
103
+ const postMatch = useMatch({
104
+ from: '/posts/$postId',
105
+ shouldThrow: false, // 매치 안 되면 undefined
106
+ })
107
+
108
+ if (postMatch) {
109
+ return <span>Post: {postMatch.params.postId}</span>
110
+ }
111
+
112
+ return null
113
+ }
114
+ ```
115
+
116
+ ### useParams() (Global)
117
+
118
+ ```tsx
119
+ import { useParams } from '@tanstack/react-router'
120
+
121
+ // 타입 지정 필요
122
+ const { postId } = useParams({ from: '/posts/$postId' })
123
+
124
+ // strict: false로 모든 params
125
+ const params = useParams({ strict: false })
126
+ ```
127
+
128
+ ### useSearch() (Global)
129
+
130
+ ```tsx
131
+ import { useSearch } from '@tanstack/react-router'
132
+
133
+ // 타입 지정 필요
134
+ const { page } = useSearch({ from: '/products' })
135
+
136
+ // strict: false로 현재 라우트 search
137
+ const search = useSearch({ strict: false })
138
+ ```
139
+
140
+ ### useRouterState()
141
+
142
+ 라우터 전체 상태 접근.
143
+
144
+ ```tsx
145
+ import { useRouterState } from '@tanstack/react-router'
146
+
147
+ function CurrentPath() {
148
+ const pathname = useRouterState({
149
+ select: state => state.location.pathname,
150
+ })
151
+
152
+ return <span>Current: {pathname}</span>
153
+ }
154
+
155
+ // 전체 location
156
+ const location = useRouterState({
157
+ select: state => state.location,
158
+ })
159
+
160
+ // pending 상태
161
+ const isLoading = useRouterState({
162
+ select: state => state.isLoading,
163
+ })
164
+ ```
165
+
166
+ ### useLocation()
167
+
168
+ 현재 location 정보.
169
+
170
+ ```tsx
171
+ import { useLocation } from '@tanstack/react-router'
172
+
173
+ function Component() {
174
+ const location = useLocation()
175
+
176
+ console.log(location.pathname) // '/posts/123'
177
+ console.log(location.search) // { page: 1 }
178
+ console.log(location.hash) // '#section'
179
+ }
180
+ ```
181
+
182
+ ## Hook 비교
183
+
184
+ | Hook | Scope | Type Safety | 용도 |
185
+ |------|-------|-------------|------|
186
+ | `Route.useLoaderData()` | Route | Auto | Loader 데이터 |
187
+ | `Route.useParams()` | Route | Auto | Path params |
188
+ | `Route.useSearch()` | Route | Auto | Search params |
189
+ | `Route.useRouteContext()` | Route | Auto | Route context |
190
+ | `useParams({ from })` | Global | Manual | 다른 라우트 params |
191
+ | `useSearch({ from })` | Global | Manual | 다른 라우트 search |
192
+ | `useMatch({ from })` | Global | Manual | 라우트 매치 정보 |
193
+ | `useNavigate()` | Global | Auto | 네비게이션 |
194
+ | `useRouterState()` | Global | Manual | 라우터 상태 |
195
+ | `useLocation()` | Global | Auto | 현재 location |
@@ -0,0 +1,150 @@
1
+ # TanStack Router
2
+
3
+ > **Version**: 1.x | Type-safe React Router
4
+
5
+ @navigation.md
6
+ @search-params.md
7
+ @route-context.md
8
+ @hooks.md
9
+ @error-handling.md
10
+
11
+ ---
12
+
13
+ ## Quick Reference
14
+
15
+ ```tsx
16
+ import { createFileRoute, Link, Outlet } from '@tanstack/react-router'
17
+
18
+ // 기본 라우트
19
+ export const Route = createFileRoute('/about')({
20
+ component: AboutPage,
21
+ })
22
+
23
+ // Loader + 동적 파라미터
24
+ export const Route = createFileRoute('/posts/$postId')({
25
+ loader: async ({ params }) => {
26
+ const post = await getPost(params.postId)
27
+ return { post }
28
+ },
29
+ component: PostPage,
30
+ })
31
+
32
+ function PostPage() {
33
+ const { post } = Route.useLoaderData()
34
+ return <h1>{post.title}</h1>
35
+ }
36
+
37
+ // Search Params (Zod)
38
+ export const Route = createFileRoute('/products')({
39
+ validateSearch: z.object({
40
+ page: z.number().catch(1),
41
+ sort: z.enum(['newest', 'price']).catch('newest'),
42
+ }),
43
+ component: ProductsPage,
44
+ })
45
+
46
+ function ProductsPage() {
47
+ const { page, sort } = Route.useSearch()
48
+ return <div>Page {page}, Sort: {sort}</div>
49
+ }
50
+ ```
51
+
52
+ ---
53
+
54
+ ## 파일 구조
55
+
56
+ ```
57
+ routes/
58
+ ├── __root.tsx # Root layout
59
+ ├── index.tsx # /
60
+ ├── about.tsx # /about
61
+ ├── posts/
62
+ │ ├── index.tsx # /posts
63
+ │ └── $postId.tsx # /posts/:postId
64
+ ├── _authed/ # Protected routes (pathless)
65
+ │ ├── dashboard.tsx # /dashboard
66
+ │ └── settings.tsx # /settings
67
+ └── $.tsx # Catch-all (404)
68
+ ```
69
+
70
+ | 파일명 패턴 | 설명 |
71
+ |------------|------|
72
+ | `index.tsx` | 디렉토리 루트 |
73
+ | `$param.tsx` | 동적 세그먼트 |
74
+ | `_layout/` | Pathless layout |
75
+ | `$.tsx` | Catch-all |
76
+
77
+ ---
78
+
79
+ ## Root Route
80
+
81
+ ```tsx
82
+ // routes/__root.tsx
83
+ import { createRootRoute, Outlet } from '@tanstack/react-router'
84
+
85
+ export const Route = createRootRoute({
86
+ component: RootLayout,
87
+ notFoundComponent: () => <div>404 Not Found</div>,
88
+ })
89
+
90
+ function RootLayout() {
91
+ return (
92
+ <div>
93
+ <nav>{/* navigation */}</nav>
94
+ <main>
95
+ <Outlet />
96
+ </main>
97
+ </div>
98
+ )
99
+ }
100
+ ```
101
+
102
+ ---
103
+
104
+ ## Route Options
105
+
106
+ | 옵션 | 설명 |
107
+ |------|------|
108
+ | `component` | 렌더링할 컴포넌트 |
109
+ | `loader` | 데이터 로드 함수 |
110
+ | `beforeLoad` | 로드 전 실행 (인증 체크 등) |
111
+ | `validateSearch` | Search params 스키마 |
112
+ | `pendingComponent` | 로딩 중 표시 |
113
+ | `errorComponent` | 에러 발생 시 표시 |
114
+ | `notFoundComponent` | Not found 시 표시 |
115
+
116
+ ---
117
+
118
+ ## Navigation
119
+
120
+ ```tsx
121
+ import { Link, useNavigate } from '@tanstack/react-router'
122
+
123
+ // Link
124
+ <Link to="/posts/$postId" params={{ postId: '123' }}>
125
+ Post
126
+ </Link>
127
+
128
+ // Search params
129
+ <Link to="/products" search={{ page: 1, sort: 'newest' }}>
130
+ Products
131
+ </Link>
132
+
133
+ // Programmatic
134
+ const navigate = useNavigate()
135
+ navigate({ to: '/posts/$postId', params: { postId: '123' } })
136
+ navigate({ to: '/products', search: prev => ({ ...prev, page: 2 }) })
137
+ ```
138
+
139
+ ---
140
+
141
+ ## Hooks
142
+
143
+ | Hook | 용도 |
144
+ |------|------|
145
+ | `Route.useLoaderData()` | Loader 반환값 |
146
+ | `Route.useParams()` | Path 파라미터 |
147
+ | `Route.useSearch()` | Search params |
148
+ | `Route.useRouteContext()` | Route context |
149
+ | `useNavigate()` | 프로그래밍 네비게이션 |
150
+ | `useMatch({ from })` | 특정 라우트 매치 정보 |
@@ -0,0 +1,150 @@
1
+ # TanStack Router - Navigation
2
+
3
+ Link 컴포넌트와 프로그래밍 네비게이션.
4
+
5
+ ## Link 컴포넌트
6
+
7
+ ```tsx
8
+ import { Link } from '@tanstack/react-router'
9
+
10
+ // 기본
11
+ <Link to="/about">About</Link>
12
+
13
+ // 동적 파라미터
14
+ <Link to="/posts/$postId" params={{ postId: '123' }}>
15
+ Post 123
16
+ </Link>
17
+
18
+ // Search params
19
+ <Link to="/products" search={{ page: 1, sort: 'newest' }}>
20
+ Products
21
+ </Link>
22
+
23
+ // Search params 병합
24
+ <Link to="/products" search={prev => ({ ...prev, page: 2 })}>
25
+ Next Page
26
+ </Link>
27
+
28
+ // Active 스타일
29
+ <Link
30
+ to="/about"
31
+ activeProps={{ className: 'text-blue-500 font-bold' }}
32
+ inactiveProps={{ className: 'text-gray-500' }}
33
+ >
34
+ About
35
+ </Link>
36
+
37
+ // 정확히 일치할 때만 active
38
+ <Link to="/" activeOptions={{ exact: true }}>
39
+ Home
40
+ </Link>
41
+ ```
42
+
43
+ ## Link Props
44
+
45
+ | Prop | 타입 | 설명 |
46
+ |------|------|------|
47
+ | `to` | string | 목적지 경로 |
48
+ | `params` | object | Path 파라미터 |
49
+ | `search` | object \| function | Search params |
50
+ | `hash` | string | Hash |
51
+ | `replace` | boolean | history replace |
52
+ | `preload` | 'intent' \| 'render' \| 'viewport' | Preload 전략 |
53
+ | `activeProps` | object | Active 시 props |
54
+ | `inactiveProps` | object | Inactive 시 props |
55
+
56
+ ## useNavigate
57
+
58
+ ```tsx
59
+ import { useNavigate } from '@tanstack/react-router'
60
+
61
+ function Component() {
62
+ const navigate = useNavigate()
63
+
64
+ // 기본
65
+ const goToAbout = () => {
66
+ navigate({ to: '/about' })
67
+ }
68
+
69
+ // 동적 파라미터
70
+ const goToPost = (postId: string) => {
71
+ navigate({ to: '/posts/$postId', params: { postId } })
72
+ }
73
+
74
+ // Search params
75
+ const updateSearch = () => {
76
+ navigate({
77
+ to: '/products',
78
+ search: prev => ({ ...prev, page: 2 }),
79
+ })
80
+ }
81
+
82
+ // Replace (뒤로가기 안 됨)
83
+ const replaceRoute = () => {
84
+ navigate({ to: '/login', replace: true })
85
+ }
86
+
87
+ // 상대 경로
88
+ const goUp = () => {
89
+ navigate({ to: '..' })
90
+ }
91
+
92
+ return (
93
+ <button onClick={goToAbout}>Go to About</button>
94
+ )
95
+ }
96
+ ```
97
+
98
+ ## navigate 옵션
99
+
100
+ | 옵션 | 타입 | 설명 |
101
+ |------|------|------|
102
+ | `to` | string | 목적지 경로 |
103
+ | `params` | object | Path 파라미터 |
104
+ | `search` | object \| function | Search params |
105
+ | `hash` | string | Hash |
106
+ | `replace` | boolean | history.replace 사용 |
107
+ | `resetScroll` | boolean | 스크롤 리셋 |
108
+
109
+ ## Preloading
110
+
111
+ ```tsx
112
+ // hover 시 preload
113
+ <Link to="/posts" preload="intent">
114
+ Posts
115
+ </Link>
116
+
117
+ // 렌더링 시 preload
118
+ <Link to="/dashboard" preload="render">
119
+ Dashboard
120
+ </Link>
121
+
122
+ // viewport 진입 시 preload
123
+ <Link to="/products" preload="viewport">
124
+ Products
125
+ </Link>
126
+ ```
127
+
128
+ ## 조건부 네비게이션
129
+
130
+ ```tsx
131
+ function SubmitButton() {
132
+ const navigate = useNavigate()
133
+ const [isPending, startTransition] = useTransition()
134
+
135
+ const handleSubmit = async () => {
136
+ const result = await submitForm()
137
+ if (result.success) {
138
+ startTransition(() => {
139
+ navigate({ to: '/success' })
140
+ })
141
+ }
142
+ }
143
+
144
+ return (
145
+ <button onClick={handleSubmit} disabled={isPending}>
146
+ Submit
147
+ </button>
148
+ )
149
+ }
150
+ ```