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.
Files changed (123) hide show
  1. package/.claude/commands/analyze.md +19 -0
  2. package/.claude/commands/audit.md +26 -0
  3. package/.claude/commands/build.md +39 -0
  4. package/.claude/commands/commit.md +25 -0
  5. package/.claude/commands/fix.md +23 -0
  6. package/.claude/commands/plan.md +18 -0
  7. package/.claude/commands/suggest.md +23 -0
  8. package/.claude/commands/verify.md +18 -0
  9. package/.claude/hooks/post-bash-router.sh +20 -0
  10. package/.claude/hooks/post-commit.sh +140 -0
  11. package/.claude/hooks/pre-edit.sh +129 -0
  12. package/.claude/hooks/session-check.sh +79 -0
  13. package/.claude/settings.json +40 -6
  14. package/.claude/settings.local.json +3 -1
  15. package/.claude/skills/actix.md +337 -0
  16. package/.claude/skills/alembic.md +504 -0
  17. package/.claude/skills/angular.md +237 -0
  18. package/.claude/skills/api-design.md +187 -0
  19. package/.claude/skills/aspnet-core.md +377 -0
  20. package/.claude/skills/astro.md +245 -0
  21. package/.claude/skills/auth-clerk.md +327 -0
  22. package/.claude/skills/auth-firebase.md +367 -0
  23. package/.claude/skills/auth-nextauth.md +359 -0
  24. package/.claude/skills/auth-supabase.md +368 -0
  25. package/.claude/skills/axum.md +386 -0
  26. package/.claude/skills/blazor.md +456 -0
  27. package/.claude/skills/chi.md +348 -0
  28. package/.claude/skills/code-review.md +133 -0
  29. package/.claude/skills/csharp.md +296 -0
  30. package/.claude/skills/css-modules.md +325 -0
  31. package/.claude/skills/cypress.md +343 -0
  32. package/.claude/skills/debugging.md +133 -0
  33. package/.claude/skills/diesel.md +392 -0
  34. package/.claude/skills/django.md +301 -0
  35. package/.claude/skills/docker.md +319 -0
  36. package/.claude/skills/doctrine.md +473 -0
  37. package/.claude/skills/documentation.md +182 -0
  38. package/.claude/skills/dotnet.md +409 -0
  39. package/.claude/skills/drizzle.md +293 -0
  40. package/.claude/skills/echo.md +321 -0
  41. package/.claude/skills/eloquent.md +256 -0
  42. package/.claude/skills/emotion.md +426 -0
  43. package/.claude/skills/entity-framework.md +370 -0
  44. package/.claude/skills/express.md +316 -0
  45. package/.claude/skills/fastapi.md +329 -0
  46. package/.claude/skills/fastify.md +299 -0
  47. package/.claude/skills/fiber.md +315 -0
  48. package/.claude/skills/flask.md +322 -0
  49. package/.claude/skills/gin.md +342 -0
  50. package/.claude/skills/git.md +116 -0
  51. package/.claude/skills/github-actions.md +353 -0
  52. package/.claude/skills/go.md +377 -0
  53. package/.claude/skills/gorm.md +409 -0
  54. package/.claude/skills/graphql.md +478 -0
  55. package/.claude/skills/hibernate.md +379 -0
  56. package/.claude/skills/hono.md +306 -0
  57. package/.claude/skills/java.md +400 -0
  58. package/.claude/skills/jest.md +313 -0
  59. package/.claude/skills/jpa.md +282 -0
  60. package/.claude/skills/kotlin.md +347 -0
  61. package/.claude/skills/kubernetes.md +363 -0
  62. package/.claude/skills/laravel.md +414 -0
  63. package/.claude/skills/mcp-browser.md +320 -0
  64. package/.claude/skills/mcp-database.md +219 -0
  65. package/.claude/skills/mcp-fetch.md +241 -0
  66. package/.claude/skills/mcp-filesystem.md +204 -0
  67. package/.claude/skills/mcp-github.md +217 -0
  68. package/.claude/skills/mcp-memory.md +240 -0
  69. package/.claude/skills/mcp-search.md +218 -0
  70. package/.claude/skills/mcp-slack.md +262 -0
  71. package/.claude/skills/micronaut.md +388 -0
  72. package/.claude/skills/mongodb.md +319 -0
  73. package/.claude/skills/mongoose.md +355 -0
  74. package/.claude/skills/mysql.md +281 -0
  75. package/.claude/skills/nestjs.md +335 -0
  76. package/.claude/skills/nextjs-app-router.md +260 -0
  77. package/.claude/skills/nextjs-pages.md +172 -0
  78. package/.claude/skills/nuxt.md +202 -0
  79. package/.claude/skills/openapi.md +489 -0
  80. package/.claude/skills/performance.md +199 -0
  81. package/.claude/skills/php.md +398 -0
  82. package/.claude/skills/playwright.md +371 -0
  83. package/.claude/skills/postgresql.md +257 -0
  84. package/.claude/skills/prisma.md +293 -0
  85. package/.claude/skills/pydantic.md +304 -0
  86. package/.claude/skills/pytest.md +313 -0
  87. package/.claude/skills/python.md +272 -0
  88. package/.claude/skills/quarkus.md +377 -0
  89. package/.claude/skills/react.md +230 -0
  90. package/.claude/skills/redis.md +391 -0
  91. package/.claude/skills/refactoring.md +143 -0
  92. package/.claude/skills/remix.md +246 -0
  93. package/.claude/skills/rest-api.md +490 -0
  94. package/.claude/skills/rocket.md +366 -0
  95. package/.claude/skills/rust.md +341 -0
  96. package/.claude/skills/sass.md +380 -0
  97. package/.claude/skills/sea-orm.md +382 -0
  98. package/.claude/skills/security.md +167 -0
  99. package/.claude/skills/sequelize.md +395 -0
  100. package/.claude/skills/spring-boot.md +416 -0
  101. package/.claude/skills/sqlalchemy.md +269 -0
  102. package/.claude/skills/sqlx-rust.md +408 -0
  103. package/.claude/skills/state-jotai.md +346 -0
  104. package/.claude/skills/state-mobx.md +353 -0
  105. package/.claude/skills/state-pinia.md +431 -0
  106. package/.claude/skills/state-redux.md +337 -0
  107. package/.claude/skills/state-tanstack-query.md +434 -0
  108. package/.claude/skills/state-zustand.md +340 -0
  109. package/.claude/skills/styled-components.md +403 -0
  110. package/.claude/skills/svelte.md +238 -0
  111. package/.claude/skills/sveltekit.md +207 -0
  112. package/.claude/skills/symfony.md +437 -0
  113. package/.claude/skills/tailwind.md +279 -0
  114. package/.claude/skills/terraform.md +394 -0
  115. package/.claude/skills/testing-library.md +371 -0
  116. package/.claude/skills/trpc.md +426 -0
  117. package/.claude/skills/typeorm.md +368 -0
  118. package/.claude/skills/vitest.md +330 -0
  119. package/.claude/skills/vue.md +202 -0
  120. package/.claude/skills/warp.md +365 -0
  121. package/README.md +135 -52
  122. package/package.json +1 -1
  123. package/system/triggers.md +152 -11
