@smicolon/ai-kit 0.1.0 → 0.1.1

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 (162) hide show
  1. package/.claude-plugin/CLAUDE.md +7 -0
  2. package/.claude-plugin/marketplace.json +373 -0
  3. package/package.json +4 -3
  4. package/packs/architect/CHANGELOG.md +17 -0
  5. package/packs/architect/README.md +58 -0
  6. package/packs/architect/agents/system-architect.md +768 -0
  7. package/packs/architect/commands/diagram-create.md +300 -0
  8. package/packs/better-auth/.claude-plugin/plugin.json +14 -0
  9. package/packs/better-auth/.mcp.json +14 -0
  10. package/packs/better-auth/CHANGELOG.md +26 -0
  11. package/packs/better-auth/README.md +125 -0
  12. package/packs/better-auth/agents/auth-architect.md +278 -0
  13. package/packs/better-auth/commands/auth-provider-add.md +265 -0
  14. package/packs/better-auth/commands/auth-setup.md +298 -0
  15. package/packs/better-auth/skills/auth-security/SKILL.md +425 -0
  16. package/packs/better-auth/skills/better-auth-patterns/SKILL.md +455 -0
  17. package/packs/dev-loop/.claude-plugin/plugin.json +10 -0
  18. package/packs/dev-loop/CHANGELOG.md +69 -0
  19. package/packs/dev-loop/README.md +155 -0
  20. package/packs/dev-loop/commands/cancel-dev.md +21 -0
  21. package/packs/dev-loop/commands/dev-loop.md +72 -0
  22. package/packs/dev-loop/commands/dev-plan.md +351 -0
  23. package/packs/dev-loop/hooks/hooks.json +15 -0
  24. package/packs/dev-loop/hooks/stop-hook.sh +178 -0
  25. package/packs/dev-loop/scripts/setup-dev-loop.sh +194 -0
  26. package/packs/dev-loop/skills/tdd-planner/SKILL.md +249 -0
  27. package/packs/dev-loop/skills/tdd-planner/references/framework-patterns.md +874 -0
  28. package/packs/dev-loop/skills/tdd-planner/references/good-example.md +260 -0
  29. package/packs/dev-loop/skills/tdd-planner/references/plan-template.md +275 -0
  30. package/packs/django/CHANGELOG.md +39 -0
  31. package/packs/django/README.md +92 -0
  32. package/packs/django/agents/django-architect.md +182 -0
  33. package/packs/django/agents/django-builder.md +250 -0
  34. package/packs/django/agents/django-feature-based.md +420 -0
  35. package/packs/django/agents/django-reviewer.md +253 -0
  36. package/packs/django/agents/django-tester.md +230 -0
  37. package/packs/django/commands/api-endpoint.md +285 -0
  38. package/packs/django/commands/model-create.md +178 -0
  39. package/packs/django/commands/test-generate.md +325 -0
  40. package/packs/django/rules/migrations.md +138 -0
  41. package/packs/django/rules/models.md +167 -0
  42. package/packs/django/rules/serializers.md +126 -0
  43. package/packs/django/rules/services.md +131 -0
  44. package/packs/django/rules/tests.md +140 -0
  45. package/packs/django/rules/views.md +102 -0
  46. package/packs/django/skills/import-convention-enforcer/SKILL.md +226 -0
  47. package/packs/django/skills/import-convention-enforcer/patterns/django-imports.md +343 -0
  48. package/packs/django/skills/migration-safety-checker/SKILL.md +375 -0
  49. package/packs/django/skills/model-entity-validator/SKILL.md +298 -0
  50. package/packs/django/skills/performance-optimizer/SKILL.md +447 -0
  51. package/packs/django/skills/red-phase-verifier/SKILL.md +180 -0
  52. package/packs/django/skills/security-first-validator/SKILL.md +435 -0
  53. package/packs/django/skills/test-coverage-advisor/SKILL.md +394 -0
  54. package/packs/django/skills/test-validity-checker/SKILL.md +194 -0
  55. package/packs/failure-log/.claude-plugin/plugin.json +14 -0
  56. package/packs/failure-log/CHANGELOG.md +20 -0
  57. package/packs/failure-log/README.md +168 -0
  58. package/packs/failure-log/commands/failure-add.md +106 -0
  59. package/packs/failure-log/commands/failure-list.md +89 -0
  60. package/packs/failure-log/hooks/hooks.json +16 -0
  61. package/packs/failure-log/hooks/scripts/inject-failures.sh +64 -0
  62. package/packs/failure-log/skills/failure-log-manager/SKILL.md +164 -0
  63. package/packs/flutter/.claude-plugin/plugin.json +10 -0
  64. package/packs/flutter/CHANGELOG.md +19 -0
  65. package/packs/flutter/README.md +170 -0
  66. package/packs/flutter/agents/flutter-architect.md +166 -0
  67. package/packs/flutter/agents/flutter-builder.md +303 -0
  68. package/packs/flutter/agents/release-manager.md +355 -0
  69. package/packs/flutter/commands/fastlane-setup.md +188 -0
  70. package/packs/flutter/commands/flutter-build.md +90 -0
  71. package/packs/flutter/commands/flutter-deploy.md +133 -0
  72. package/packs/flutter/commands/flutter-test.md +117 -0
  73. package/packs/flutter/commands/signing-setup.md +209 -0
  74. package/packs/flutter/hooks/hooks.json +17 -0
  75. package/packs/flutter/skills/fastlane-knowledge/SKILL.md +193 -0
  76. package/packs/flutter/skills/flutter-architecture/SKILL.md +127 -0
  77. package/packs/flutter/skills/store-publishing/SKILL.md +163 -0
  78. package/packs/hono/.claude-plugin/plugin.json +19 -0
  79. package/packs/hono/CHANGELOG.md +19 -0
  80. package/packs/hono/README.md +143 -0
  81. package/packs/hono/agents/hono-architect.md +240 -0
  82. package/packs/hono/agents/hono-builder.md +285 -0
  83. package/packs/hono/agents/hono-reviewer.md +279 -0
  84. package/packs/hono/agents/hono-tester.md +346 -0
  85. package/packs/hono/commands/middleware-create.md +223 -0
  86. package/packs/hono/commands/project-init.md +306 -0
  87. package/packs/hono/commands/route-create.md +153 -0
  88. package/packs/hono/commands/rpc-client.md +263 -0
  89. package/packs/hono/hooks/hooks.json +4 -0
  90. package/packs/hono/skills/cloudflare-bindings/SKILL.md +408 -0
  91. package/packs/hono/skills/hono-patterns/SKILL.md +309 -0
  92. package/packs/hono/skills/rpc-typesafe/SKILL.md +388 -0
  93. package/packs/hono/skills/zod-validation/SKILL.md +332 -0
  94. package/packs/nestjs/CHANGELOG.md +29 -0
  95. package/packs/nestjs/README.md +75 -0
  96. package/packs/nestjs/agents/nestjs-architect.md +402 -0
  97. package/packs/nestjs/agents/nestjs-builder.md +301 -0
  98. package/packs/nestjs/agents/nestjs-tester.md +437 -0
  99. package/packs/nestjs/commands/module-create.md +369 -0
  100. package/packs/nestjs/rules/controllers.md +92 -0
  101. package/packs/nestjs/rules/dto.md +124 -0
  102. package/packs/nestjs/rules/entities.md +102 -0
  103. package/packs/nestjs/rules/services.md +106 -0
  104. package/packs/nestjs/skills/barrel-export-manager/SKILL.md +389 -0
  105. package/packs/nestjs/skills/import-convention-enforcer/SKILL.md +365 -0
  106. package/packs/nextjs/CHANGELOG.md +36 -0
  107. package/packs/nextjs/README.md +76 -0
  108. package/packs/nextjs/agents/frontend-tester.md +680 -0
  109. package/packs/nextjs/agents/frontend-visual.md +820 -0
  110. package/packs/nextjs/agents/nextjs-architect.md +331 -0
  111. package/packs/nextjs/agents/nextjs-modular.md +433 -0
  112. package/packs/nextjs/commands/component-create.md +398 -0
  113. package/packs/nextjs/rules/api-routes.md +129 -0
  114. package/packs/nextjs/rules/components.md +106 -0
  115. package/packs/nextjs/rules/hooks.md +132 -0
  116. package/packs/nextjs/skills/accessibility-validator/SKILL.md +445 -0
  117. package/packs/nextjs/skills/import-convention-enforcer/SKILL.md +399 -0
  118. package/packs/nextjs/skills/react-form-validator/SKILL.md +569 -0
  119. package/packs/nuxtjs/CHANGELOG.md +30 -0
  120. package/packs/nuxtjs/README.md +56 -0
  121. package/packs/nuxtjs/agents/frontend-tester.md +680 -0
  122. package/packs/nuxtjs/agents/frontend-visual.md +820 -0
  123. package/packs/nuxtjs/agents/nuxtjs-architect.md +537 -0
  124. package/packs/nuxtjs/commands/component-create.md +223 -0
  125. package/packs/nuxtjs/rules/components.md +101 -0
  126. package/packs/nuxtjs/rules/composables.md +118 -0
  127. package/packs/nuxtjs/rules/server-routes.md +127 -0
  128. package/packs/nuxtjs/skills/accessibility-validator/SKILL.md +183 -0
  129. package/packs/nuxtjs/skills/import-convention-enforcer/SKILL.md +196 -0
  130. package/packs/nuxtjs/skills/veevalidate-form-validator/SKILL.md +190 -0
  131. package/packs/onboard/CHANGELOG.md +22 -0
  132. package/packs/onboard/README.md +103 -0
  133. package/packs/onboard/agents/onboard-guide.md +118 -0
  134. package/packs/onboard/commands/onboard.md +313 -0
  135. package/packs/onboard/skills/onboard-context-provider/SKILL.md +98 -0
  136. package/packs/tanstack-router/.claude-plugin/plugin.json +14 -0
  137. package/packs/tanstack-router/CHANGELOG.md +30 -0
  138. package/packs/tanstack-router/README.md +113 -0
  139. package/packs/tanstack-router/agents/tanstack-architect.md +173 -0
  140. package/packs/tanstack-router/agents/tanstack-builder.md +360 -0
  141. package/packs/tanstack-router/agents/tanstack-tester.md +454 -0
  142. package/packs/tanstack-router/commands/form-create.md +313 -0
  143. package/packs/tanstack-router/commands/query-create.md +263 -0
  144. package/packs/tanstack-router/commands/route-create.md +190 -0
  145. package/packs/tanstack-router/commands/table-create.md +413 -0
  146. package/packs/tanstack-router/skills/ai-patterns/SKILL.md +370 -0
  147. package/packs/tanstack-router/skills/db-patterns/SKILL.md +346 -0
  148. package/packs/tanstack-router/skills/devtools-patterns/SKILL.md +415 -0
  149. package/packs/tanstack-router/skills/form-patterns/SKILL.md +425 -0
  150. package/packs/tanstack-router/skills/pacer-patterns/SKILL.md +341 -0
  151. package/packs/tanstack-router/skills/query-patterns/SKILL.md +359 -0
  152. package/packs/tanstack-router/skills/router-patterns/SKILL.md +285 -0
  153. package/packs/tanstack-router/skills/store-patterns/SKILL.md +351 -0
  154. package/packs/tanstack-router/skills/table-patterns/SKILL.md +531 -0
  155. package/packs/tanstack-router/skills/tanstack-conventions/SKILL.md +428 -0
  156. package/packs/tanstack-router/skills/virtual-patterns/SKILL.md +490 -0
  157. package/packs/worktree/.claude-plugin/plugin.json +19 -0
  158. package/packs/worktree/CHANGELOG.md +24 -0
  159. package/packs/worktree/README.md +110 -0
  160. package/packs/worktree/commands/wt.md +73 -0
  161. package/packs/worktree/scripts/wt.sh +396 -0
  162. package/packs/worktree/skills/worktree-manager/SKILL.md +68 -0
