autoworkflow 3.1.5 → 3.6.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 (124) 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/post-edit.sh +190 -17
  12. package/.claude/hooks/pre-edit.sh +221 -0
  13. package/.claude/hooks/session-check.sh +90 -0
  14. package/.claude/settings.json +56 -6
  15. package/.claude/settings.local.json +5 -1
  16. package/.claude/skills/actix.md +337 -0
  17. package/.claude/skills/alembic.md +504 -0
  18. package/.claude/skills/angular.md +237 -0
  19. package/.claude/skills/api-design.md +187 -0
  20. package/.claude/skills/aspnet-core.md +377 -0
  21. package/.claude/skills/astro.md +245 -0
  22. package/.claude/skills/auth-clerk.md +327 -0
  23. package/.claude/skills/auth-firebase.md +367 -0
  24. package/.claude/skills/auth-nextauth.md +359 -0
  25. package/.claude/skills/auth-supabase.md +368 -0
  26. package/.claude/skills/axum.md +386 -0
  27. package/.claude/skills/blazor.md +456 -0
  28. package/.claude/skills/chi.md +348 -0
  29. package/.claude/skills/code-review.md +133 -0
  30. package/.claude/skills/csharp.md +296 -0
  31. package/.claude/skills/css-modules.md +325 -0
  32. package/.claude/skills/cypress.md +343 -0
  33. package/.claude/skills/debugging.md +133 -0
  34. package/.claude/skills/diesel.md +392 -0
  35. package/.claude/skills/django.md +301 -0
  36. package/.claude/skills/docker.md +319 -0
  37. package/.claude/skills/doctrine.md +473 -0
  38. package/.claude/skills/documentation.md +182 -0
  39. package/.claude/skills/dotnet.md +409 -0
  40. package/.claude/skills/drizzle.md +293 -0
  41. package/.claude/skills/echo.md +321 -0
  42. package/.claude/skills/eloquent.md +256 -0
  43. package/.claude/skills/emotion.md +426 -0
  44. package/.claude/skills/entity-framework.md +370 -0
  45. package/.claude/skills/express.md +316 -0
  46. package/.claude/skills/fastapi.md +329 -0
  47. package/.claude/skills/fastify.md +299 -0
  48. package/.claude/skills/fiber.md +315 -0
  49. package/.claude/skills/flask.md +322 -0
  50. package/.claude/skills/gin.md +342 -0
  51. package/.claude/skills/git.md +116 -0
  52. package/.claude/skills/github-actions.md +353 -0
  53. package/.claude/skills/go.md +377 -0
  54. package/.claude/skills/gorm.md +409 -0
  55. package/.claude/skills/graphql.md +478 -0
  56. package/.claude/skills/hibernate.md +379 -0
  57. package/.claude/skills/hono.md +306 -0
  58. package/.claude/skills/java.md +400 -0
  59. package/.claude/skills/jest.md +313 -0
  60. package/.claude/skills/jpa.md +282 -0
  61. package/.claude/skills/kotlin.md +347 -0
  62. package/.claude/skills/kubernetes.md +363 -0
  63. package/.claude/skills/laravel.md +414 -0
  64. package/.claude/skills/mcp-browser.md +320 -0
  65. package/.claude/skills/mcp-database.md +219 -0
  66. package/.claude/skills/mcp-fetch.md +241 -0
  67. package/.claude/skills/mcp-filesystem.md +204 -0
  68. package/.claude/skills/mcp-github.md +217 -0
  69. package/.claude/skills/mcp-memory.md +240 -0
  70. package/.claude/skills/mcp-search.md +218 -0
  71. package/.claude/skills/mcp-slack.md +262 -0
  72. package/.claude/skills/micronaut.md +388 -0
  73. package/.claude/skills/mongodb.md +319 -0
  74. package/.claude/skills/mongoose.md +355 -0
  75. package/.claude/skills/mysql.md +281 -0
  76. package/.claude/skills/nestjs.md +335 -0
  77. package/.claude/skills/nextjs-app-router.md +260 -0
  78. package/.claude/skills/nextjs-pages.md +172 -0
  79. package/.claude/skills/nuxt.md +202 -0
  80. package/.claude/skills/openapi.md +489 -0
  81. package/.claude/skills/performance.md +199 -0
  82. package/.claude/skills/php.md +398 -0
  83. package/.claude/skills/playwright.md +371 -0
  84. package/.claude/skills/postgresql.md +257 -0
  85. package/.claude/skills/prisma.md +293 -0
  86. package/.claude/skills/pydantic.md +304 -0
  87. package/.claude/skills/pytest.md +313 -0
  88. package/.claude/skills/python.md +272 -0
  89. package/.claude/skills/quarkus.md +377 -0
  90. package/.claude/skills/react.md +230 -0
  91. package/.claude/skills/redis.md +391 -0
  92. package/.claude/skills/refactoring.md +143 -0
  93. package/.claude/skills/remix.md +246 -0
  94. package/.claude/skills/rest-api.md +490 -0
  95. package/.claude/skills/rocket.md +366 -0
  96. package/.claude/skills/rust.md +341 -0
  97. package/.claude/skills/sass.md +380 -0
  98. package/.claude/skills/sea-orm.md +382 -0
  99. package/.claude/skills/security.md +167 -0
  100. package/.claude/skills/sequelize.md +395 -0
  101. package/.claude/skills/spring-boot.md +416 -0
  102. package/.claude/skills/sqlalchemy.md +269 -0
  103. package/.claude/skills/sqlx-rust.md +408 -0
  104. package/.claude/skills/state-jotai.md +346 -0
  105. package/.claude/skills/state-mobx.md +353 -0
  106. package/.claude/skills/state-pinia.md +431 -0
  107. package/.claude/skills/state-redux.md +337 -0
  108. package/.claude/skills/state-tanstack-query.md +434 -0
  109. package/.claude/skills/state-zustand.md +340 -0
  110. package/.claude/skills/styled-components.md +403 -0
  111. package/.claude/skills/svelte.md +238 -0
  112. package/.claude/skills/sveltekit.md +207 -0
  113. package/.claude/skills/symfony.md +437 -0
  114. package/.claude/skills/tailwind.md +279 -0
  115. package/.claude/skills/terraform.md +394 -0
  116. package/.claude/skills/testing-library.md +371 -0
  117. package/.claude/skills/trpc.md +426 -0
  118. package/.claude/skills/typeorm.md +368 -0
  119. package/.claude/skills/vitest.md +330 -0
  120. package/.claude/skills/vue.md +202 -0
  121. package/.claude/skills/warp.md +365 -0
  122. package/README.md +163 -52
  123. package/package.json +1 -1
  124. package/system/triggers.md +256 -17
