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,414 @@
1
+ # Laravel Skill
2
+
3
+ ## Route Definition
4
+ \`\`\`php
5
+ <?php
6
+ // routes/api.php
7
+
8
+ use App\\Http\\Controllers\\Api\\V1\\UserController;
9
+
10
+ Route::prefix('v1')->group(function () {
11
+ // Public routes
12
+ Route::post('auth/login', [AuthController::class, 'login']);
13
+ Route::post('auth/register', [AuthController::class, 'register']);
14
+
15
+ // Protected routes
16
+ Route::middleware('auth:sanctum')->group(function () {
17
+ Route::apiResource('users', UserController::class);
18
+ Route::get('users/{user}/posts', [UserController::class, 'posts']);
19
+ });
20
+ });
21
+ \`\`\`
22
+
23
+ ## Controller
24
+ \`\`\`php
25
+ <?php
26
+
27
+ namespace App\\Http\\Controllers\\Api\\V1;
28
+
29
+ use App\\Http\\Controllers\\Controller;
30
+ use App\\Http\\Requests\\CreateUserRequest;
31
+ use App\\Http\\Requests\\UpdateUserRequest;
32
+ use App\\Http\\Resources\\UserResource;
33
+ use App\\Http\\Resources\\UserCollection;
34
+ use App\\Models\\User;
35
+ use Illuminate\\Http\\JsonResponse;
36
+ use Illuminate\\Http\\Response;
37
+
38
+ final class UserController extends Controller
39
+ {
40
+ public function index(): UserCollection
41
+ {
42
+ $users = User::query()
43
+ ->where('is_active', true)
44
+ ->latest()
45
+ ->paginate();
46
+
47
+ return new UserCollection($users);
48
+ }
49
+
50
+ public function show(User $user): UserResource
51
+ {
52
+ return new UserResource($user->load(['profile', 'roles']));
53
+ }
54
+
55
+ public function store(CreateUserRequest $request): JsonResponse
56
+ {
57
+ $user = User::create([
58
+ ...$request->validated(),
59
+ 'password' => bcrypt($request->password),
60
+ ]);
61
+
62
+ return (new UserResource($user))
63
+ ->response()
64
+ ->setStatusCode(Response::HTTP_CREATED);
65
+ }
66
+
67
+ public function update(UpdateUserRequest $request, User $user): UserResource
68
+ {
69
+ $user->update($request->validated());
70
+
71
+ return new UserResource($user);
72
+ }
73
+
74
+ public function destroy(User $user): Response
75
+ {
76
+ $user->delete();
77
+
78
+ return response()->noContent();
79
+ }
80
+ }
81
+ \`\`\`
82
+
83
+ ## Form Request Validation
84
+ \`\`\`php
85
+ <?php
86
+
87
+ namespace App\\Http\\Requests;
88
+
89
+ use Illuminate\\Foundation\\Http\\FormRequest;
90
+ use Illuminate\\Validation\\Rules\\Password;
91
+
92
+ final class CreateUserRequest extends FormRequest
93
+ {
94
+ public function authorize(): bool
95
+ {
96
+ return true; // Or check permissions
97
+ }
98
+
99
+ public function rules(): array
100
+ {
101
+ return [
102
+ 'email' => ['required', 'email', 'max:255', 'unique:users'],
103
+ 'name' => ['required', 'string', 'min:2', 'max:100'],
104
+ 'password' => ['required', 'confirmed', Password::defaults()],
105
+ 'role' => ['sometimes', 'string', 'in:user,admin'],
106
+ ];
107
+ }
108
+
109
+ public function messages(): array
110
+ {
111
+ return [
112
+ 'email.unique' => 'This email is already registered.',
113
+ ];
114
+ }
115
+
116
+ protected function prepareForValidation(): void
117
+ {
118
+ $this->merge([
119
+ 'email' => strtolower($this->email),
120
+ ]);
121
+ }
122
+ }
123
+ \`\`\`
124
+
125
+ ## API Resources
126
+ \`\`\`php
127
+ <?php
128
+
129
+ namespace App\\Http\\Resources;
130
+
131
+ use Illuminate\\Http\\Request;
132
+ use Illuminate\\Http\\Resources\\Json\\JsonResource;
133
+
134
+ final class UserResource extends JsonResource
135
+ {
136
+ public function toArray(Request $request): array
137
+ {
138
+ return [
139
+ 'id' => $this->id,
140
+ 'email' => $this->email,
141
+ 'name' => $this->name,
142
+ 'is_active' => $this->is_active,
143
+ 'created_at' => $this->created_at->toISOString(),
144
+
145
+ // Conditional relationships
146
+ 'profile' => new ProfileResource($this->whenLoaded('profile')),
147
+ 'roles' => RoleResource::collection($this->whenLoaded('roles')),
148
+ 'posts_count' => $this->when(
149
+ $this->posts_count !== null,
150
+ $this->posts_count
151
+ ),
152
+ ];
153
+ }
154
+ }
155
+
156
+ // Collection with pagination metadata
157
+ final class UserCollection extends ResourceCollection
158
+ {
159
+ public function toArray(Request $request): array
160
+ {
161
+ return [
162
+ 'data' => $this->collection,
163
+ 'meta' => [
164
+ 'current_page' => $this->currentPage(),
165
+ 'per_page' => $this->perPage(),
166
+ 'total' => $this->total(),
167
+ 'last_page' => $this->lastPage(),
168
+ ],
169
+ ];
170
+ }
171
+ }
172
+ \`\`\`
173
+
174
+ ## Eloquent Model
175
+ \`\`\`php
176
+ <?php
177
+
178
+ namespace App\\Models;
179
+
180
+ use Illuminate\\Database\\Eloquent\\Factories\\HasFactory;
181
+ use Illuminate\\Database\\Eloquent\\Model;
182
+ use Illuminate\\Database\\Eloquent\\Relations\\HasMany;
183
+ use Illuminate\\Database\\Eloquent\\Relations\\HasOne;
184
+ use Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany;
185
+ use Illuminate\\Database\\Eloquent\\SoftDeletes;
186
+ use Illuminate\\Database\\Eloquent\\Builder;
187
+
188
+ final class User extends Model
189
+ {
190
+ use HasFactory, SoftDeletes;
191
+
192
+ protected $fillable = [
193
+ 'email',
194
+ 'name',
195
+ 'password',
196
+ 'is_active',
197
+ ];
198
+
199
+ protected $hidden = [
200
+ 'password',
201
+ ];
202
+
203
+ protected $casts = [
204
+ 'is_active' => 'boolean',
205
+ 'email_verified_at' => 'datetime',
206
+ ];
207
+
208
+ // Relationships
209
+ public function profile(): HasOne
210
+ {
211
+ return $this->hasOne(Profile::class);
212
+ }
213
+
214
+ public function posts(): HasMany
215
+ {
216
+ return $this->hasMany(Post::class, 'author_id');
217
+ }
218
+
219
+ public function roles(): BelongsToMany
220
+ {
221
+ return $this->belongsToMany(Role::class)->withTimestamps();
222
+ }
223
+
224
+ // Scopes
225
+ public function scopeActive(Builder $query): Builder
226
+ {
227
+ return $query->where('is_active', true);
228
+ }
229
+
230
+ public function scopeWithRole(Builder $query, string $role): Builder
231
+ {
232
+ return $query->whereHas('roles', fn($q) => $q->where('name', $role));
233
+ }
234
+
235
+ // Accessors
236
+ protected function email(): Attribute
237
+ {
238
+ return Attribute::make(
239
+ get: fn(string $value) => strtolower($value),
240
+ set: fn(string $value) => strtolower($value),
241
+ );
242
+ }
243
+
244
+ // Query methods
245
+ public static function findByEmail(string $email): ?self
246
+ {
247
+ return self::where('email', strtolower($email))->first();
248
+ }
249
+ }
250
+ \`\`\`
251
+
252
+ ## Service Pattern
253
+ \`\`\`php
254
+ <?php
255
+
256
+ namespace App\\Services;
257
+
258
+ use App\\Models\\User;
259
+ use App\\DTOs\\CreateUserData;
260
+ use Illuminate\\Support\\Facades\\DB;
261
+ use Illuminate\\Support\\Facades\\Hash;
262
+
263
+ final class UserService
264
+ {
265
+ public function __construct(
266
+ private readonly NotificationService $notifications,
267
+ ) {}
268
+
269
+ public function create(CreateUserData $data): User
270
+ {
271
+ return DB::transaction(function () use ($data) {
272
+ $user = User::create([
273
+ 'email' => $data->email,
274
+ 'name' => $data->name,
275
+ 'password' => Hash::make($data->password),
276
+ ]);
277
+
278
+ if ($data->role) {
279
+ $user->roles()->attach(Role::findByName($data->role));
280
+ }
281
+
282
+ $this->notifications->sendWelcome($user);
283
+
284
+ return $user;
285
+ });
286
+ }
287
+
288
+ public function updateProfile(User $user, array $data): User
289
+ {
290
+ $user->update($data);
291
+
292
+ if (isset($data['avatar'])) {
293
+ $user->profile()->updateOrCreate(
294
+ ['user_id' => $user->id],
295
+ ['avatar' => $data['avatar']]
296
+ );
297
+ }
298
+
299
+ return $user->fresh(['profile']);
300
+ }
301
+ }
302
+ \`\`\`
303
+
304
+ ## Migrations
305
+ \`\`\`php
306
+ <?php
307
+
308
+ use Illuminate\\Database\\Migrations\\Migration;
309
+ use Illuminate\\Database\\Schema\\Blueprint;
310
+ use Illuminate\\Support\\Facades\\Schema;
311
+
312
+ return new class extends Migration
313
+ {
314
+ public function up(): void
315
+ {
316
+ Schema::create('users', function (Blueprint $table) {
317
+ $table->uuid('id')->primary();
318
+ $table->string('email')->unique();
319
+ $table->string('name', 100);
320
+ $table->string('password');
321
+ $table->boolean('is_active')->default(true);
322
+ $table->timestamp('email_verified_at')->nullable();
323
+ $table->timestamps();
324
+ $table->softDeletes();
325
+
326
+ $table->index(['is_active', 'created_at']);
327
+ });
328
+ }
329
+
330
+ public function down(): void
331
+ {
332
+ Schema::dropIfExists('users');
333
+ }
334
+ };
335
+ \`\`\`
336
+
337
+ ## Testing
338
+ \`\`\`php
339
+ <?php
340
+
341
+ namespace Tests\\Feature;
342
+
343
+ use App\\Models\\User;
344
+ use Illuminate\\Foundation\\Testing\\RefreshDatabase;
345
+ use Tests\\TestCase;
346
+
347
+ final class UserControllerTest extends TestCase
348
+ {
349
+ use RefreshDatabase;
350
+
351
+ public function test_can_list_users(): void
352
+ {
353
+ User::factory()->count(3)->create();
354
+
355
+ $response = $this->getJson('/api/v1/users');
356
+
357
+ $response->assertOk()
358
+ ->assertJsonCount(3, 'data')
359
+ ->assertJsonStructure([
360
+ 'data' => [['id', 'email', 'name']],
361
+ 'meta' => ['current_page', 'total'],
362
+ ]);
363
+ }
364
+
365
+ public function test_can_create_user(): void
366
+ {
367
+ $data = [
368
+ 'email' => 'test@example.com',
369
+ 'name' => 'Test User',
370
+ 'password' => 'password123',
371
+ 'password_confirmation' => 'password123',
372
+ ];
373
+
374
+ $response = $this->postJson('/api/v1/users', $data);
375
+
376
+ $response->assertCreated()
377
+ ->assertJsonPath('data.email', 'test@example.com');
378
+
379
+ $this->assertDatabaseHas('users', [
380
+ 'email' => 'test@example.com',
381
+ ]);
382
+ }
383
+
384
+ public function test_cannot_create_user_with_duplicate_email(): void
385
+ {
386
+ User::factory()->create(['email' => 'test@example.com']);
387
+
388
+ $response = $this->postJson('/api/v1/users', [
389
+ 'email' => 'test@example.com',
390
+ 'name' => 'Test',
391
+ 'password' => 'password123',
392
+ 'password_confirmation' => 'password123',
393
+ ]);
394
+
395
+ $response->assertUnprocessable()
396
+ ->assertJsonValidationErrors(['email']);
397
+ }
398
+ }
399
+ \`\`\`
400
+
401
+ ## ✅ DO
402
+ - Use Form Requests for validation
403
+ - Use API Resources for response transformation
404
+ - Use Eloquent scopes for reusable queries
405
+ - Use Service classes for complex business logic
406
+ - Use DB::transaction for multi-model operations
407
+ - Use \`$casts\` for type casting
408
+
409
+ ## ❌ DON'T
410
+ - Don't put business logic in controllers
411
+ - Don't return Eloquent models directly (use Resources)
412
+ - Don't use \`DB::raw()\` without parameterized queries
413
+ - Don't eager load everything (use \`whenLoaded\`)
414
+ - Don't forget \`$fillable\` or \`$guarded\`
@@ -0,0 +1,320 @@
1
+ # MCP Browser Skill
2
+
3
+ ## Server Configuration (Puppeteer)
4
+ \`\`\`json
5
+ // claude_desktop_config.json or .claude/settings.json
6
+ {
7
+ "mcpServers": {
8
+ "puppeteer": {
9
+ "command": "npx",
10
+ "args": ["-y", "@modelcontextprotocol/server-puppeteer"]
11
+ }
12
+ }
13
+ }
14
+ \`\`\`
15
+
16
+ ## Server Configuration (Playwright)
17
+ \`\`\`json
18
+ {
19
+ "mcpServers": {
20
+ "playwright": {
21
+ "command": "npx",
22
+ "args": ["-y", "@playwright/mcp-server"],
23
+ "env": {
24
+ "BROWSER": "chromium"
25
+ }
26
+ }
27
+ }
28
+ }
29
+ \`\`\`
30
+
31
+ ## Available Tools
32
+
33
+ ### Navigation
34
+ \`\`\`
35
+ navigate
36
+ - url: string (URL to navigate to)
37
+ - Returns: page title, URL after navigation
38
+
39
+ go_back
40
+ - Returns: previous page info
41
+
42
+ go_forward
43
+ - Returns: next page info
44
+
45
+ reload
46
+ - Returns: page info after reload
47
+ \`\`\`
48
+
49
+ ### Screenshot & Content
50
+ \`\`\`
51
+ screenshot
52
+ - name?: string (optional filename)
53
+ - selector?: string (capture specific element)
54
+ - fullPage?: boolean (capture entire scrollable page)
55
+ - Returns: base64 encoded image
56
+
57
+ get_page_content
58
+ - Returns: page HTML content
59
+
60
+ get_page_text
61
+ - Returns: visible text content
62
+
63
+ get_page_title
64
+ - Returns: page title
65
+
66
+ get_current_url
67
+ - Returns: current URL
68
+ \`\`\`
69
+
70
+ ### Element Interaction
71
+ \`\`\`
72
+ click
73
+ - selector: string (CSS selector or XPath)
74
+ - button?: 'left' | 'right' | 'middle'
75
+ - clickCount?: number (for double-click: 2)
76
+ - Returns: success status
77
+
78
+ fill
79
+ - selector: string (input selector)
80
+ - value: string (text to fill)
81
+ - Returns: success status
82
+
83
+ select
84
+ - selector: string (select element)
85
+ - value: string (option value)
86
+ - Returns: selected value
87
+
88
+ check
89
+ - selector: string (checkbox/radio)
90
+ - Returns: success status
91
+
92
+ uncheck
93
+ - selector: string
94
+ - Returns: success status
95
+
96
+ hover
97
+ - selector: string
98
+ - Returns: success status
99
+
100
+ focus
101
+ - selector: string
102
+ - Returns: success status
103
+
104
+ press
105
+ - key: string (e.g., 'Enter', 'Tab', 'Escape')
106
+ - Returns: success status
107
+
108
+ type
109
+ - text: string (type character by character)
110
+ - delay?: number (ms between keystrokes)
111
+ - Returns: success status
112
+ \`\`\`
113
+
114
+ ### Element Queries
115
+ \`\`\`
116
+ query_selector
117
+ - selector: string
118
+ - Returns: element info or null
119
+
120
+ query_selector_all
121
+ - selector: string
122
+ - Returns: array of element info
123
+
124
+ get_element_text
125
+ - selector: string
126
+ - Returns: text content
127
+
128
+ get_element_attribute
129
+ - selector: string
130
+ - attribute: string
131
+ - Returns: attribute value
132
+
133
+ is_visible
134
+ - selector: string
135
+ - Returns: boolean
136
+
137
+ wait_for_selector
138
+ - selector: string
139
+ - timeout?: number (ms)
140
+ - state?: 'attached' | 'detached' | 'visible' | 'hidden'
141
+ - Returns: success status
142
+ \`\`\`
143
+
144
+ ### Page Control
145
+ \`\`\`
146
+ scroll
147
+ - direction: 'up' | 'down' | 'left' | 'right'
148
+ - amount?: number (pixels)
149
+
150
+ scroll_to_element
151
+ - selector: string
152
+
153
+ evaluate
154
+ - script: string (JavaScript to execute)
155
+ - Returns: script result
156
+
157
+ wait_for_navigation
158
+ - timeout?: number (ms)
159
+ - Returns: new page info
160
+
161
+ set_viewport
162
+ - width: number
163
+ - height: number
164
+ - deviceScaleFactor?: number
165
+ - isMobile?: boolean
166
+ \`\`\`
167
+
168
+ ## Selector Strategies
169
+
170
+ ### CSS Selectors
171
+ \`\`\`css
172
+ /* By ID */
173
+ #submit-button
174
+
175
+ /* By class */
176
+ .nav-item
177
+ .btn.btn-primary
178
+
179
+ /* By attribute */
180
+ [data-testid="login-form"]
181
+ input[type="email"]
182
+ a[href^="https://"]
183
+
184
+ /* By hierarchy */
185
+ form.login input[type="password"]
186
+ .sidebar > .menu-item:first-child
187
+
188
+ /* By state (CSS pseudo-classes) */
189
+ button:not(:disabled)
190
+ input:focus
191
+ \`\`\`
192
+
193
+ ### XPath Selectors
194
+ \`\`\`xpath
195
+ // By text content
196
+ //button[text()="Submit"]
197
+ //a[contains(text(), "Learn more")]
198
+
199
+ // By attribute
200
+ //input[@placeholder="Enter email"]
201
+ //*[@data-testid="user-profile"]
202
+
203
+ // By position
204
+ //ul/li[1]
205
+ (//button[@type="submit"])[2]
206
+
207
+ // By parent/sibling
208
+ //label[text()="Email"]/following-sibling::input
209
+ //td[text()="John"]/parent::tr
210
+ \`\`\`
211
+
212
+ ### Playwright-specific Selectors
213
+ \`\`\`
214
+ // Text selector
215
+ text=Sign In
216
+ text="Exact Match"
217
+
218
+ // Role selector (accessibility)
219
+ role=button[name="Submit"]
220
+ role=link[name=/learn more/i]
221
+
222
+ // Locator chaining
223
+ .form >> input[type="email"]
224
+ \`\`\`
225
+
226
+ ## Common Workflows
227
+
228
+ ### Form Submission
229
+ \`\`\`
230
+ 1. navigate to form URL
231
+ 2. wait_for_selector: form element
232
+ 3. fill: email field
233
+ 4. fill: password field
234
+ 5. click: submit button
235
+ 6. wait_for_navigation
236
+ 7. screenshot: confirmation page
237
+ \`\`\`
238
+
239
+ ### Data Extraction
240
+ \`\`\`
241
+ 1. navigate to page
242
+ 2. wait_for_selector: data container
243
+ 3. query_selector_all: data items
244
+ 4. Loop: get_element_text for each
245
+ 5. Return structured data
246
+ \`\`\`
247
+
248
+ ### Screenshot Testing
249
+ \`\`\`
250
+ 1. navigate to page
251
+ 2. set_viewport: specific dimensions
252
+ 3. wait_for_selector: key content
253
+ 4. screenshot: fullPage true
254
+ 5. Compare with baseline
255
+ \`\`\`
256
+
257
+ ### Authentication Flow
258
+ \`\`\`
259
+ 1. navigate: login page
260
+ 2. fill: credentials
261
+ 3. click: sign in
262
+ 4. wait_for_selector: dashboard element
263
+ 5. Cookies persist for session
264
+ \`\`\`
265
+
266
+ ## Wait Strategies
267
+ \`\`\`javascript
268
+ // Wait for element to appear
269
+ wait_for_selector with state: 'visible'
270
+
271
+ // Wait for element to disappear (loading spinner)
272
+ wait_for_selector with state: 'hidden'
273
+
274
+ // Wait for navigation after click
275
+ click then wait_for_navigation
276
+
277
+ // Wait for network idle (all requests complete)
278
+ // Some servers support: wait_for_load_state('networkidle')
279
+
280
+ // Fixed delay (avoid when possible)
281
+ // Use wait_for_selector instead
282
+ \`\`\`
283
+
284
+ ## Handling Dynamic Content
285
+ \`\`\`
286
+ // Infinite scroll
287
+ 1. scroll: down
288
+ 2. wait_for_selector: new content
289
+ 3. Repeat until done
290
+
291
+ // Modal/popup
292
+ 1. click: trigger button
293
+ 2. wait_for_selector: modal container
294
+ 3. Interact with modal content
295
+ 4. click: close button
296
+ 5. wait_for_selector with state: 'hidden'
297
+
298
+ // AJAX content
299
+ 1. click: trigger
300
+ 2. wait_for_selector: loading indicator (hidden)
301
+ 3. wait_for_selector: new content (visible)
302
+ \`\`\`
303
+
304
+ ## ❌ DON'T
305
+ - Use fixed delays instead of wait_for_selector
306
+ - Click on invisible/disabled elements
307
+ - Ignore CAPTCHA or bot protection
308
+ - Scrape sites that prohibit it in robots.txt
309
+ - Store credentials in automation scripts
310
+ - Use automation for malicious purposes
311
+
312
+ ## ✅ DO
313
+ - Use data-testid attributes for reliable selectors
314
+ - Wait for elements before interacting
315
+ - Handle navigation and loading states
316
+ - Use appropriate viewport for responsive sites
317
+ - Take screenshots for debugging failures
318
+ - Respect rate limits and robots.txt
319
+ - Handle popups and cookie banners
320
+ - Use role selectors for accessibility