@@ -0,0 +1,409 @@
1
+ # GORM Skill
2
+
3
+ ## Database Connection
4
+ \`\`\`go
5
+ package database
6
+
7
+ import (
8
+ "fmt"
9
+ "log"
10
+ "os"
11
+ "time"
12
+
13
+ "gorm.io/driver/postgres"
14
+ "gorm.io/gorm"
15
+ "gorm.io/gorm/logger"
16
+ )
17
+
18
+ func Connect() (*gorm.DB, error) {
19
+ dsn := os.Getenv("DATABASE_URL")
20
+
21
+ // Configure logger
22
+ gormLogger := logger.New(
23
+ log.New(os.Stdout, "\\r\\n", log.LstdFlags),
24
+ logger.Config{
25
+ SlowThreshold: 200 * time.Millisecond,
26
+ LogLevel: logger.Warn,
27
+ IgnoreRecordNotFoundError: true,
28
+ Colorful: true,
29
+ },
30
+ )
31
+
32
+ db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
33
+ Logger: gormLogger,
34
+ SkipDefaultTransaction: true, // Disable for performance
35
+ PrepareStmt: true, // Cache prepared statements
36
+ })
37
+ if err != nil {
38
+ return nil, fmt.Errorf("connect to database: %w", err)
39
+ }
40
+
41
+ // Configure connection pool
42
+ sqlDB, err := db.DB()
43
+ if err != nil {
44
+ return nil, err
45
+ }
46
+ sqlDB.SetMaxIdleConns(10)
47
+ sqlDB.SetMaxOpenConns(100)
48
+ sqlDB.SetConnMaxLifetime(time.Hour)
49
+
50
+ return db, nil
51
+ }
52
+ \`\`\`
53
+
54
+ ## Model Definition with Associations
55
+ \`\`\`go
56
+ package model
57
+
58
+ import (
59
+ "time"
60
+
61
+ "gorm.io/gorm"
62
+ )
63
+
64
+ // Base model (alternative to gorm.Model with UUID)
65
+ type Base struct {
66
+ ID string \`gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"\`
67
+ CreatedAt time.Time \`json:"created_at"\`
68
+ UpdatedAt time.Time \`json:"updated_at"\`
69
+ DeletedAt gorm.DeletedAt \`gorm:"index" json:"-"\`
70
+ }
71
+
72
+ type User struct {
73
+ Base
74
+ Email string \`gorm:"uniqueIndex;size:255;not null" json:"email"\`
75
+ Name string \`gorm:"size:100;not null" json:"name"\`
76
+ Password string \`gorm:"size:255;not null" json:"-"\`
77
+ IsActive bool \`gorm:"default:true" json:"is_active"\`
78
+
79
+ // Associations
80
+ Profile *Profile \`gorm:"constraint:OnDelete:CASCADE" json:"profile,omitempty"\`
81
+ Posts []Post \`gorm:"constraint:OnDelete:CASCADE" json:"posts,omitempty"\`
82
+ Roles []Role \`gorm:"many2many:user_roles" json:"roles,omitempty"\`
83
+ }
84
+
85
+ type Profile struct {
86
+ ID string \`gorm:"type:uuid;primaryKey;default:gen_random_uuid()"\`
87
+ UserID string \`gorm:"type:uuid;uniqueIndex"\`
88
+ Bio string \`gorm:"type:text"\`
89
+ AvatarURL string \`gorm:"size:500"\`
90
+ }
91
+
92
+ type Post struct {
93
+ Base
94
+ Title string \`gorm:"size:255;not null"\`
95
+ Content string \`gorm:"type:text"\`
96
+ Published bool \`gorm:"default:false"\`
97
+ AuthorID string \`gorm:"type:uuid;index"\`
98
+
99
+ Author *User \`gorm:"foreignKey:AuthorID" json:"author,omitempty"\`
100
+ Tags []Tag \`gorm:"many2many:post_tags" json:"tags,omitempty"\`
101
+ Comments []Comment \`gorm:"constraint:OnDelete:CASCADE" json:"comments,omitempty"\`
102
+ }
103
+
104
+ type Tag struct {
105
+ ID uint \`gorm:"primaryKey"\`
106
+ Name string \`gorm:"uniqueIndex;size:50"\`
107
+ Posts []Post \`gorm:"many2many:post_tags"\`
108
+ }
109
+
110
+ type Comment struct {
111
+ Base
112
+ PostID string \`gorm:"type:uuid;index"\`
113
+ UserID string \`gorm:"type:uuid;index"\`
114
+ Content string \`gorm:"type:text;not null"\`
115
+
116
+ User *User \`gorm:"foreignKey:UserID"\`
117
+ }
118
+
119
+ type Role struct {
120
+ ID uint \`gorm:"primaryKey"\`
121
+ Name string \`gorm:"uniqueIndex;size:50"\`
122
+ Users []User \`gorm:"many2many:user_roles"\`
123
+ }
124
+ \`\`\`
125
+
126
+ ## Hooks (Callbacks)
127
+ \`\`\`go
128
+ import (
129
+ "golang.org/x/crypto/bcrypt"
130
+ "gorm.io/gorm"
131
+ )
132
+
133
+ // BeforeCreate hook - hash password
134
+ func (u *User) BeforeCreate(tx *gorm.DB) error {
135
+ if u.Password != "" {
136
+ hashed, err := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.DefaultCost)
137
+ if err != nil {
138
+ return err
139
+ }
140
+ u.Password = string(hashed)
141
+ }
142
+ return nil
143
+ }
144
+
145
+ // AfterCreate hook - send welcome email
146
+ func (u *User) AfterCreate(tx *gorm.DB) error {
147
+ // Queue welcome email (don't block)
148
+ go sendWelcomeEmail(u.Email)
149
+ return nil
150
+ }
151
+
152
+ // BeforeUpdate hook - validate changes
153
+ func (u *User) BeforeUpdate(tx *gorm.DB) error {
154
+ // Hash password if changed
155
+ if tx.Statement.Changed("Password") {
156
+ hashed, err := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.DefaultCost)
157
+ if err != nil {
158
+ return err
159
+ }
160
+ tx.Statement.SetColumn("Password", string(hashed))
161
+ }
162
+ return nil
163
+ }
164
+
165
+ // BeforeDelete hook - cleanup related data
166
+ func (u *User) BeforeDelete(tx *gorm.DB) error {
167
+ // Archive user data before soft delete
168
+ return tx.Model(&Post{}).Where("author_id = ?", u.ID).Update("author_id", nil).Error
169
+ }
170
+ \`\`\`
171
+
172
+ ## CRUD Operations
173
+ \`\`\`go
174
+ package repository
175
+
176
+ import (
177
+ "context"
178
+ "errors"
179
+
180
+ "gorm.io/gorm"
181
+ )
182
+
183
+ var ErrNotFound = errors.New("record not found")
184
+
185
+ type UserRepository struct {
186
+ db *gorm.DB
187
+ }
188
+
189
+ func NewUserRepository(db *gorm.DB) *UserRepository {
190
+ return &UserRepository{db: db}
191
+ }
192
+
193
+ // Create
194
+ func (r *UserRepository) Create(ctx context.Context, user *model.User) error {
195
+ return r.db.WithContext(ctx).Create(user).Error
196
+ }
197
+
198
+ // Find by ID with eager loading
199
+ func (r *UserRepository) GetByID(ctx context.Context, id string) (*model.User, error) {
200
+ var user model.User
201
+ err := r.db.WithContext(ctx).
202
+ Preload("Profile").
203
+ Preload("Roles").
204
+ First(&user, "id = ?", id).Error
205
+
206
+ if errors.Is(err, gorm.ErrRecordNotFound) {
207
+ return nil, ErrNotFound
208
+ }
209
+ return &user, err
210
+ }
211
+
212
+ // Find by email
213
+ func (r *UserRepository) GetByEmail(ctx context.Context, email string) (*model.User, error) {
214
+ var user model.User
215
+ err := r.db.WithContext(ctx).Where("email = ?", email).First(&user).Error
216
+ if errors.Is(err, gorm.ErrRecordNotFound) {
217
+ return nil, ErrNotFound
218
+ }
219
+ return &user, err
220
+ }
221
+
222
+ // Update specific fields
223
+ func (r *UserRepository) Update(ctx context.Context, user *model.User) error {
224
+ return r.db.WithContext(ctx).
225
+ Model(user).
226
+ Select("Name", "Email", "IsActive"). // Only update these fields
227
+ Updates(user).Error
228
+ }
229
+
230
+ // Delete (soft delete with gorm.DeletedAt)
231
+ func (r *UserRepository) Delete(ctx context.Context, id string) error {
232
+ result := r.db.WithContext(ctx).Delete(&model.User{}, "id = ?", id)
233
+ if result.RowsAffected == 0 {
234
+ return ErrNotFound
235
+ }
236
+ return result.Error
237
+ }
238
+
239
+ // Hard delete (permanent)
240
+ func (r *UserRepository) HardDelete(ctx context.Context, id string) error {
241
+ return r.db.WithContext(ctx).Unscoped().Delete(&model.User{}, "id = ?", id).Error
242
+ }
243
+ \`\`\`
244
+
245
+ ## Transactions
246
+ \`\`\`go
247
+ // Manual transaction
248
+ func (r *UserRepository) CreateWithProfile(ctx context.Context, user *model.User, profile *model.Profile) error {
249
+ return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
250
+ if err := tx.Create(user).Error; err != nil {
251
+ return err // Rollback
252
+ }
253
+
254
+ profile.UserID = user.ID
255
+ if err := tx.Create(profile).Error; err != nil {
256
+ return err // Rollback
257
+ }
258
+
259
+ return nil // Commit
260
+ })
261
+ }
262
+
263
+ // Nested transaction with savepoints
264
+ func (r *UserRepository) ComplexOperation(ctx context.Context) error {
265
+ return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
266
+ // First operation
267
+ if err := tx.Create(&user1).Error; err != nil {
268
+ return err
269
+ }
270
+
271
+ // Nested transaction (savepoint)
272
+ err := tx.Transaction(func(tx2 *gorm.DB) error {
273
+ if err := tx2.Create(&user2).Error; err != nil {
274
+ return err // Rollback to savepoint
275
+ }
276
+ return nil
277
+ })
278
+ if err != nil {
279
+ // Savepoint rolled back, but outer transaction continues
280
+ log.Printf("nested transaction failed: %v", err)
281
+ }
282
+
283
+ return nil // Commit outer transaction
284
+ })
285
+ }
286
+ \`\`\`
287
+
288
+ ## Scopes (Reusable Query Logic)
289
+ \`\`\`go
290
+ // Scope for active users only
291
+ func ActiveUsers(db *gorm.DB) *gorm.DB {
292
+ return db.Where("is_active = ?", true)
293
+ }
294
+
295
+ // Scope for pagination
296
+ func Paginate(page, perPage int) func(db *gorm.DB) *gorm.DB {
297
+ return func(db *gorm.DB) *gorm.DB {
298
+ if page <= 0 {
299
+ page = 1
300
+ }
301
+ if perPage <= 0 || perPage > 100 {
302
+ perPage = 20
303
+ }
304
+ offset := (page - 1) * perPage
305
+ return db.Offset(offset).Limit(perPage)
306
+ }
307
+ }
308
+
309
+ // Scope for date range
310
+ func CreatedBetween(start, end time.Time) func(db *gorm.DB) *gorm.DB {
311
+ return func(db *gorm.DB) *gorm.DB {
312
+ return db.Where("created_at BETWEEN ? AND ?", start, end)
313
+ }
314
+ }
315
+
316
+ // Using scopes
317
+ func (r *UserRepository) ListActive(ctx context.Context, page, perPage int) ([]model.User, int64, error) {
318
+ var users []model.User
319
+ var total int64
320
+
321
+ db := r.db.WithContext(ctx).Model(&model.User{}).Scopes(ActiveUsers)
322
+
323
+ // Get total count
324
+ if err := db.Count(&total).Error; err != nil {
325
+ return nil, 0, err
326
+ }
327
+
328
+ // Get paginated results
329
+ err := db.Scopes(Paginate(page, perPage)).
330
+ Order("created_at DESC").
331
+ Find(&users).Error
332
+
333
+ return users, total, err
334
+ }
335
+ \`\`\`
336
+
337
+ ## Complex Queries
338
+ \`\`\`go
339
+ // Join with subquery
340
+ func (r *PostRepository) GetPopularPosts(ctx context.Context, minComments int) ([]model.Post, error) {
341
+ var posts []model.Post
342
+
343
+ subquery := r.db.Model(&model.Comment{}).
344
+ Select("post_id, COUNT(*) as comment_count").
345
+ Group("post_id").
346
+ Having("COUNT(*) >= ?", minComments)
347
+
348
+ err := r.db.WithContext(ctx).
349
+ Joins("JOIN (?) AS pc ON posts.id = pc.post_id", subquery).
350
+ Preload("Author").
351
+ Preload("Tags").
352
+ Order("pc.comment_count DESC").
353
+ Find(&posts).Error
354
+
355
+ return posts, err
356
+ }
357
+
358
+ // Raw SQL when needed
359
+ func (r *UserRepository) GetUserStats(ctx context.Context, userID string) (*UserStats, error) {
360
+ var stats UserStats
361
+ err := r.db.WithContext(ctx).Raw(\`
362
+ SELECT
363
+ u.id,
364
+ u.name,
365
+ COUNT(DISTINCT p.id) as post_count,
366
+ COUNT(DISTINCT c.id) as comment_count
367
+ FROM users u
368
+ LEFT JOIN posts p ON p.author_id = u.id
369
+ LEFT JOIN comments c ON c.user_id = u.id
370
+ WHERE u.id = ?
371
+ GROUP BY u.id, u.name
372
+ \`, userID).Scan(&stats).Error
373
+
374
+ return &stats, err
375
+ }
376
+ \`\`\`
377
+
378
+ ## Migrations
379
+ \`\`\`go
380
+ func AutoMigrate(db *gorm.DB) error {
381
+ return db.AutoMigrate(
382
+ &model.User{},
383
+ &model.Profile{},
384
+ &model.Post{},
385
+ &model.Tag{},
386
+ &model.Comment{},
387
+ &model.Role{},
388
+ )
389
+ }
390
+
391
+ // For production, use a migration tool like golang-migrate
392
+ \`\`\`
393
+
394
+ ## ✅ DO
395
+ - Use \`WithContext(ctx)\` for all database operations
396
+ - Use \`Preload\` for eager loading associations
397
+ - Use scopes for reusable query logic
398
+ - Use transactions for multi-step operations
399
+ - Use \`Select\` to limit fields updated
400
+ - Handle \`gorm.ErrRecordNotFound\` explicitly
401
+ - Use prepared statements (\`PrepareStmt: true\`)
402
+
403
+ ## ❌ DON'T
404
+ - Don't use \`Save()\` for partial updates (updates all fields)
405
+ - Don't ignore hook errors
406
+ - Don't use \`Find\` for single records (use \`First\` or \`Take\`)
407
+ - Don't forget \`Unscoped()\` when querying soft-deleted records
408
+ - Don't use \`AutoMigrate\` in production without review
409
+ - Don't use raw SQL without parameterized queries