create-agent-skills 1.0.0 → 1.1.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/README.md CHANGED
@@ -41,6 +41,12 @@ npx create-agent-skills
41
41
  | `code-review` | Reviews code for bugs, style, and security issues |
42
42
  | `create-agent-skill` | Helps create new skills following guidelines |
43
43
  | `documentation` | Creates clear READMEs, API docs, and comments |
44
+ | `git-commit` | Writes conventional commit messages |
45
+ | `git-pr` | Creates well-structured pull requests |
46
+ | `git-review` | Reviews PRs for code quality and best practices |
47
+ | `maestro-testing` | Write E2E tests for mobile/web apps using Maestro |
48
+ | `tailwindcss-v4` | Tailwind CSS v4 setup and migration guide |
49
+ | `tauri-v2` | Build desktop apps with Tauri v2 + React |
44
50
  | `testing` | Helps write unit, integration, and E2E tests |
45
51
 
46
52
  ## Creating New Skills
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-agent-skills",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "CLI tool to install Agent Skills for AI coding assistants",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
@@ -0,0 +1,691 @@
1
+ ---
2
+ name: maestro-testing
3
+ description: Write robust E2E tests for mobile apps and web using Maestro. Use when creating UI automation tests, flow-based testing, or setting up test suites with optimal selectors and best practices.
4
+ ---
5
+
6
+ # Maestro Testing Skill
7
+
8
+ Write robust End-to-End tests for mobile apps (iOS/Android) and web applications using Maestro - a simple, powerful, and reliable UI testing framework.
9
+
10
+ ## When to Use This Skill
11
+
12
+ - Writing E2E tests for mobile apps (iOS, Android, React Native, Flutter)
13
+ - Testing web applications in desktop browsers
14
+ - Creating reusable test flows and page objects
15
+ - Setting up CI/CD test automation with Maestro
16
+ - Migrating from other testing frameworks to Maestro
17
+
18
+ ## Examples & Resources
19
+
20
+ ### Examples
21
+ - [Login Flow](examples/login-flow.md) - Complete login test with subflows and best practices
22
+ - [Checkout Flow](examples/checkout-flow.md) - E-commerce checkout with scrolling and forms
23
+ - [Project Structure](examples/project-structure.md) - Recommended test suite organization
24
+
25
+ ### Resources
26
+ - [Commands Reference](resources/commands-reference.md) - Quick reference cheat sheet for all commands
27
+
28
+
29
+ ## Prerequisites
30
+
31
+ - Maestro CLI installed (`curl -fsSL "https://get.maestro.mobile.dev" | bash`)
32
+ - For mobile: Android emulator or iOS simulator running
33
+ - For web: Chrome browser installed
34
+ - App installed on device/emulator (for mobile testing)
35
+
36
+ ## Flow File Structure
37
+
38
+ Every Maestro flow is a YAML file with optional configuration header and commands:
39
+
40
+ ```yaml
41
+ # Flow configuration (optional header above ---)
42
+ appId: com.example.myapp # Required: package name (Android) or bundle ID (iOS)
43
+ name: Login Flow Test # Optional: custom name for reports
44
+ tags:
45
+ - smoke
46
+ - login
47
+ env:
48
+ USERNAME: testuser@example.com
49
+ PASSWORD: secret123
50
+ ---
51
+ # Commands start after ---
52
+ - launchApp
53
+ - tapOn: "Login"
54
+ - inputText: "${USERNAME}"
55
+ ```
56
+
57
+ ## Selector Priority (Most to Least Preferred)
58
+
59
+ > [!IMPORTANT]
60
+ > Always prefer stable selectors. The order of preference:
61
+
62
+ | Priority | Selector | When to Use | Example |
63
+ |----------|----------|-------------|---------|
64
+ | 1 | `id` | Best for dynamic content, i18n apps | `id: "login_button"` |
65
+ | 2 | `text` | Stable static text, readable tests | `text: "Submit"` |
66
+ | 3 | `id` + `text` | Unique identification | `id: "btn", text: "OK"` |
67
+ | 4 | Relative selectors | Complex layouts | `below: "Email", id: "input"` |
68
+ | 5 | `index` | Last resort for duplicates | `text: "Item", index: 2` |
69
+ | ❌ | `point` coordinates | Avoid - device dependent | `point: "50%, 50%"` |
70
+
71
+ ### Selector Syntax Reference
72
+
73
+ ```yaml
74
+ # Basic selectors
75
+ - tapOn: "Button Text" # Shorthand for text
76
+ - tapOn:
77
+ id: "submit_btn" # By accessibility ID
78
+ - tapOn:
79
+ text: "Login" # By visible text
80
+ enabled: true # Must be enabled
81
+
82
+ # Relative position selectors
83
+ - tapOn:
84
+ below: "Email Label" # Element below another
85
+ id: "input_field"
86
+ - tapOn:
87
+ above:
88
+ id: "footer" # Element above footer
89
+ - tapOn:
90
+ leftOf: "Price"
91
+ text: "Quantity"
92
+ - tapOn:
93
+ containsChild: "Icon" # Parent contains child with text
94
+ - tapOn:
95
+ childOf:
96
+ id: "toolbar" # Child of element with ID
97
+
98
+ # Multiple matches - use index (0-based)
99
+ - tapOn:
100
+ text: "Add to Cart"
101
+ index: 0 # First matching element
102
+
103
+ # Element state selectors
104
+ - assertVisible:
105
+ text: "Submit"
106
+ enabled: true # Must be enabled
107
+ checked: false # Checkbox unchecked
108
+ focused: true # Has keyboard focus
109
+ selected: true # Is selected
110
+
111
+ # Size-based selectors
112
+ - tapOn:
113
+ width: 100
114
+ height: 50
115
+ tolerance: 10 # ±10 pixels
116
+
117
+ # Element traits
118
+ - tapOn:
119
+ traits: text # Contains text
120
+ - tapOn:
121
+ traits: long-text # 200+ characters
122
+ - tapOn:
123
+ traits: square # Width ≈ Height
124
+
125
+ # Regular expressions (all text/id fields support regex)
126
+ - assertVisible: "Total: \\$[0-9]+\\.[0-9]{2}"
127
+ - tapOn:
128
+ id: ".*_submit_button"
129
+ - assertVisible: ".*brown fox.*" # Partial match with .*
130
+ ```
131
+
132
+ ## Essential Commands
133
+
134
+ ### App Lifecycle
135
+
136
+ ```yaml
137
+ # Launch app (uses appId from config)
138
+ - launchApp
139
+
140
+ # Launch with clean state
141
+ - launchApp:
142
+ clearState: true
143
+ clearKeychain: true # iOS only
144
+
145
+ # Launch specific app
146
+ - launchApp:
147
+ appId: "com.other.app"
148
+
149
+ # Launch with specific permissions
150
+ - launchApp:
151
+ permissions:
152
+ notifications: deny
153
+ camera: allow
154
+ location: deny
155
+
156
+ # Stop and kill app
157
+ - stopApp
158
+ - killApp
159
+ ```
160
+
161
+ ### Tap Interactions
162
+
163
+ ```yaml
164
+ # Simple tap
165
+ - tapOn: "Button"
166
+ - tapOn:
167
+ id: "submit_btn"
168
+
169
+ # Double tap
170
+ - doubleTapOn: "Zoom In"
171
+
172
+ # Long press
173
+ - longPressOn: "Item to Delete"
174
+
175
+ # Tap with retry (for async loading)
176
+ - tapOn:
177
+ text: "Load More"
178
+ retryTapIfNoChange: true # Retry if screen doesn't change
179
+
180
+ # Repeated taps
181
+ - tapOn:
182
+ text: "+"
183
+ repeat: 5
184
+ delay: 200 # 200ms between taps
185
+
186
+ # Tap relative point within element
187
+ - tapOn:
188
+ text: "A text with a hyperlink"
189
+ point: "90%, 50%" # Tap right side of element
190
+ ```
191
+
192
+ ### Text Input
193
+
194
+ ```yaml
195
+ # Type text (into focused field)
196
+ - inputText: "Hello World"
197
+
198
+ # Tap field first, then type
199
+ - tapOn:
200
+ id: "email_input"
201
+ - inputText: "user@example.com"
202
+
203
+ # Random data generation
204
+ - inputRandomEmail # Random email
205
+ - inputRandomPersonName # Random name
206
+ - inputRandomNumber:
207
+ length: 6 # 6-digit number
208
+ - inputRandomText:
209
+ length: 10 # 10 random characters
210
+
211
+ # Erase text
212
+ - eraseText: 10 # Delete 10 characters
213
+ - eraseText # Delete all (focused field)
214
+
215
+ # Copy and paste
216
+ - copyTextFrom:
217
+ id: "generated_code"
218
+ - pasteText
219
+ ```
220
+
221
+ ### Assertions
222
+
223
+ ```yaml
224
+ # Assert element is visible
225
+ - assertVisible: "Welcome"
226
+ - assertVisible:
227
+ id: "success_message"
228
+ enabled: true
229
+
230
+ # Assert element is NOT visible
231
+ - assertNotVisible: "Error"
232
+ - assertNotVisible:
233
+ id: "loading_spinner"
234
+
235
+ # Assert with custom message (for debugging)
236
+ - assertTrue:
237
+ condition: ${value > 0}
238
+ label: "Value should be positive"
239
+
240
+ # AI-powered assertions (requires API key)
241
+ - assertWithAI: "The login form is displayed correctly"
242
+ - assertNoDefectsWithAI # Check for visual defects
243
+ ```
244
+
245
+ ### Scrolling
246
+
247
+ ```yaml
248
+ # Simple scroll down
249
+ - scroll
250
+
251
+ # Scroll until element visible
252
+ - scrollUntilVisible:
253
+ element: "Terms and Conditions"
254
+ direction: DOWN # DOWN|UP|LEFT|RIGHT
255
+ timeout: 30000 # Max 30 seconds
256
+ speed: 40 # 0-100 (higher = faster)
257
+
258
+ # Scroll with centering
259
+ - scrollUntilVisible:
260
+ element:
261
+ id: "target_item"
262
+ centerElement: true # Center element on screen
263
+
264
+ # Horizontal scroll
265
+ - scrollUntilVisible:
266
+ element: "Category 5"
267
+ direction: RIGHT
268
+ ```
269
+
270
+ ### Swipe Gestures
271
+
272
+ ```yaml
273
+ # Swipe in direction
274
+ - swipe:
275
+ direction: LEFT # Swipe left (e.g., dismiss)
276
+
277
+ # Swipe on specific element
278
+ - swipe:
279
+ from:
280
+ id: "swipeable_item"
281
+ direction: LEFT
282
+
283
+ # Swipe between points (relative %)
284
+ - swipe:
285
+ start: "90%, 50%"
286
+ end: "10%, 50%"
287
+
288
+ # Swipe between absolute coordinates
289
+ - swipe:
290
+ start: "300, 500"
291
+ end: "100, 500"
292
+ ```
293
+
294
+ ### Navigation
295
+
296
+ ```yaml
297
+ # Press back button (Android/iOS)
298
+ - back
299
+
300
+ # Open deep link
301
+ - openLink: "myapp://profile/123"
302
+
303
+ # Press hardware keys (Android)
304
+ - pressKey: Home
305
+ - pressKey: Enter
306
+ - pressKey: Volume Up
307
+ ```
308
+
309
+ ### Wait Commands
310
+
311
+ ```yaml
312
+ # Wait for element (default behavior)
313
+ # Most commands automatically wait for elements
314
+
315
+ # Extended wait with timeout
316
+ - extendedWaitUntil:
317
+ visible: "Success"
318
+ timeout: 30000 # Wait up to 30 seconds
319
+
320
+ # Wait for animation to complete
321
+ - waitForAnimationToEnd
322
+
323
+ # Control settle time for dynamic content
324
+ - tapOn:
325
+ text: "Submit"
326
+ waitToSettleTimeoutMs: 1000 # Max wait for screen to settle
327
+ ```
328
+
329
+ ### Screenshots and Recording
330
+
331
+ ```yaml
332
+ # Take screenshot
333
+ - takeScreenshot: "login_screen" # Saved to output directory
334
+
335
+ # Video recording
336
+ - startRecording: "test_flow"
337
+ - launchApp
338
+ - tapOn: "Login"
339
+ - stopRecording
340
+ ```
341
+
342
+ ## Reusable Flows (Page Object Pattern)
343
+
344
+ ### Directory Structure
345
+
346
+ ```
347
+ .maestro/
348
+ ├── config.yaml # Workspace configuration
349
+ ├── flows/
350
+ │ ├── login.yaml # Test: Login flow
351
+ │ ├── checkout.yaml # Test: Checkout flow
352
+ │ └── ...
353
+ ├── subflows/ # Reusable components
354
+ │ ├── login-steps.yaml # Login actions
355
+ │ ├── logout-steps.yaml # Logout actions
356
+ │ └── navigate-to-*.yaml # Navigation helpers
357
+ └── scripts/
358
+ └── helpers.js # JavaScript helpers
359
+ ```
360
+
361
+ ### Subflow Example (login-steps.yaml)
362
+
363
+ ```yaml
364
+ # subflows/login-steps.yaml
365
+ # No appId needed - inherits from parent flow
366
+
367
+ - tapOn:
368
+ id: "email_input"
369
+ - inputText: "${EMAIL}"
370
+ - tapOn:
371
+ id: "password_input"
372
+ - inputText: "${PASSWORD}"
373
+ - tapOn:
374
+ id: "login_button"
375
+ - assertVisible:
376
+ id: "home_screen"
377
+ ```
378
+
379
+ ### Main Flow Using Subflow
380
+
381
+ ```yaml
382
+ # flows/login.yaml
383
+ appId: com.example.app
384
+ env:
385
+ EMAIL: test@example.com
386
+ PASSWORD: password123
387
+ ---
388
+ - launchApp:
389
+ clearState: true
390
+
391
+ - runFlow: ../subflows/login-steps.yaml
392
+
393
+ - assertVisible: "Welcome back"
394
+ ```
395
+
396
+ ### Conditional Flow Execution
397
+
398
+ ```yaml
399
+ # Run subflow only if element visible
400
+ - runFlow:
401
+ when:
402
+ visible: "Cookie Banner"
403
+ file: ../subflows/dismiss-cookies.yaml
404
+
405
+ # Run inline commands conditionally
406
+ - runFlow:
407
+ when:
408
+ visible: "Update Available"
409
+ commands:
410
+ - tapOn: "Later"
411
+
412
+ # Platform-specific flows
413
+ - runFlow:
414
+ when:
415
+ platform: iOS
416
+ file: ../subflows/ios-specific.yaml
417
+ ```
418
+
419
+ ### Passing Parameters to Subflows
420
+
421
+ ```yaml
422
+ # Main flow
423
+ - runFlow:
424
+ file: ../subflows/add-to-cart.yaml
425
+ env:
426
+ PRODUCT_NAME: "Blue T-Shirt"
427
+ QUANTITY: "2"
428
+
429
+ # subflows/add-to-cart.yaml
430
+ - scrollUntilVisible:
431
+ element: "${PRODUCT_NAME}"
432
+ - tapOn: "${PRODUCT_NAME}"
433
+ - tapOn:
434
+ text: "+"
435
+ repeat: ${QUANTITY - 1}
436
+ - tapOn: "Add to Cart"
437
+ ```
438
+
439
+ ## Workspace Configuration
440
+
441
+ Create `.maestro/config.yaml`:
442
+
443
+ ```yaml
444
+ # Flows to include (glob patterns)
445
+ flows:
446
+ - 'flows/*'
447
+ - '!flows/wip-*' # Exclude work-in-progress
448
+
449
+ # Tags configuration
450
+ includeTags:
451
+ - smoke
452
+ excludeTags:
453
+ - flaky
454
+
455
+ # Execution order
456
+ executionOrder:
457
+ continueOnFailure: false
458
+ flowsOrder:
459
+ - flows/login.yaml # Run first
460
+ - flows/onboarding.yaml # Run second
461
+
462
+ # Test output directory
463
+ testOutputDir: test-results
464
+
465
+ # Platform-specific settings
466
+ platform:
467
+ ios:
468
+ disableAnimations: true # Reduce flakiness
469
+ android:
470
+ disableAnimations: true
471
+ ```
472
+
473
+ ## Loops and Repeat
474
+
475
+ ```yaml
476
+ # Repeat commands N times
477
+ - repeat:
478
+ times: 3
479
+ commands:
480
+ - tapOn: "Next"
481
+ - scroll
482
+
483
+ # Repeat while condition true
484
+ - repeat:
485
+ while:
486
+ visible: "Load More"
487
+ commands:
488
+ - tapOn: "Load More"
489
+ - scroll
490
+
491
+ # Repeat with index variable
492
+ - repeat:
493
+ times: ${ITEM_COUNT}
494
+ commands:
495
+ - tapOn: "Item ${maestro.repeatIndex}"
496
+ ```
497
+
498
+ ## JavaScript Integration
499
+
500
+ ```yaml
501
+ # Inline JavaScript
502
+ - evalScript: ${output.total = quantity * price}
503
+
504
+ # Run external script
505
+ - runScript: scripts/calculate-total.js
506
+
507
+ # Use script output
508
+ - assertVisible: "Total: $${output.total}"
509
+ ```
510
+
511
+ ### JavaScript File Example (scripts/helpers.js)
512
+
513
+ ```javascript
514
+ // scripts/helpers.js
515
+ function generateTestEmail() {
516
+ const timestamp = Date.now();
517
+ return `test_${timestamp}@example.com`;
518
+ }
519
+
520
+ // Set output variables
521
+ output.testEmail = generateTestEmail();
522
+ output.currentDate = new Date().toISOString().split('T')[0];
523
+ ```
524
+
525
+ ## Retry Mechanism
526
+
527
+ ```yaml
528
+ # Retry a block of commands on failure
529
+ - retry:
530
+ maxRetries: 3
531
+ commands:
532
+ - tapOn: "Retry Connection"
533
+ - assertVisible: "Connected"
534
+ ```
535
+
536
+ ## Device Settings
537
+
538
+ ```yaml
539
+ # Airplane mode
540
+ - setAirplaneMode:
541
+ enabled: true
542
+ - toggleAirplaneMode
543
+
544
+ # Location (requires permissions)
545
+ - setLocation:
546
+ latitude: 37.7749
547
+ longitude: -122.4194
548
+
549
+ # Orientation
550
+ - setOrientation: LANDSCAPE
551
+ - setOrientation: PORTRAIT
552
+
553
+ # Clipboard
554
+ - setClipboard: "Pasted content"
555
+ - pasteText
556
+ ```
557
+
558
+ ## Best Practices
559
+
560
+ ### 1. Use Stable Selectors
561
+
562
+ ```yaml
563
+ # ✅ Good: Use accessibility IDs
564
+ - tapOn:
565
+ id: "submit_button"
566
+
567
+ # ✅ Good: Use stable text
568
+ - tapOn: "Sign In"
569
+
570
+ # ⚠️ Avoid: Index unless necessary
571
+ - tapOn:
572
+ text: "Button"
573
+ index: 3
574
+
575
+ # ❌ Bad: Avoid coordinates
576
+ - tapOn:
577
+ point: "150, 300"
578
+ ```
579
+
580
+ ### 2. Wait for Async Operations
581
+
582
+ ```yaml
583
+ # ✅ Good: Use assertVisible before interaction
584
+ - assertVisible:
585
+ id: "loaded_content"
586
+ - tapOn: "Continue"
587
+
588
+ # ✅ Good: Use extendedWaitUntil for long operations
589
+ - tapOn: "Submit Order"
590
+ - extendedWaitUntil:
591
+ visible: "Order Confirmed"
592
+ timeout: 60000
593
+ ```
594
+
595
+ ### 3. Clear State for Isolation
596
+
597
+ ```yaml
598
+ # ✅ Good: Start fresh
599
+ - launchApp:
600
+ clearState: true
601
+ ```
602
+
603
+ ### 4. Use Descriptive Flow Names
604
+
605
+ ```yaml
606
+ # ✅ Good naming
607
+ appId: com.example.app
608
+ name: "User can complete checkout with credit card"
609
+ tags:
610
+ - checkout
611
+ - payment
612
+ - smoke
613
+ ```
614
+
615
+ ### 5. Create Reusable Subflows
616
+
617
+ ```yaml
618
+ # ✅ Good: Extract common sequences
619
+ - runFlow: ../subflows/login-as-user.yaml
620
+ - runFlow: ../subflows/navigate-to-settings.yaml
621
+ ```
622
+
623
+ ### 6. Handle Dynamic Content
624
+
625
+ ```yaml
626
+ # ✅ Good: Use regex for dynamic text
627
+ - assertVisible: "Order #[0-9]+"
628
+ - assertVisible: "Welcome, .*"
629
+
630
+ # ✅ Good: Use relative selectors
631
+ - tapOn:
632
+ below: "Shipping Address"
633
+ id: "edit_button"
634
+ ```
635
+
636
+ ## Running Tests
637
+
638
+ ```bash
639
+ # Run single flow
640
+ maestro test flows/login.yaml
641
+
642
+ # Run all flows in directory
643
+ maestro test .maestro/flows/
644
+
645
+ # Run with specific config
646
+ maestro test --config .maestro/config.yaml .maestro/flows/
647
+
648
+ # Run with tags
649
+ maestro test --include-tags=smoke --exclude-tags=flaky .maestro/
650
+
651
+ # Continuous mode (re-run on file changes)
652
+ maestro test --continuous flows/login.yaml
653
+
654
+ # Debug mode with hierarchy viewer
655
+ maestro hierarchy
656
+
657
+ # Interactive studio
658
+ maestro studio
659
+ ```
660
+
661
+ ## Common Mistakes to Avoid
662
+
663
+ | Mistake | Problem | Solution |
664
+ |---------|---------|----------|
665
+ | Using coordinates | Breaks on different devices | Use `id` or `text` selectors |
666
+ | Not clearing state | Tests depend on previous state | Use `clearState: true` |
667
+ | Hardcoding wait times | Flaky or slow tests | Use `assertVisible` or `extendedWaitUntil` |
668
+ | Duplicate selectors | Wrong element tapped | Use relative selectors or `index` |
669
+ | Long monolithic flows | Hard to maintain | Break into subflows |
670
+ | Not using tags | Can't run subsets | Tag flows by category |
671
+
672
+ ## Web Testing (Chrome)
673
+
674
+ ```yaml
675
+ appId: com.google.chrome
676
+ ---
677
+ - launchApp
678
+ - openLink: "https://example.com"
679
+ - assertVisible: "Example Domain"
680
+ - tapOn: "More information..."
681
+ ```
682
+
683
+ > [!NOTE]
684
+ > For web testing, Maestro uses Chrome and interacts with the DOM through accessibility APIs. Some complex SPAs may require `waitForAnimationToEnd` after navigation.
685
+
686
+ ## References
687
+
688
+ - [Maestro Documentation](https://docs.maestro.dev/)
689
+ - [Commands Reference](https://docs.maestro.dev/api-reference/commands)
690
+ - [Selectors Reference](https://docs.maestro.dev/api-reference/selectors)
691
+ - [Workspace Configuration](https://docs.maestro.dev/api-reference/configuration/workspace-configuration)