@@ -0,0 +1,285 @@
1
+ ---
2
+ name: TanStack Router Patterns
3
+ description: >-
4
+ Auto-enforce TanStack Router file-based routing conventions. Activates when
5
+ creating routes, configuring navigation, handling route params, or working
6
+ with search params in React SPA applications.
7
+ version: 1.0.0
8
+ ---
9
+
10
+ # TanStack Router Patterns
11
+
12
+ This skill enforces TanStack Router best practices for file-based routing in React SPA applications.
13
+
14
+ ## Route File Naming Conventions
15
+
16
+ | Pattern | Example | URL Path | Purpose |
17
+ |---------|---------|----------|---------|
18
+ | `__root.tsx` | `routes/__root.tsx` | - | Root layout, wraps all routes |
19
+ | `index.tsx` | `routes/index.tsx` | `/` | Index route |
20
+ | `about.tsx` | `routes/about.tsx` | `/about` | Static segment |
21
+ | `posts.tsx` | `routes/posts.tsx` | `/posts` | Layout route (has `<Outlet />`) |
22
+ | `posts.index.tsx` | `routes/posts.index.tsx` | `/posts` | Posts index (nested in layout) |
23
+ | `posts.$postId.tsx` | `routes/posts.$postId.tsx` | `/posts/:postId` | Dynamic parameter |
24
+ | `posts_.$postId.edit.tsx` | `routes/posts_.$postId.edit.tsx` | `/posts/:postId/edit` | Pathless parent layout |
25
+ | `_auth.tsx` | `routes/_auth.tsx` | - | Pathless layout (no URL segment) |
26
+ | `_auth.login.tsx` | `routes/_auth.login.tsx` | `/login` | Child of pathless layout |
27
+ | `(marketing)/` | `routes/(marketing)/about.tsx` | `/about` | Route group (organization only) |
28
+ | `$.tsx` | `routes/$.tsx` | `/*` | Catch-all/splat route |
29
+
30
+ ## Route File Structure
31
+
32
+ ### Basic Route
33
+ ```typescript
34
+ // routes/about.tsx
35
+ import { createFileRoute } from '@tanstack/react-router'
36
+
37
+ export const Route = createFileRoute('/about')({
38
+ component: AboutPage,
39
+ })
40
+
41
+ function AboutPage() {
42
+ return <div>About Page</div>
43
+ }
44
+ ```
45
+
46
+ ### Route with Loader
47
+ ```typescript
48
+ // routes/posts.$postId.tsx
49
+ import { createFileRoute } from '@tanstack/react-router'
50
+ import { postQueryOptions } from '@/features/posts/queries'
51
+
52
+ export const Route = createFileRoute('/posts/$postId')({
53
+ loader: ({ context: { queryClient }, params }) =>
54
+ queryClient.ensureQueryData(postQueryOptions(params.postId)),
55
+ component: PostDetailPage,
56
+ })
57
+
58
+ function PostDetailPage() {
59
+ const { postId } = Route.useParams()
60
+ const post = Route.useLoaderData()
61
+
62
+ return <PostDetail post={post} />
63
+ }
64
+ ```
65
+
66
+ ### Route with Search Params
67
+ ```typescript
68
+ // routes/posts.tsx
69
+ import { createFileRoute } from '@tanstack/react-router'
70
+ import { z } from 'zod'
71
+
72
+ const postsSearchSchema = z.object({
73
+ page: z.number().default(1),
74
+ sort: z.enum(['newest', 'oldest', 'popular']).default('newest'),
75
+ search: z.string().optional(),
76
+ })
77
+
78
+ export const Route = createFileRoute('/posts')({
79
+ validateSearch: postsSearchSchema,
80
+ component: PostsPage,
81
+ })
82
+
83
+ function PostsPage() {
84
+ const { page, sort, search } = Route.useSearch()
85
+ const navigate = Route.useNavigate()
86
+
87
+ const setPage = (newPage: number) => {
88
+ navigate({ search: (prev) => ({ ...prev, page: newPage }) })
89
+ }
90
+
91
+ return <PostList page={page} sort={sort} search={search} onPageChange={setPage} />
92
+ }
93
+ ```
94
+
95
+ ### Root Route with Context
96
+ ```typescript
97
+ // routes/__root.tsx
98
+ import { createRootRouteWithContext, Outlet } from '@tanstack/react-router'
99
+ import type { QueryClient } from '@tanstack/react-query'
100
+
101
+ interface RouterContext {
102
+ queryClient: QueryClient
103
+ }
104
+
105
+ export const Route = createRootRouteWithContext<RouterContext>()({
106
+ component: RootComponent,
107
+ beforeLoad: async ({ context }) => {
108
+ // Auth check, theme loading, etc.
109
+ return { user: await getUser() }
110
+ },
111
+ })
112
+
113
+ function RootComponent() {
114
+ return (
115
+ <div>
116
+ <Header />
117
+ <Outlet />
118
+ <Footer />
119
+ </div>
120
+ )
121
+ }
122
+ ```
123
+
124
+ ### Pathless Layout Route
125
+ ```typescript
126
+ // routes/_auth.tsx
127
+ import { createFileRoute, Outlet, redirect } from '@tanstack/react-router'
128
+
129
+ export const Route = createFileRoute('/_auth')({
130
+ beforeLoad: async ({ context }) => {
131
+ if (!context.user) {
132
+ throw redirect({ to: '/login' })
133
+ }
134
+ },
135
+ component: AuthLayout,
136
+ })
137
+
138
+ function AuthLayout() {
139
+ return (
140
+ <div className="authenticated-layout">
141
+ <Sidebar />
142
+ <main>
143
+ <Outlet />
144
+ </main>
145
+ </div>
146
+ )
147
+ }
148
+ ```
149
+
150
+ ## Navigation Patterns
151
+
152
+ ### Declarative Navigation
153
+ ```typescript
154
+ import { Link } from '@tanstack/react-router'
155
+
156
+ // Basic link
157
+ <Link to="/about">About</Link>
158
+
159
+ // With params
160
+ <Link to="/posts/$postId" params={{ postId: '123' }}>
161
+ View Post
162
+ </Link>
163
+
164
+ // With search params
165
+ <Link to="/posts" search={{ page: 2, sort: 'newest' }}>
166
+ Page 2
167
+ </Link>
168
+
169
+ // Active styling
170
+ <Link
171
+ to="/posts"
172
+ activeProps={{ className: 'active' }}
173
+ inactiveProps={{ className: 'inactive' }}
174
+ >
175
+ Posts
176
+ </Link>
177
+ ```
178
+
179
+ ### Imperative Navigation
180
+ ```typescript
181
+ import { useNavigate } from '@tanstack/react-router'
182
+
183
+ function PostCard({ post }) {
184
+ const navigate = useNavigate()
185
+
186
+ const handleClick = () => {
187
+ navigate({
188
+ to: '/posts/$postId',
189
+ params: { postId: post.id },
190
+ search: { tab: 'comments' }
191
+ })
192
+ }
193
+
194
+ return <div onClick={handleClick}>{post.title}</div>
195
+ }
196
+ ```
197
+
198
+ ### Programmatic Redirect
199
+ ```typescript
200
+ import { redirect } from '@tanstack/react-router'
201
+
202
+ export const Route = createFileRoute('/admin')({
203
+ beforeLoad: async ({ context }) => {
204
+ if (!context.user?.isAdmin) {
205
+ throw redirect({ to: '/', search: { error: 'unauthorized' } })
206
+ }
207
+ },
208
+ })
209
+ ```
210
+
211
+ ## Error Handling
212
+
213
+ ### Route Error Boundary
214
+ ```typescript
215
+ export const Route = createFileRoute('/posts/$postId')({
216
+ loader: async ({ params }) => {
217
+ const post = await getPost(params.postId)
218
+ if (!post) {
219
+ throw new Error('Post not found')
220
+ }
221
+ return post
222
+ },
223
+ errorComponent: ({ error }) => (
224
+ <div className="error">
225
+ <h2>Error loading post</h2>
226
+ <p>{error.message}</p>
227
+ </div>
228
+ ),
229
+ component: PostPage,
230
+ })
231
+ ```
232
+
233
+ ### Pending Component
234
+ ```typescript
235
+ export const Route = createFileRoute('/posts')({
236
+ pendingComponent: () => <div>Loading posts...</div>,
237
+ pendingMinMs: 500, // Show pending after 500ms
238
+ pendingMs: 1000, // Minimum pending display time
239
+ component: PostsPage,
240
+ })
241
+ ```
242
+
243
+ ## Conventions to Enforce
244
+
245
+ 1. **Always use `createFileRoute`** - Never manual route configuration
246
+ 2. **Type-safe params** - Use `Route.useParams()` for full type inference
247
+ 3. **Validate search params** - Use Zod schemas with `validateSearch`
248
+ 4. **Loaders for data** - Prefetch with `ensureQueryData`, not direct fetches
249
+ 5. **Context for shared data** - Auth, theme, queryClient in root context
250
+ 6. **Error boundaries** - Always provide `errorComponent` for data routes
251
+ 7. **Pathless layouts** - Use `_` prefix for auth guards, feature layouts
252
+
253
+ ## Anti-Patterns to Block
254
+
255
+ ```typescript
256
+ // ❌ WRONG: Manual route definition
257
+ const router = createRouter({
258
+ routes: [{ path: '/posts', component: Posts }]
259
+ })
260
+
261
+ // ✅ CORRECT: File-based routes
262
+ // routes/posts.tsx with createFileRoute
263
+
264
+ // ❌ WRONG: useParams from react-router-dom
265
+ import { useParams } from 'react-router-dom'
266
+
267
+ // ✅ CORRECT: Route-specific useParams
268
+ const { postId } = Route.useParams()
269
+
270
+ // ❌ WRONG: Unvalidated search params
271
+ const search = new URLSearchParams(window.location.search)
272
+
273
+ // ✅ CORRECT: Validated with Zod
274
+ const { page, sort } = Route.useSearch()
275
+
276
+ // ❌ WRONG: Direct fetch in loader
277
+ loader: async () => {
278
+ const response = await fetch('/api/posts')
279
+ return response.json()
280
+ }
281
+
282
+ // ✅ CORRECT: Use query client
283
+ loader: ({ context: { queryClient } }) =>
284
+ queryClient.ensureQueryData(postsQueryOptions())
285
+ ```
@@ -0,0 +1,351 @@
1
+ ---
2
+ name: TanStack Store Patterns (Alpha)
3
+ description: >-
4
+ TanStack Store patterns for framework-agnostic reactive state. Activates when
5
+ implementing client-side state, global stores, or reactive state management
6
+ in React applications. NOTE: Alpha library - API may change.
7
+ version: 1.0.0
8
+ ---
9
+
10
+ # TanStack Store Patterns (Alpha)
11
+
12
+ > **Alpha Library**: TanStack Store is in alpha. APIs may change between versions.
13
+
14
+ This skill covers TanStack Store for framework-agnostic reactive state management.
15
+
16
+ ## Basic Store
17
+
18
+ ```typescript
19
+ // lib/stores/counter-store.ts
20
+ import { Store } from '@tanstack/store'
21
+
22
+ export const counterStore = new Store({
23
+ count: 0,
24
+ })
25
+
26
+ // Actions
27
+ export const increment = () => {
28
+ counterStore.setState((state) => ({
29
+ ...state,
30
+ count: state.count + 1,
31
+ }))
32
+ }
33
+
34
+ export const decrement = () => {
35
+ counterStore.setState((state) => ({
36
+ ...state,
37
+ count: state.count - 1,
38
+ }))
39
+ }
40
+
41
+ export const reset = () => {
42
+ counterStore.setState((state) => ({
43
+ ...state,
44
+ count: 0,
45
+ }))
46
+ }
47
+ ```
48
+
49
+ ## Using Store in React
50
+
51
+ ```typescript
52
+ import { useStore } from '@tanstack/react-store'
53
+ import { counterStore, increment, decrement, reset } from '@/lib/stores/counter-store'
54
+
55
+ function Counter() {
56
+ const count = useStore(counterStore, (state) => state.count)
57
+
58
+ return (
59
+ <div>
60
+ <span>Count: {count}</span>
61
+ <button onClick={decrement}>-</button>
62
+ <button onClick={increment}>+</button>
63
+ <button onClick={reset}>Reset</button>
64
+ </div>
65
+ )
66
+ }
67
+ ```
68
+
69
+ ## Complex Store with Derived State
70
+
71
+ ```typescript
72
+ // lib/stores/cart-store.ts
73
+ import { Store } from '@tanstack/store'
74
+
75
+ interface CartItem {
76
+ id: string
77
+ name: string
78
+ price: number
79
+ quantity: number
80
+ }
81
+
82
+ interface CartState {
83
+ items: CartItem[]
84
+ }
85
+
86
+ export const cartStore = new Store<CartState>({
87
+ items: [],
88
+ })
89
+
90
+ // Derived selectors
91
+ export const selectCartItems = (state: CartState) => state.items
92
+
93
+ export const selectCartTotal = (state: CartState) =>
94
+ state.items.reduce((sum, item) => sum + item.price * item.quantity, 0)
95
+
96
+ export const selectCartCount = (state: CartState) =>
97
+ state.items.reduce((sum, item) => sum + item.quantity, 0)
98
+
99
+ // Actions
100
+ export const addToCart = (item: Omit<CartItem, 'quantity'>) => {
101
+ cartStore.setState((state) => {
102
+ const existing = state.items.find((i) => i.id === item.id)
103
+ if (existing) {
104
+ return {
105
+ ...state,
106
+ items: state.items.map((i) =>
107
+ i.id === item.id ? { ...i, quantity: i.quantity + 1 } : i
108
+ ),
109
+ }
110
+ }
111
+ return {
112
+ ...state,
113
+ items: [...state.items, { ...item, quantity: 1 }],
114
+ }
115
+ })
116
+ }
117
+
118
+ export const removeFromCart = (itemId: string) => {
119
+ cartStore.setState((state) => ({
120
+ ...state,
121
+ items: state.items.filter((i) => i.id !== itemId),
122
+ }))
123
+ }
124
+
125
+ export const updateQuantity = (itemId: string, quantity: number) => {
126
+ if (quantity <= 0) {
127
+ removeFromCart(itemId)
128
+ return
129
+ }
130
+ cartStore.setState((state) => ({
131
+ ...state,
132
+ items: state.items.map((i) =>
133
+ i.id === itemId ? { ...i, quantity } : i
134
+ ),
135
+ }))
136
+ }
137
+
138
+ export const clearCart = () => {
139
+ cartStore.setState(() => ({ items: [] }))
140
+ }
141
+ ```
142
+
143
+ ## Using Complex Store
144
+
145
+ ```typescript
146
+ import { useStore } from '@tanstack/react-store'
147
+ import {
148
+ cartStore,
149
+ selectCartItems,
150
+ selectCartTotal,
151
+ selectCartCount,
152
+ updateQuantity,
153
+ removeFromCart,
154
+ } from '@/lib/stores/cart-store'
155
+
156
+ function CartSummary() {
157
+ const count = useStore(cartStore, selectCartCount)
158
+ const total = useStore(cartStore, selectCartTotal)
159
+
160
+ return (
161
+ <div>
162
+ <span>{count} items</span>
163
+ <span>${total.toFixed(2)}</span>
164
+ </div>
165
+ )
166
+ }
167
+
168
+ function CartItems() {
169
+ const items = useStore(cartStore, selectCartItems)
170
+
171
+ return (
172
+ <ul>
173
+ {items.map((item) => (
174
+ <li key={item.id}>
175
+ <span>{item.name}</span>
176
+ <input
177
+ type="number"
178
+ value={item.quantity}
179
+ onChange={(e) => updateQuantity(item.id, parseInt(e.target.value))}
180
+ min={0}
181
+ />
182
+ <button onClick={() => removeFromCart(item.id)}>Remove</button>
183
+ </li>
184
+ ))}
185
+ </ul>
186
+ )
187
+ }
188
+ ```
189
+
190
+ ## Store with Persistence
191
+
192
+ ```typescript
193
+ // lib/stores/theme-store.ts
194
+ import { Store } from '@tanstack/store'
195
+
196
+ type Theme = 'light' | 'dark' | 'system'
197
+
198
+ interface ThemeState {
199
+ theme: Theme
200
+ }
201
+
202
+ const getInitialTheme = (): Theme => {
203
+ if (typeof window === 'undefined') return 'system'
204
+ const stored = localStorage.getItem('theme')
205
+ if (stored === 'light' || stored === 'dark' || stored === 'system') {
206
+ return stored
207
+ }
208
+ return 'system'
209
+ }
210
+
211
+ export const themeStore = new Store<ThemeState>({
212
+ theme: getInitialTheme(),
213
+ })
214
+
215
+ // Subscribe to persist changes
216
+ themeStore.subscribe(() => {
217
+ const { theme } = themeStore.state
218
+ localStorage.setItem('theme', theme)
219
+
220
+ // Apply theme to document
221
+ const root = document.documentElement
222
+ if (theme === 'system') {
223
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
224
+ root.classList.toggle('dark', prefersDark)
225
+ } else {
226
+ root.classList.toggle('dark', theme === 'dark')
227
+ }
228
+ })
229
+
230
+ export const setTheme = (theme: Theme) => {
231
+ themeStore.setState(() => ({ theme }))
232
+ }
233
+ ```
234
+
235
+ ## Store Factory Pattern
236
+
237
+ ```typescript
238
+ // lib/stores/create-entity-store.ts
239
+ import { Store } from '@tanstack/store'
240
+
241
+ interface EntityState<T> {
242
+ entities: Record<string, T>
243
+ ids: string[]
244
+ loading: boolean
245
+ error: string | null
246
+ }
247
+
248
+ export function createEntityStore<T extends { id: string }>() {
249
+ const store = new Store<EntityState<T>>({
250
+ entities: {},
251
+ ids: [],
252
+ loading: false,
253
+ error: null,
254
+ })
255
+
256
+ return {
257
+ store,
258
+
259
+ selectAll: (state: EntityState<T>) =>
260
+ state.ids.map((id) => state.entities[id]),
261
+
262
+ selectById: (id: string) => (state: EntityState<T>) =>
263
+ state.entities[id],
264
+
265
+ setMany: (items: T[]) => {
266
+ store.setState((state) => ({
267
+ ...state,
268
+ entities: items.reduce((acc, item) => ({ ...acc, [item.id]: item }), state.entities),
269
+ ids: [...new Set([...state.ids, ...items.map((i) => i.id)])],
270
+ }))
271
+ },
272
+
273
+ setOne: (item: T) => {
274
+ store.setState((state) => ({
275
+ ...state,
276
+ entities: { ...state.entities, [item.id]: item },
277
+ ids: state.ids.includes(item.id) ? state.ids : [...state.ids, item.id],
278
+ }))
279
+ },
280
+
281
+ removeOne: (id: string) => {
282
+ store.setState((state) => {
283
+ const { [id]: removed, ...entities } = state.entities
284
+ return {
285
+ ...state,
286
+ entities,
287
+ ids: state.ids.filter((i) => i !== id),
288
+ }
289
+ })
290
+ },
291
+
292
+ setLoading: (loading: boolean) => {
293
+ store.setState((state) => ({ ...state, loading }))
294
+ },
295
+
296
+ setError: (error: string | null) => {
297
+ store.setState((state) => ({ ...state, error }))
298
+ },
299
+ }
300
+ }
301
+
302
+ // Usage
303
+ const usersStore = createEntityStore<User>()
304
+ ```
305
+
306
+ ## When to Use TanStack Store vs Query
307
+
308
+ | Use Case | Solution |
309
+ |----------|----------|
310
+ | Server data | TanStack Query |
311
+ | URL state | TanStack Router |
312
+ | Form state | TanStack Form |
313
+ | Local UI state | React useState |
314
+ | Shared client state | TanStack Store |
315
+ | Theme/preferences | TanStack Store + localStorage |
316
+ | Cart/wishlist | TanStack Store |
317
+
318
+ ## Conventions
319
+
320
+ 1. **Separate stores by domain** - One store per feature/concern
321
+ 2. **Selector functions** - Define selectors for derived state
322
+ 3. **Action functions** - Export actions, don't expose setState directly
323
+ 4. **Type safety** - Always type your store state
324
+ 5. **Persistence** - Use subscribe for localStorage sync
325
+ 6. **Prefer Query** - Use Query for server state, Store for client-only
326
+
327
+ ## Anti-Patterns
328
+
329
+ ```typescript
330
+ // ❌ WRONG: Mutating state directly
331
+ cartStore.state.items.push(newItem)
332
+
333
+ // ✅ CORRECT: Using setState
334
+ cartStore.setState((state) => ({
335
+ ...state,
336
+ items: [...state.items, newItem],
337
+ }))
338
+
339
+ // ❌ WRONG: Storing server data
340
+ const postsStore = new Store({ posts: [] })
341
+
342
+ // ✅ CORRECT: Use Query for server data
343
+ const { data: posts } = useQuery(postsQueryOptions())
344
+
345
+ // ❌ WRONG: Exposing setState directly
346
+ export { counterStore }
347
+ // Component: counterStore.setState(...)
348
+
349
+ // ✅ CORRECT: Export action functions
350
+ export const increment = () => counterStore.setState(...)
351
+ ```