autoworkflow 3.1.5 → 3.5.0
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/.claude/commands/analyze.md +19 -0
- package/.claude/commands/audit.md +26 -0
- package/.claude/commands/build.md +39 -0
- package/.claude/commands/commit.md +25 -0
- package/.claude/commands/fix.md +23 -0
- package/.claude/commands/plan.md +18 -0
- package/.claude/commands/suggest.md +23 -0
- package/.claude/commands/verify.md +18 -0
- package/.claude/hooks/post-bash-router.sh +20 -0
- package/.claude/hooks/post-commit.sh +140 -0
- package/.claude/hooks/pre-edit.sh +129 -0
- package/.claude/hooks/session-check.sh +79 -0
- package/.claude/settings.json +40 -6
- package/.claude/settings.local.json +3 -1
- package/.claude/skills/actix.md +337 -0
- package/.claude/skills/alembic.md +504 -0
- package/.claude/skills/angular.md +237 -0
- package/.claude/skills/api-design.md +187 -0
- package/.claude/skills/aspnet-core.md +377 -0
- package/.claude/skills/astro.md +245 -0
- package/.claude/skills/auth-clerk.md +327 -0
- package/.claude/skills/auth-firebase.md +367 -0
- package/.claude/skills/auth-nextauth.md +359 -0
- package/.claude/skills/auth-supabase.md +368 -0
- package/.claude/skills/axum.md +386 -0
- package/.claude/skills/blazor.md +456 -0
- package/.claude/skills/chi.md +348 -0
- package/.claude/skills/code-review.md +133 -0
- package/.claude/skills/csharp.md +296 -0
- package/.claude/skills/css-modules.md +325 -0
- package/.claude/skills/cypress.md +343 -0
- package/.claude/skills/debugging.md +133 -0
- package/.claude/skills/diesel.md +392 -0
- package/.claude/skills/django.md +301 -0
- package/.claude/skills/docker.md +319 -0
- package/.claude/skills/doctrine.md +473 -0
- package/.claude/skills/documentation.md +182 -0
- package/.claude/skills/dotnet.md +409 -0
- package/.claude/skills/drizzle.md +293 -0
- package/.claude/skills/echo.md +321 -0
- package/.claude/skills/eloquent.md +256 -0
- package/.claude/skills/emotion.md +426 -0
- package/.claude/skills/entity-framework.md +370 -0
- package/.claude/skills/express.md +316 -0
- package/.claude/skills/fastapi.md +329 -0
- package/.claude/skills/fastify.md +299 -0
- package/.claude/skills/fiber.md +315 -0
- package/.claude/skills/flask.md +322 -0
- package/.claude/skills/gin.md +342 -0
- package/.claude/skills/git.md +116 -0
- package/.claude/skills/github-actions.md +353 -0
- package/.claude/skills/go.md +377 -0
- package/.claude/skills/gorm.md +409 -0
- package/.claude/skills/graphql.md +478 -0
- package/.claude/skills/hibernate.md +379 -0
- package/.claude/skills/hono.md +306 -0
- package/.claude/skills/java.md +400 -0
- package/.claude/skills/jest.md +313 -0
- package/.claude/skills/jpa.md +282 -0
- package/.claude/skills/kotlin.md +347 -0
- package/.claude/skills/kubernetes.md +363 -0
- package/.claude/skills/laravel.md +414 -0
- package/.claude/skills/mcp-browser.md +320 -0
- package/.claude/skills/mcp-database.md +219 -0
- package/.claude/skills/mcp-fetch.md +241 -0
- package/.claude/skills/mcp-filesystem.md +204 -0
- package/.claude/skills/mcp-github.md +217 -0
- package/.claude/skills/mcp-memory.md +240 -0
- package/.claude/skills/mcp-search.md +218 -0
- package/.claude/skills/mcp-slack.md +262 -0
- package/.claude/skills/micronaut.md +388 -0
- package/.claude/skills/mongodb.md +319 -0
- package/.claude/skills/mongoose.md +355 -0
- package/.claude/skills/mysql.md +281 -0
- package/.claude/skills/nestjs.md +335 -0
- package/.claude/skills/nextjs-app-router.md +260 -0
- package/.claude/skills/nextjs-pages.md +172 -0
- package/.claude/skills/nuxt.md +202 -0
- package/.claude/skills/openapi.md +489 -0
- package/.claude/skills/performance.md +199 -0
- package/.claude/skills/php.md +398 -0
- package/.claude/skills/playwright.md +371 -0
- package/.claude/skills/postgresql.md +257 -0
- package/.claude/skills/prisma.md +293 -0
- package/.claude/skills/pydantic.md +304 -0
- package/.claude/skills/pytest.md +313 -0
- package/.claude/skills/python.md +272 -0
- package/.claude/skills/quarkus.md +377 -0
- package/.claude/skills/react.md +230 -0
- package/.claude/skills/redis.md +391 -0
- package/.claude/skills/refactoring.md +143 -0
- package/.claude/skills/remix.md +246 -0
- package/.claude/skills/rest-api.md +490 -0
- package/.claude/skills/rocket.md +366 -0
- package/.claude/skills/rust.md +341 -0
- package/.claude/skills/sass.md +380 -0
- package/.claude/skills/sea-orm.md +382 -0
- package/.claude/skills/security.md +167 -0
- package/.claude/skills/sequelize.md +395 -0
- package/.claude/skills/spring-boot.md +416 -0
- package/.claude/skills/sqlalchemy.md +269 -0
- package/.claude/skills/sqlx-rust.md +408 -0
- package/.claude/skills/state-jotai.md +346 -0
- package/.claude/skills/state-mobx.md +353 -0
- package/.claude/skills/state-pinia.md +431 -0
- package/.claude/skills/state-redux.md +337 -0
- package/.claude/skills/state-tanstack-query.md +434 -0
- package/.claude/skills/state-zustand.md +340 -0
- package/.claude/skills/styled-components.md +403 -0
- package/.claude/skills/svelte.md +238 -0
- package/.claude/skills/sveltekit.md +207 -0
- package/.claude/skills/symfony.md +437 -0
- package/.claude/skills/tailwind.md +279 -0
- package/.claude/skills/terraform.md +394 -0
- package/.claude/skills/testing-library.md +371 -0
- package/.claude/skills/trpc.md +426 -0
- package/.claude/skills/typeorm.md +368 -0
- package/.claude/skills/vitest.md +330 -0
- package/.claude/skills/vue.md +202 -0
- package/.claude/skills/warp.md +365 -0
- package/README.md +135 -52
- package/package.json +1 -1
- package/system/triggers.md +152 -11
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
# Zustand Skill
|
|
2
|
+
|
|
3
|
+
## Basic Store
|
|
4
|
+
\`\`\`typescript
|
|
5
|
+
import { create } from 'zustand';
|
|
6
|
+
|
|
7
|
+
interface User {
|
|
8
|
+
id: string;
|
|
9
|
+
email: string;
|
|
10
|
+
name: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface UserStore {
|
|
14
|
+
// State
|
|
15
|
+
user: User | null;
|
|
16
|
+
loading: boolean;
|
|
17
|
+
error: string | null;
|
|
18
|
+
|
|
19
|
+
// Actions
|
|
20
|
+
setUser: (user: User) => void;
|
|
21
|
+
clearUser: () => void;
|
|
22
|
+
fetchUser: (id: string) => Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const useUserStore = create<UserStore>((set, get) => ({
|
|
26
|
+
// Initial state
|
|
27
|
+
user: null,
|
|
28
|
+
loading: false,
|
|
29
|
+
error: null,
|
|
30
|
+
|
|
31
|
+
// Sync actions
|
|
32
|
+
setUser: (user) => set({ user }),
|
|
33
|
+
clearUser: () => set({ user: null, error: null }),
|
|
34
|
+
|
|
35
|
+
// Async action
|
|
36
|
+
fetchUser: async (id) => {
|
|
37
|
+
set({ loading: true, error: null });
|
|
38
|
+
try {
|
|
39
|
+
const response = await fetch(\`/api/users/\${id}\`);
|
|
40
|
+
if (!response.ok) throw new Error('Failed to fetch user');
|
|
41
|
+
const user = await response.json();
|
|
42
|
+
set({ user, loading: false });
|
|
43
|
+
} catch (error) {
|
|
44
|
+
set({ error: (error as Error).message, loading: false });
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
}));
|
|
48
|
+
|
|
49
|
+
// Usage in components
|
|
50
|
+
function UserProfile({ userId }: { userId: string }) {
|
|
51
|
+
const { user, loading, fetchUser } = useUserStore();
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
fetchUser(userId);
|
|
55
|
+
}, [userId, fetchUser]);
|
|
56
|
+
|
|
57
|
+
if (loading) return <div>Loading...</div>;
|
|
58
|
+
return <div>{user?.name}</div>;
|
|
59
|
+
}
|
|
60
|
+
\`\`\`
|
|
61
|
+
|
|
62
|
+
## Selectors (Performance Optimization)
|
|
63
|
+
\`\`\`typescript
|
|
64
|
+
// ❌ Bad: Re-renders on any state change
|
|
65
|
+
const { user, loading, error } = useUserStore();
|
|
66
|
+
|
|
67
|
+
// ✅ Good: Only re-renders when selected state changes
|
|
68
|
+
const user = useUserStore((state) => state.user);
|
|
69
|
+
const loading = useUserStore((state) => state.loading);
|
|
70
|
+
|
|
71
|
+
// Multiple values with shallow comparison
|
|
72
|
+
import { shallow } from 'zustand/shallow';
|
|
73
|
+
|
|
74
|
+
const { user, loading } = useUserStore(
|
|
75
|
+
(state) => ({ user: state.user, loading: state.loading }),
|
|
76
|
+
shallow
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
// Or use useShallow hook
|
|
80
|
+
import { useShallow } from 'zustand/react/shallow';
|
|
81
|
+
|
|
82
|
+
const { user, loading } = useUserStore(
|
|
83
|
+
useShallow((state) => ({ user: state.user, loading: state.loading }))
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
// Derived/computed values
|
|
87
|
+
const userName = useUserStore((state) => state.user?.name);
|
|
88
|
+
const isLoggedIn = useUserStore((state) => !!state.user);
|
|
89
|
+
\`\`\`
|
|
90
|
+
|
|
91
|
+
## Slices Pattern (Multiple Stores)
|
|
92
|
+
\`\`\`typescript
|
|
93
|
+
import { create, StateCreator } from 'zustand';
|
|
94
|
+
|
|
95
|
+
// User slice
|
|
96
|
+
interface UserSlice {
|
|
97
|
+
user: User | null;
|
|
98
|
+
setUser: (user: User) => void;
|
|
99
|
+
logout: () => void;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const createUserSlice: StateCreator<
|
|
103
|
+
UserSlice & CartSlice,
|
|
104
|
+
[],
|
|
105
|
+
[],
|
|
106
|
+
UserSlice
|
|
107
|
+
> = (set) => ({
|
|
108
|
+
user: null,
|
|
109
|
+
setUser: (user) => set({ user }),
|
|
110
|
+
logout: () => set({ user: null }),
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// Cart slice
|
|
114
|
+
interface CartSlice {
|
|
115
|
+
items: CartItem[];
|
|
116
|
+
addItem: (item: CartItem) => void;
|
|
117
|
+
removeItem: (id: string) => void;
|
|
118
|
+
clearCart: () => void;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const createCartSlice: StateCreator<
|
|
122
|
+
UserSlice & CartSlice,
|
|
123
|
+
[],
|
|
124
|
+
[],
|
|
125
|
+
CartSlice
|
|
126
|
+
> = (set) => ({
|
|
127
|
+
items: [],
|
|
128
|
+
addItem: (item) => set((state) => ({ items: [...state.items, item] })),
|
|
129
|
+
removeItem: (id) => set((state) => ({
|
|
130
|
+
items: state.items.filter((item) => item.id !== id),
|
|
131
|
+
})),
|
|
132
|
+
clearCart: () => set({ items: [] }),
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Combined store
|
|
136
|
+
type StoreState = UserSlice & CartSlice;
|
|
137
|
+
|
|
138
|
+
const useStore = create<StoreState>()((...a) => ({
|
|
139
|
+
...createUserSlice(...a),
|
|
140
|
+
...createCartSlice(...a),
|
|
141
|
+
}));
|
|
142
|
+
|
|
143
|
+
// Usage
|
|
144
|
+
const user = useStore((state) => state.user);
|
|
145
|
+
const items = useStore((state) => state.items);
|
|
146
|
+
\`\`\`
|
|
147
|
+
|
|
148
|
+
## Middleware
|
|
149
|
+
|
|
150
|
+
### Persist (localStorage/sessionStorage)
|
|
151
|
+
\`\`\`typescript
|
|
152
|
+
import { create } from 'zustand';
|
|
153
|
+
import { persist, createJSONStorage } from 'zustand/middleware';
|
|
154
|
+
|
|
155
|
+
const useUserStore = create<UserStore>()(
|
|
156
|
+
persist(
|
|
157
|
+
(set, get) => ({
|
|
158
|
+
user: null,
|
|
159
|
+
setUser: (user) => set({ user }),
|
|
160
|
+
logout: () => set({ user: null }),
|
|
161
|
+
}),
|
|
162
|
+
{
|
|
163
|
+
name: 'user-storage', // localStorage key
|
|
164
|
+
storage: createJSONStorage(() => localStorage), // default
|
|
165
|
+
// Or sessionStorage:
|
|
166
|
+
// storage: createJSONStorage(() => sessionStorage),
|
|
167
|
+
|
|
168
|
+
// Partial persistence (only persist some fields)
|
|
169
|
+
partialize: (state) => ({ user: state.user }),
|
|
170
|
+
|
|
171
|
+
// Version for migrations
|
|
172
|
+
version: 1,
|
|
173
|
+
migrate: (persistedState, version) => {
|
|
174
|
+
if (version === 0) {
|
|
175
|
+
// Migration logic
|
|
176
|
+
}
|
|
177
|
+
return persistedState as UserStore;
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
// Skip hydration until ready
|
|
181
|
+
skipHydration: true,
|
|
182
|
+
}
|
|
183
|
+
)
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
// Manual hydration (when skipHydration: true)
|
|
187
|
+
useEffect(() => {
|
|
188
|
+
useUserStore.persist.rehydrate();
|
|
189
|
+
}, []);
|
|
190
|
+
|
|
191
|
+
// Clear persisted state
|
|
192
|
+
useUserStore.persist.clearStorage();
|
|
193
|
+
\`\`\`
|
|
194
|
+
|
|
195
|
+
### DevTools
|
|
196
|
+
\`\`\`typescript
|
|
197
|
+
import { create } from 'zustand';
|
|
198
|
+
import { devtools } from 'zustand/middleware';
|
|
199
|
+
|
|
200
|
+
const useStore = create<StoreState>()(
|
|
201
|
+
devtools(
|
|
202
|
+
(set) => ({
|
|
203
|
+
// ... store
|
|
204
|
+
}),
|
|
205
|
+
{
|
|
206
|
+
name: 'MyApp Store', // Name in DevTools
|
|
207
|
+
enabled: process.env.NODE_ENV === 'development',
|
|
208
|
+
}
|
|
209
|
+
)
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
// Named actions in DevTools
|
|
213
|
+
set({ user }, false, 'setUser');
|
|
214
|
+
set({ user: null }, false, 'logout');
|
|
215
|
+
\`\`\`
|
|
216
|
+
|
|
217
|
+
### Immer (Immutable Updates)
|
|
218
|
+
\`\`\`typescript
|
|
219
|
+
import { create } from 'zustand';
|
|
220
|
+
import { immer } from 'zustand/middleware/immer';
|
|
221
|
+
|
|
222
|
+
interface TodoStore {
|
|
223
|
+
todos: Todo[];
|
|
224
|
+
addTodo: (text: string) => void;
|
|
225
|
+
toggleTodo: (id: string) => void;
|
|
226
|
+
updateTodo: (id: string, text: string) => void;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const useTodoStore = create<TodoStore>()(
|
|
230
|
+
immer((set) => ({
|
|
231
|
+
todos: [],
|
|
232
|
+
|
|
233
|
+
addTodo: (text) =>
|
|
234
|
+
set((state) => {
|
|
235
|
+
state.todos.push({ id: crypto.randomUUID(), text, completed: false });
|
|
236
|
+
}),
|
|
237
|
+
|
|
238
|
+
toggleTodo: (id) =>
|
|
239
|
+
set((state) => {
|
|
240
|
+
const todo = state.todos.find((t) => t.id === id);
|
|
241
|
+
if (todo) todo.completed = !todo.completed;
|
|
242
|
+
}),
|
|
243
|
+
|
|
244
|
+
updateTodo: (id, text) =>
|
|
245
|
+
set((state) => {
|
|
246
|
+
const todo = state.todos.find((t) => t.id === id);
|
|
247
|
+
if (todo) todo.text = text;
|
|
248
|
+
}),
|
|
249
|
+
}))
|
|
250
|
+
);
|
|
251
|
+
\`\`\`
|
|
252
|
+
|
|
253
|
+
### Combining Middleware
|
|
254
|
+
\`\`\`typescript
|
|
255
|
+
const useStore = create<StoreState>()(
|
|
256
|
+
devtools(
|
|
257
|
+
persist(
|
|
258
|
+
immer((set) => ({
|
|
259
|
+
// ... store implementation
|
|
260
|
+
})),
|
|
261
|
+
{ name: 'app-storage' }
|
|
262
|
+
),
|
|
263
|
+
{ name: 'AppStore' }
|
|
264
|
+
)
|
|
265
|
+
);
|
|
266
|
+
\`\`\`
|
|
267
|
+
|
|
268
|
+
## Subscribing to Changes
|
|
269
|
+
\`\`\`typescript
|
|
270
|
+
// Subscribe outside React
|
|
271
|
+
const unsubscribe = useUserStore.subscribe(
|
|
272
|
+
(state) => console.log('State changed:', state)
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
// Subscribe to specific slice
|
|
276
|
+
const unsubscribe = useUserStore.subscribe(
|
|
277
|
+
(state) => state.user,
|
|
278
|
+
(user, prevUser) => {
|
|
279
|
+
console.log('User changed:', { user, prevUser });
|
|
280
|
+
}
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
// Get state outside React
|
|
284
|
+
const currentUser = useUserStore.getState().user;
|
|
285
|
+
|
|
286
|
+
// Set state outside React
|
|
287
|
+
useUserStore.setState({ user: newUser });
|
|
288
|
+
|
|
289
|
+
// With action
|
|
290
|
+
useUserStore.getState().setUser(newUser);
|
|
291
|
+
\`\`\`
|
|
292
|
+
|
|
293
|
+
## Testing
|
|
294
|
+
\`\`\`typescript
|
|
295
|
+
import { act, renderHook } from '@testing-library/react';
|
|
296
|
+
import { useUserStore } from './userStore';
|
|
297
|
+
|
|
298
|
+
// Reset store between tests
|
|
299
|
+
beforeEach(() => {
|
|
300
|
+
useUserStore.setState({ user: null, loading: false, error: null });
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
test('should set user', () => {
|
|
304
|
+
const { result } = renderHook(() => useUserStore());
|
|
305
|
+
|
|
306
|
+
act(() => {
|
|
307
|
+
result.current.setUser({ id: '1', email: 'test@example.com', name: 'Test' });
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
expect(result.current.user).toEqual({
|
|
311
|
+
id: '1',
|
|
312
|
+
email: 'test@example.com',
|
|
313
|
+
name: 'Test',
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
// Test with initial state
|
|
318
|
+
test('should start with loading false', () => {
|
|
319
|
+
const { result } = renderHook(() =>
|
|
320
|
+
useUserStore((state) => state.loading)
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
expect(result.current).toBe(false);
|
|
324
|
+
});
|
|
325
|
+
\`\`\`
|
|
326
|
+
|
|
327
|
+
## ❌ DON'T
|
|
328
|
+
- Destructure entire store (causes unnecessary re-renders)
|
|
329
|
+
- Put functions in state that should be actions
|
|
330
|
+
- Forget to use shallow comparison for object selectors
|
|
331
|
+
- Mutate state directly (use set or immer)
|
|
332
|
+
|
|
333
|
+
## ✅ DO
|
|
334
|
+
- Use selectors for fine-grained subscriptions
|
|
335
|
+
- Use shallow for multiple values
|
|
336
|
+
- Use persist middleware for local storage
|
|
337
|
+
- Use devtools in development
|
|
338
|
+
- Use immer for complex nested updates
|
|
339
|
+
- Use slices for large stores
|
|
340
|
+
- Reset store state in tests
|