autoworkflow 3.1.4 → 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 +174 -11
  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,408 @@
1
+ # SQLx Skill
2
+
3
+ ## Setup and Configuration
4
+ \`\`\`bash
5
+ # Add to Cargo.toml
6
+ [dependencies]
7
+ sqlx = { version = "0.7", features = ["runtime-tokio", "postgres", "uuid", "chrono", "migrate"] }
8
+
9
+ # Install sqlx-cli
10
+ cargo install sqlx-cli --no-default-features --features postgres
11
+
12
+ # Create database
13
+ sqlx database create
14
+
15
+ # Create migration
16
+ sqlx migrate add create_users
17
+
18
+ # Run migrations
19
+ sqlx migrate run
20
+
21
+ # Prepare for offline compile-time checking
22
+ cargo sqlx prepare
23
+ \`\`\`
24
+
25
+ ## Connection Pool
26
+ \`\`\`rust
27
+ use sqlx::postgres::{PgPool, PgPoolOptions};
28
+ use std::time::Duration;
29
+
30
+ pub async fn create_pool() -> Result<PgPool, sqlx::Error> {
31
+ let database_url = std::env::var("DATABASE_URL")
32
+ .expect("DATABASE_URL must be set");
33
+
34
+ PgPoolOptions::new()
35
+ .max_connections(100)
36
+ .min_connections(5)
37
+ .acquire_timeout(Duration::from_secs(10))
38
+ .idle_timeout(Duration::from_secs(300))
39
+ .connect(&database_url)
40
+ .await
41
+ }
42
+
43
+ // Run migrations at startup
44
+ pub async fn run_migrations(pool: &PgPool) -> Result<(), sqlx::migrate::MigrateError> {
45
+ sqlx::migrate!("./migrations")
46
+ .run(pool)
47
+ .await
48
+ }
49
+ \`\`\`
50
+
51
+ ## Model Definitions
52
+ \`\`\`rust
53
+ use chrono::{DateTime, Utc};
54
+ use serde::{Deserialize, Serialize};
55
+ use sqlx::FromRow;
56
+ use uuid::Uuid;
57
+
58
+ #[derive(Debug, Clone, FromRow, Serialize)]
59
+ pub struct User {
60
+ pub id: Uuid,
61
+ pub email: String,
62
+ pub name: String,
63
+ #[serde(skip_serializing)]
64
+ pub password_hash: String,
65
+ pub is_active: bool,
66
+ pub created_at: DateTime<Utc>,
67
+ pub updated_at: DateTime<Utc>,
68
+ }
69
+
70
+ #[derive(Debug, Clone, FromRow, Serialize)]
71
+ pub struct Post {
72
+ pub id: Uuid,
73
+ pub title: String,
74
+ pub content: String,
75
+ pub published: bool,
76
+ pub author_id: Uuid,
77
+ pub created_at: DateTime<Utc>,
78
+ pub updated_at: DateTime<Utc>,
79
+ }
80
+
81
+ // For partial selects
82
+ #[derive(Debug, FromRow, Serialize)]
83
+ pub struct UserSummary {
84
+ pub id: Uuid,
85
+ pub email: String,
86
+ pub name: String,
87
+ }
88
+ \`\`\`
89
+
90
+ ## Compile-Time Checked Queries (query_as!)
91
+ \`\`\`rust
92
+ use sqlx::PgPool;
93
+
94
+ pub struct UserRepository {
95
+ pool: PgPool,
96
+ }
97
+
98
+ impl UserRepository {
99
+ pub fn new(pool: PgPool) -> Self {
100
+ Self { pool }
101
+ }
102
+
103
+ // Create - compile-time checked
104
+ pub async fn create(
105
+ &self,
106
+ email: &str,
107
+ name: &str,
108
+ password: &str,
109
+ ) -> Result<User, AppError> {
110
+ let id = Uuid::new_v4();
111
+ let password_hash = hash_password(password)?;
112
+ let now = Utc::now();
113
+
114
+ sqlx::query_as!(
115
+ User,
116
+ r#"
117
+ INSERT INTO users (id, email, name, password_hash, is_active, created_at, updated_at)
118
+ VALUES ($1, $2, $3, $4, true, $5, $5)
119
+ RETURNING id, email, name, password_hash, is_active, created_at, updated_at
120
+ "#,
121
+ id,
122
+ email,
123
+ name,
124
+ password_hash,
125
+ now
126
+ )
127
+ .fetch_one(&self.pool)
128
+ .await
129
+ .map_err(AppError::from)
130
+ }
131
+
132
+ // Find by ID
133
+ pub async fn find_by_id(&self, id: Uuid) -> Result<Option<User>, AppError> {
134
+ sqlx::query_as!(
135
+ User,
136
+ "SELECT id, email, name, password_hash, is_active, created_at, updated_at FROM users WHERE id = $1",
137
+ id
138
+ )
139
+ .fetch_optional(&self.pool)
140
+ .await
141
+ .map_err(AppError::from)
142
+ }
143
+
144
+ // List with pagination
145
+ pub async fn list(&self, page: i64, per_page: i64) -> Result<Vec<User>, AppError> {
146
+ let offset = (page - 1) * per_page;
147
+
148
+ sqlx::query_as!(
149
+ User,
150
+ r#"
151
+ SELECT id, email, name, password_hash, is_active, created_at, updated_at
152
+ FROM users
153
+ WHERE is_active = true
154
+ ORDER BY created_at DESC
155
+ LIMIT $1 OFFSET $2
156
+ "#,
157
+ per_page,
158
+ offset
159
+ )
160
+ .fetch_all(&self.pool)
161
+ .await
162
+ .map_err(AppError::from)
163
+ }
164
+
165
+ // Count
166
+ pub async fn count_active(&self) -> Result<i64, AppError> {
167
+ let result = sqlx::query_scalar!(
168
+ "SELECT COUNT(*) FROM users WHERE is_active = true"
169
+ )
170
+ .fetch_one(&self.pool)
171
+ .await?;
172
+
173
+ Ok(result.unwrap_or(0))
174
+ }
175
+
176
+ // Update
177
+ pub async fn update(
178
+ &self,
179
+ id: Uuid,
180
+ name: Option<&str>,
181
+ email: Option<&str>,
182
+ ) -> Result<User, AppError> {
183
+ sqlx::query_as!(
184
+ User,
185
+ r#"
186
+ UPDATE users
187
+ SET
188
+ name = COALESCE($2, name),
189
+ email = COALESCE($3, email),
190
+ updated_at = NOW()
191
+ WHERE id = $1
192
+ RETURNING id, email, name, password_hash, is_active, created_at, updated_at
193
+ "#,
194
+ id,
195
+ name,
196
+ email
197
+ )
198
+ .fetch_one(&self.pool)
199
+ .await
200
+ .map_err(AppError::from)
201
+ }
202
+
203
+ // Delete
204
+ pub async fn delete(&self, id: Uuid) -> Result<bool, AppError> {
205
+ let result = sqlx::query!("DELETE FROM users WHERE id = $1", id)
206
+ .execute(&self.pool)
207
+ .await?;
208
+
209
+ Ok(result.rows_affected() > 0)
210
+ }
211
+ }
212
+ \`\`\`
213
+
214
+ ## Dynamic Queries (query_as with QueryBuilder)
215
+ \`\`\`rust
216
+ use sqlx::QueryBuilder;
217
+
218
+ pub async fn search_users(
219
+ &self,
220
+ query: Option<&str>,
221
+ is_active: Option<bool>,
222
+ page: i64,
223
+ per_page: i64,
224
+ ) -> Result<Vec<User>, AppError> {
225
+ let mut builder = QueryBuilder::new(
226
+ "SELECT id, email, name, password_hash, is_active, created_at, updated_at FROM users WHERE 1=1"
227
+ );
228
+
229
+ if let Some(q) = query {
230
+ builder.push(" AND (name ILIKE ");
231
+ builder.push_bind(format!("%{}%", q));
232
+ builder.push(" OR email ILIKE ");
233
+ builder.push_bind(format!("%{}%", q));
234
+ builder.push(")");
235
+ }
236
+
237
+ if let Some(active) = is_active {
238
+ builder.push(" AND is_active = ");
239
+ builder.push_bind(active);
240
+ }
241
+
242
+ builder.push(" ORDER BY created_at DESC");
243
+ builder.push(" LIMIT ");
244
+ builder.push_bind(per_page);
245
+ builder.push(" OFFSET ");
246
+ builder.push_bind((page - 1) * per_page);
247
+
248
+ builder
249
+ .build_query_as::<User>()
250
+ .fetch_all(&self.pool)
251
+ .await
252
+ .map_err(AppError::from)
253
+ }
254
+
255
+ // Bulk insert
256
+ pub async fn bulk_create(&self, users: Vec<NewUser>) -> Result<Vec<User>, AppError> {
257
+ let mut builder = QueryBuilder::new(
258
+ "INSERT INTO users (id, email, name, password_hash, is_active, created_at, updated_at) "
259
+ );
260
+
261
+ builder.push_values(users, |mut b, user| {
262
+ b.push_bind(Uuid::new_v4())
263
+ .push_bind(&user.email)
264
+ .push_bind(&user.name)
265
+ .push_bind(&user.password_hash)
266
+ .push_bind(true)
267
+ .push_bind(Utc::now())
268
+ .push_bind(Utc::now());
269
+ });
270
+
271
+ builder.push(" RETURNING id, email, name, password_hash, is_active, created_at, updated_at");
272
+
273
+ builder
274
+ .build_query_as::<User>()
275
+ .fetch_all(&self.pool)
276
+ .await
277
+ .map_err(AppError::from)
278
+ }
279
+ \`\`\`
280
+
281
+ ## Transactions
282
+ \`\`\`rust
283
+ use sqlx::Acquire;
284
+
285
+ pub async fn create_user_with_profile(
286
+ &self,
287
+ email: &str,
288
+ name: &str,
289
+ password: &str,
290
+ bio: &str,
291
+ ) -> Result<(User, Profile), AppError> {
292
+ let mut tx = self.pool.begin().await?;
293
+
294
+ let user = sqlx::query_as!(
295
+ User,
296
+ r#"
297
+ INSERT INTO users (id, email, name, password_hash, is_active, created_at, updated_at)
298
+ VALUES ($1, $2, $3, $4, true, NOW(), NOW())
299
+ RETURNING id, email, name, password_hash, is_active, created_at, updated_at
300
+ "#,
301
+ Uuid::new_v4(),
302
+ email,
303
+ name,
304
+ hash_password(password)?
305
+ )
306
+ .fetch_one(&mut *tx)
307
+ .await?;
308
+
309
+ let profile = sqlx::query_as!(
310
+ Profile,
311
+ r#"
312
+ INSERT INTO profiles (id, user_id, bio)
313
+ VALUES ($1, $2, $3)
314
+ RETURNING id, user_id, bio
315
+ "#,
316
+ Uuid::new_v4(),
317
+ user.id,
318
+ bio
319
+ )
320
+ .fetch_one(&mut *tx)
321
+ .await?;
322
+
323
+ tx.commit().await?;
324
+
325
+ Ok((user, profile))
326
+ }
327
+
328
+ // Transaction with savepoint
329
+ pub async fn complex_operation(&self) -> Result<(), AppError> {
330
+ let mut tx = self.pool.begin().await?;
331
+
332
+ // First operation
333
+ create_user(&mut *tx, user1).await?;
334
+
335
+ // Nested transaction (savepoint)
336
+ let savepoint = tx.begin().await?;
337
+ match create_user(&mut *savepoint, user2).await {
338
+ Ok(_) => savepoint.commit().await?,
339
+ Err(e) => {
340
+ // Savepoint rolled back, outer transaction continues
341
+ tracing::warn!("Savepoint failed: {:?}", e);
342
+ }
343
+ }
344
+
345
+ tx.commit().await?;
346
+ Ok(())
347
+ }
348
+ \`\`\`
349
+
350
+ ## Streaming Results
351
+ \`\`\`rust
352
+ use futures::TryStreamExt;
353
+
354
+ pub async fn process_all_users(&self) -> Result<(), AppError> {
355
+ let mut stream = sqlx::query_as!(
356
+ User,
357
+ "SELECT id, email, name, password_hash, is_active, created_at, updated_at FROM users"
358
+ )
359
+ .fetch(&self.pool);
360
+
361
+ while let Some(user) = stream.try_next().await? {
362
+ // Process each user without loading all into memory
363
+ process_user(&user).await?;
364
+ }
365
+
366
+ Ok(())
367
+ }
368
+
369
+ // With batching
370
+ pub async fn export_users(&self) -> Result<(), AppError> {
371
+ let mut stream = sqlx::query_as!(User, "SELECT * FROM users")
372
+ .fetch(&self.pool);
373
+
374
+ let mut batch = Vec::with_capacity(100);
375
+
376
+ while let Some(user) = stream.try_next().await? {
377
+ batch.push(user);
378
+
379
+ if batch.len() >= 100 {
380
+ export_batch(&batch).await?;
381
+ batch.clear();
382
+ }
383
+ }
384
+
385
+ // Process remaining
386
+ if !batch.is_empty() {
387
+ export_batch(&batch).await?;
388
+ }
389
+
390
+ Ok(())
391
+ }
392
+ \`\`\`
393
+
394
+ ## ✅ DO
395
+ - Use \`query_as!\` for compile-time checked queries
396
+ - Use \`query_scalar!\` for single values
397
+ - Use \`fetch_optional\` for queries that may return no results
398
+ - Use transactions for multi-step operations
399
+ - Use \`QueryBuilder\` for dynamic queries
400
+ - Run \`cargo sqlx prepare\` before building for offline mode
401
+ - Use streaming for large result sets
402
+
403
+ ## ❌ DON'T
404
+ - Don't use \`fetch_one\` unless you're certain a row exists
405
+ - Don't forget to handle \`sqlx::Error\` properly
406
+ - Don't build SQL strings manually - use \`QueryBuilder\`
407
+ - Don't hold transactions longer than necessary
408
+ - Don't forget \`&mut *tx\` when passing transaction to queries