@@ -0,0 +1,386 @@
1
+ # Axum Skill
2
+
3
+ ## Application Setup
4
+ \`\`\`rust
5
+ use axum::{
6
+ Router,
7
+ routing::{get, post, put, delete},
8
+ middleware,
9
+ };
10
+ use tower_http::{cors::CorsLayer, trace::TraceLayer, compression::CompressionLayer};
11
+ use std::net::SocketAddr;
12
+
13
+ #[tokio::main]
14
+ async fn main() {
15
+ // Initialize tracing
16
+ tracing_subscriber::init();
17
+
18
+ // Create shared state
19
+ let db_pool = create_pool().await.expect("Failed to create pool");
20
+ let state = AppState { db: db_pool };
21
+
22
+ // Build router
23
+ let app = Router::new()
24
+ .nest("/api/v1", api_routes())
25
+ .layer(TraceLayer::new_for_http())
26
+ .layer(CompressionLayer::new())
27
+ .layer(CorsLayer::permissive())
28
+ .with_state(state);
29
+
30
+ let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
31
+ tracing::info!("Listening on {}", addr);
32
+
33
+ let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
34
+ axum::serve(listener, app).await.unwrap();
35
+ }
36
+
37
+ fn api_routes() -> Router<AppState> {
38
+ Router::new()
39
+ .nest("/auth", auth_routes())
40
+ .nest("/users", user_routes())
41
+ .nest("/posts", post_routes())
42
+ }
43
+
44
+ fn auth_routes() -> Router<AppState> {
45
+ Router::new()
46
+ .route("/login", post(login))
47
+ .route("/register", post(register))
48
+ }
49
+
50
+ fn user_routes() -> Router<AppState> {
51
+ Router::new()
52
+ .route("/", get(list_users).post(create_user))
53
+ .route("/:id", get(get_user).put(update_user).delete(delete_user))
54
+ .layer(middleware::from_fn(auth_middleware))
55
+ }
56
+ \`\`\`
57
+
58
+ ## Shared State
59
+ \`\`\`rust
60
+ use axum::extract::State;
61
+ use sqlx::PgPool;
62
+
63
+ #[derive(Clone)]
64
+ pub struct AppState {
65
+ pub db: PgPool,
66
+ }
67
+
68
+ // Access in handlers
69
+ async fn get_user(
70
+ State(state): State<AppState>,
71
+ Path(id): Path<String>,
72
+ ) -> Result<Json<User>, AppError> {
73
+ let user = sqlx::query_as!(User, "SELECT * FROM users WHERE id = $1", id)
74
+ .fetch_optional(&state.db)
75
+ .await?
76
+ .ok_or(AppError::NotFound)?;
77
+
78
+ Ok(Json(user))
79
+ }
80
+ \`\`\`
81
+
82
+ ## Extractors
83
+ \`\`\`rust
84
+ use axum::{
85
+ extract::{Path, Query, State, Json},
86
+ http::HeaderMap,
87
+ };
88
+ use serde::Deserialize;
89
+
90
+ // JSON body with validation
91
+ #[derive(Deserialize)]
92
+ pub struct CreateUserRequest {
93
+ pub email: String,
94
+ pub name: String,
95
+ pub password: String,
96
+ }
97
+
98
+ async fn create_user(
99
+ State(state): State<AppState>,
100
+ Json(body): Json<CreateUserRequest>,
101
+ ) -> Result<(StatusCode, Json<User>), AppError> {
102
+ // Validate
103
+ if body.email.is_empty() {
104
+ return Err(AppError::Validation("Email is required".into()));
105
+ }
106
+
107
+ let user = User::new(body.email, body.name, &body.password)?;
108
+ // ... save user
109
+
110
+ Ok((StatusCode::CREATED, Json(user)))
111
+ }
112
+
113
+ // Query parameters
114
+ #[derive(Deserialize)]
115
+ pub struct PaginationQuery {
116
+ #[serde(default = "default_page")]
117
+ pub page: u32,
118
+ #[serde(default = "default_per_page")]
119
+ pub per_page: u32,
120
+ }
121
+
122
+ fn default_page() -> u32 { 1 }
123
+ fn default_per_page() -> u32 { 20 }
124
+
125
+ async fn list_users(
126
+ State(state): State<AppState>,
127
+ Query(query): Query<PaginationQuery>,
128
+ ) -> Result<Json<Vec<User>>, AppError> {
129
+ let offset = (query.page - 1) * query.per_page;
130
+ let users = sqlx::query_as!(User,
131
+ "SELECT * FROM users LIMIT $1 OFFSET $2",
132
+ query.per_page as i64,
133
+ offset as i64
134
+ )
135
+ .fetch_all(&state.db)
136
+ .await?;
137
+
138
+ Ok(Json(users))
139
+ }
140
+
141
+ // Multiple path parameters
142
+ async fn get_post_comment(
143
+ Path((post_id, comment_id)): Path<(String, String)>,
144
+ ) -> Result<Json<Comment>, AppError> {
145
+ // ...
146
+ }
147
+
148
+ // Headers
149
+ async fn with_headers(headers: HeaderMap) -> impl IntoResponse {
150
+ let user_agent = headers
151
+ .get("user-agent")
152
+ .and_then(|v| v.to_str().ok())
153
+ .unwrap_or("unknown");
154
+ format!("User-Agent: {}", user_agent)
155
+ }
156
+ \`\`\`
157
+
158
+ ## Custom Extractors
159
+ \`\`\`rust
160
+ use axum::{
161
+ async_trait,
162
+ extract::FromRequestParts,
163
+ http::{request::Parts, StatusCode},
164
+ RequestPartsExt,
165
+ };
166
+ use axum_extra::{
167
+ headers::{authorization::Bearer, Authorization},
168
+ TypedHeader,
169
+ };
170
+
171
+ pub struct AuthUser {
172
+ pub user_id: String,
173
+ pub role: String,
174
+ }
175
+
176
+ #[async_trait]
177
+ impl<S> FromRequestParts<S> for AuthUser
178
+ where
179
+ S: Send + Sync,
180
+ {
181
+ type Rejection = AppError;
182
+
183
+ async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
184
+ // Extract Authorization header
185
+ let TypedHeader(Authorization(bearer)) = parts
186
+ .extract::<TypedHeader<Authorization<Bearer>>>()
187
+ .await
188
+ .map_err(|_| AppError::Unauthorized)?;
189
+
190
+ // Validate token
191
+ let claims = validate_token(bearer.token())
192
+ .map_err(|_| AppError::Unauthorized)?;
193
+
194
+ Ok(AuthUser {
195
+ user_id: claims.sub,
196
+ role: claims.role,
197
+ })
198
+ }
199
+ }
200
+
201
+ // Use in handler
202
+ async fn protected_handler(auth: AuthUser) -> impl IntoResponse {
203
+ format!("Hello, user {}", auth.user_id)
204
+ }
205
+ \`\`\`
206
+
207
+ ## Middleware
208
+ \`\`\`rust
209
+ use axum::{
210
+ middleware::Next,
211
+ http::Request,
212
+ response::Response,
213
+ };
214
+
215
+ // Function-based middleware
216
+ async fn auth_middleware(
217
+ request: Request<axum::body::Body>,
218
+ next: Next,
219
+ ) -> Result<Response, AppError> {
220
+ let auth_header = request
221
+ .headers()
222
+ .get("Authorization")
223
+ .and_then(|v| v.to_str().ok());
224
+
225
+ let token = match auth_header {
226
+ Some(h) if h.starts_with("Bearer ") => &h[7..],
227
+ _ => return Err(AppError::Unauthorized),
228
+ };
229
+
230
+ validate_token(token).map_err(|_| AppError::Unauthorized)?;
231
+
232
+ Ok(next.run(request).await)
233
+ }
234
+
235
+ // Apply to routes
236
+ fn protected_routes() -> Router<AppState> {
237
+ Router::new()
238
+ .route("/profile", get(get_profile))
239
+ .layer(middleware::from_fn(auth_middleware))
240
+ }
241
+
242
+ // Request timing middleware
243
+ async fn timing_middleware(
244
+ request: Request<axum::body::Body>,
245
+ next: Next,
246
+ ) -> Response {
247
+ let start = std::time::Instant::now();
248
+ let method = request.method().clone();
249
+ let uri = request.uri().clone();
250
+
251
+ let response = next.run(request).await;
252
+
253
+ let duration = start.elapsed();
254
+ tracing::info!("{} {} - {:?}", method, uri, duration);
255
+
256
+ response
257
+ }
258
+ \`\`\`
259
+
260
+ ## Error Handling
261
+ \`\`\`rust
262
+ use axum::{
263
+ response::{IntoResponse, Response},
264
+ http::StatusCode,
265
+ Json,
266
+ };
267
+ use thiserror::Error;
268
+
269
+ #[derive(Error, Debug)]
270
+ pub enum AppError {
271
+ #[error("Resource not found")]
272
+ NotFound,
273
+
274
+ #[error("Unauthorized")]
275
+ Unauthorized,
276
+
277
+ #[error("Validation error: {0}")]
278
+ Validation(String),
279
+
280
+ #[error("Database error")]
281
+ Database(#[from] sqlx::Error),
282
+ }
283
+
284
+ impl IntoResponse for AppError {
285
+ fn into_response(self) -> Response {
286
+ let (status, message) = match &self {
287
+ AppError::NotFound => (StatusCode::NOT_FOUND, self.to_string()),
288
+ AppError::Unauthorized => (StatusCode::UNAUTHORIZED, self.to_string()),
289
+ AppError::Validation(_) => (StatusCode::BAD_REQUEST, self.to_string()),
290
+ AppError::Database(e) => {
291
+ tracing::error!("Database error: {:?}", e);
292
+ (StatusCode::INTERNAL_SERVER_ERROR, "Internal server error".into())
293
+ }
294
+ };
295
+
296
+ let body = Json(serde_json::json!({
297
+ "error": message
298
+ }));
299
+
300
+ (status, body).into_response()
301
+ }
302
+ }
303
+
304
+ // Handler returning Result
305
+ async fn get_user(
306
+ State(state): State<AppState>,
307
+ Path(id): Path<String>,
308
+ ) -> Result<Json<User>, AppError> {
309
+ let user = find_user(&state.db, &id)
310
+ .await?
311
+ .ok_or(AppError::NotFound)?;
312
+
313
+ Ok(Json(user))
314
+ }
315
+ \`\`\`
316
+
317
+ ## Testing
318
+ \`\`\`rust
319
+ #[cfg(test)]
320
+ mod tests {
321
+ use super::*;
322
+ use axum::{
323
+ body::Body,
324
+ http::{Request, StatusCode},
325
+ };
326
+ use tower::ServiceExt; // for oneshot
327
+
328
+ async fn create_test_app() -> Router {
329
+ let state = AppState {
330
+ db: create_test_pool().await,
331
+ };
332
+ Router::new()
333
+ .nest("/api/v1", api_routes())
334
+ .with_state(state)
335
+ }
336
+
337
+ #[tokio::test]
338
+ async fn test_create_user() {
339
+ let app = create_test_app().await;
340
+
341
+ let response = app
342
+ .oneshot(
343
+ Request::builder()
344
+ .method("POST")
345
+ .uri("/api/v1/users")
346
+ .header("Content-Type", "application/json")
347
+ .body(Body::from(r#"{"email":"test@example.com","name":"Test","password":"password123"}"#))
348
+ .unwrap()
349
+ )
350
+ .await
351
+ .unwrap();
352
+
353
+ assert_eq!(response.status(), StatusCode::CREATED);
354
+ }
355
+
356
+ #[tokio::test]
357
+ async fn test_get_user_not_found() {
358
+ let app = create_test_app().await;
359
+
360
+ let response = app
361
+ .oneshot(
362
+ Request::builder()
363
+ .uri("/api/v1/users/nonexistent")
364
+ .body(Body::empty())
365
+ .unwrap()
366
+ )
367
+ .await
368
+ .unwrap();
369
+
370
+ assert_eq!(response.status(), StatusCode::NOT_FOUND);
371
+ }
372
+ }
373
+ \`\`\`
374
+
375
+ ## ✅ DO
376
+ - Use \`Router::nest\` for modular route organization
377
+ - Implement \`IntoResponse\` for custom error types
378
+ - Use \`tower\` layers for cross-cutting concerns
379
+ - Use custom extractors for auth/validation
380
+ - Clone state only when needed (it should be \`Arc\` internally)
381
+
382
+ ## ❌ DON'T
383
+ - Don't block async runtime with sync operations
384
+ - Don't forget to derive \`Clone\` for \`AppState\`
385
+ - Don't expose internal errors to clients
386
+ - Don't use \`.unwrap()\` in handlers - return \`Result\`