diff --git a/docs/superpowers/specs/2026-05-14-build-skill-design.md b/docs/superpowers/specs/2026-05-14-build-skill-design.md new file mode 100644 index 0000000..376dabd --- /dev/null +++ b/docs/superpowers/specs/2026-05-14-build-skill-design.md @@ -0,0 +1,242 @@ +# Design Spec: /build Skill — Universal Feature Implementation Command + +**Date:** 2026-05-14 +**Status:** Draft — Awaiting user review +**Scope:** A single Cursor slash command that orchestrates the full feature development lifecycle + +--- + +## 1. Problem Statement + +Today, implementing any feature (notifications, auth, payments, etc.) requires the user to manually: +- Specify TDD in the prompt +- Remember to include integration test scenarios +- Know which external services need configuring +- Know which files to touch for their specific stack +- Invoke the right skills in the right order + +This leads to inconsistent implementations, missed test scenarios, and forgotten native setup steps. + +**Goal:** One command — `/build implement [anything]` — that handles everything from research through PR, following every rule, skill, hook, and architecture pattern defined in the project. + +--- + +## 2. Decision + +**Form:** Cursor slash command skill file (`.md.tmpl` inside the generator's template system) +**Scope:** Universal + context-aware (reads `project-brief.yaml` to adapt behavior) +**Integration test gate:** Auto-generate `integration_test/` files + pause for device run + +--- + +## 3. Architecture + +### 3.1 File Placement + +``` +flutter-cursor-templates/ + generator/ + templates/ + skills/ + build/ + SKILL.md.tmpl ← The skill (single new file) + test/ + golden/ + bloc-clean-firebase/skills/build/SKILL.md + riverpod-ff-supabase/skills/build/SKILL.md + getx-mvc-rest/skills/build/SKILL.md +``` + +The generator renders this to `.cursor/skills/build/SKILL.md` in the user's project — same pattern as every other skill. + +### 3.2 Template Variables + +| Placeholder | Source in project-brief.yaml | +|---|---| +| `{{PROJECT_NAME}}` | `project.name` | +| `{{ARCHITECTURE}}` | `stack.architecture` | +| `{{STATE_MANAGEMENT}}` | `stack.state_management` | +| `{{ROUTING}}` | `stack.routing` | +| `{{BACKEND}}` | `stack.backend` | +| `{{PLATFORMS_LIST}}` | `stack.platforms` | +| `{{CODEGEN_LIST}}` | `stack.codegen` | +| `{{TESTING_DEPTH}}` | `testing.depth` | +| `{{E2E_TOOL}}` | `testing.e2e_tool` | +| `{{FEATURES_LIST}}` | `features.modules` | +| `{{SPECIAL_FEATURES}}` | `features.special` | +| `{{FLAVORS_LIST}}` | `environments.flavors` | +| `{{CICD_TOOL}}` | `environments.cicd` | +| `{{PACKAGE_ID}}` | `project.package` | + +Conditional blocks: `{{#if backend_firebase}}`, `{{#if platforms_ios}}`, `{{#if platforms_android}}`, `{{#if codegen_freezed}}`, `{{#if codegen_injectable}}`, `{{#if special_push_notifications}}`, `{{#if special_deep_linking}}` + +--- + +## 4. The 7 Phases + +### Phase 1: Context Loading +1. Read `project-brief.yaml` — extract stack, platforms, existing features +2. Parse user's request → match against FEATURE_REGISTRY keywords +3. Print context summary table (feature type, stack, platforms, rules loaded) +4. If feature already in `features.modules`, confirm extending vs. rebuilding + +### Phase 2: Deep Research +1. Enumerate required packages + correct versions for current Flutter stable +2. Build complete file manifest: new files (by architecture pattern) + modified files +3. List all external service configuration required per platform +4. Print research results before touching any file + +### Phase 3: TDD Implementation +**Invokes: `superpowers:test-driven-development`** + +- **Red:** Write all failing unit + widget tests from FEATURE_REGISTRY scenarios. Run to confirm red. Show actual output. +- **Green:** Implement domain → data → presentation layers. Run tests to green. Show actual output. +- **Refactor:** Clean up per `flutter-core.mdc`. Run `flutter analyze`. Show output. + +### Phase 4: Integration Test Generation +1. Create `integration_test/[feature_type]/` directory +2. Generate one test file per logical scenario cluster +3. Each file: standard boilerplate with `IntegrationTestWidgetsFlutterBinding` (or Patrol if `{{E2E_TOOL}} = patrol`) +4. Generate `integration_test/[feature]/README.md` — test matrix with run commands + +**PAUSE GATE:** Print table of all integration test files, what they cover, whether hardware is required, and the exact `flutter test` commands. Wait for user to paste device output before Phase 6. + +### Phase 5: External Setup Checklist +- Grouped by service/platform (Firebase, iOS, Android, backend) +- iOS/Android: table format — Step | Where | What | How to Verify +- Console steps: numbered checklist with exact menu paths and URLs + +### Phase 6: Verification Gate +**Invokes: `superpowers:verification-before-completion`** + +Required evidence (real output pasted by user): +- `flutter test test/features/[feature]/ --no-pub` → all tests pass +- `flutter analyze` → no issues +- Integration test device output from Phase 4 +- `lefthook run pre-commit` → all hooks pass + +If any failure: invoke `superpowers:systematic-debugging` before retrying. + +### Phase 7: PR Preparation +**Invokes: `superpowers:finishing-a-development-branch`** + +- Conventional commit: `feat([feature_type]): implement [feature] end-to-end` +- PR description: what built, test count, integration test matrix, external setup status +- CI/CD advice for `{{CICD_TOOL}}` (secret scoping per flavor) + +--- + +## 5. FEATURE_REGISTRY + +Seven feature types with full metadata: + +| Feature Type | Keywords (sample) | Packages | Integration Test Scenarios | +|---|---|---|---| +| `notifications` | notification, push, FCM, APNs | firebase_messaging, flutter_local_notifications | foreground/background/killed receipt, tap redirect in all 3 states, payload parsing | +| `auth` | login, sign in, biometric, OAuth, JWT | firebase_auth, local_auth, flutter_secure_storage | full login flow, biometric + fallback, token refresh, cold start with session | +| `payments` | payment, Stripe, IAP, checkout | flutter_stripe, purchases_flutter | test-card checkout, 3DS, Apple/Google Pay, subscription restore | +| `deep_links` | deep link, universal link, app link | app_links, go_router | cold/background/foreground link handling, auth-guarded routes | +| `analytics` | analytics, event, tracking, screen view | firebase_analytics, mixpanel_flutter | event triggered on action, screen view on nav, opt-out disables tracking | +| `storage` | file upload, Firebase Storage, Hive, Isar | firebase_storage, isar, drift | upload + retrieve, offline cache, background upload restore | +| `camera_media` | camera, photo, QR, barcode, video | camera, image_picker, mobile_scanner | photo capture, gallery pick, QR decode, permission denied UI | + +Each entry also specifies: `rules_to_load`, `files_to_touch` (new + modified), `external_setup` (per service/platform), `unit_test_scenarios`, `widget_test_scenarios`. + +--- + +## 6. Integration Test Template Structure + +```dart +// integration_test/[feature]/[scenario]_test.dart +// Generated by /build — {{PROJECT_NAME}} +// Run: flutter test integration_test/[feature]/[scenario]_test.dart -d + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:{{PACKAGE_ID}}/main.dart' as app; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group('[FeatureType] — [Scenario Name]', () { + setUp(() async { /* seed state / reset storage */ }); + tearDown(() async { /* cleanup */ }); + + testWidgets( + 'given [precondition], when [action], then [expected outcome]', + (tester) async { + app.main(); + await tester.pumpAndSettle(); + // test body + }, + ); + }); +} +``` + +For `{{E2E_TOOL}} = patrol`: imports swap to `package:patrol`, uses `$` Patrol selector syntax. + +--- + +## 7. Skill File Sections (exact headings) + +``` +# Build — {{PROJECT_NAME}} +## Usage +## FEATURE_REGISTRY +## Phase 1: Context Loading +## Phase 2: Deep Research +## Phase 3: TDD Implementation +## Phase 4: Integration Test Generation +## Phase 5: External Setup Checklist +## Phase 6: Verification Gate +## Phase 7: PR Preparation +## Rules applied every phase +## Code generation notes +``` + +--- + +## 8. Cross-Skill Orchestration + +The `/build` skill orchestrates existing skills internally: +- Invokes `/scaffold-feature` patterns to create the initial file skeleton (Phase 3b) +- Borrows `/generate-tests` naming and structure conventions (Phase 3a) +- Invokes `/generate-api-client` if `api_docs.format != none` and feature needs API (Phase 2) +- References `/deploy` checklist for flavor-scoped secret handling (Phase 7) + +--- + +## 9. Rules Applied Every Phase + +Always active (no conditional): +- `.cursor/rules/flutter-core.mdc` +- `.cursor/rules/security-standards.mdc` +- `.cursor/rules/project-context.mdc` +- Feature-type rules from `FEATURE_REGISTRY.rules_to_load` + +--- + +## 10. Enhancements / Out of Scope for V1 + +- Reference project URL fetching and pattern extraction (Phase 1) — included in V1 +- Multi-feature requests spanning 2+ feature types — detect and prompt decomposition +- Automatic CI secret rotation advice — Phase 7, advisory only +- Web platform integration test support — flagged as TODO in generated README +- Visual companion for architecture diagrams — post-V1 + +--- + +## 11. Verification + +End-to-end test of the skill: + +1. Run `cursor_gen --wizard` on a test project → choose `bloc/clean/firebase` stack +2. Confirm `.cursor/skills/build/SKILL.md` is generated +3. In Cursor, type `/build implement notification module end-to-end` +4. Verify Phase 1 prints correct context table from project-brief.yaml +5. Verify Phase 3 tests are red before implementation, green after +6. Verify Phase 4 generates `integration_test/notifications/` with 10 test files +7. Verify Phase 5 checklist lists Firebase Console + APNs steps +8. Verify Phase 6 requires real output before proceeding +9. Run golden tests: `dart test test/generator_test.dart` — all stacks must match golden SKILL.md diff --git a/flutter-cursor-templates/generator/src/resolver.dart b/flutter-cursor-templates/generator/src/resolver.dart index 09c1ec1..f38a14d 100644 --- a/flutter-cursor-templates/generator/src/resolver.dart +++ b/flutter-cursor-templates/generator/src/resolver.dart @@ -72,6 +72,7 @@ class Resolver { 'skills/scaffold-feature', 'skills/scaffold-screen', 'skills/generate-tests', + 'skills/build', ]); if (brief.apiDocsFormat != 'none') files.add('skills/generate-api-client'); if (brief.flavors.length > 1) files.add('skills/create-flavor'); @@ -123,6 +124,7 @@ class Resolver { if (key.contains('i18n')) return 'localization.enabled: true'; if (key.contains('migration')) return 'state_management is GetX — migration guidance included'; if (key.contains('security-agent')) return 'scale: ${brief.scale} or auth is configured'; + if (key == 'skills/build') return 'Always included — universal TDD-first feature implementation command'; if (key.contains('api-client')) return 'api_docs.format is set'; if (key.contains('realtime')) return 'features.special contains realtime'; return 'Included'; diff --git a/flutter-cursor-templates/generator/templates/skills/build/SKILL.md.tmpl b/flutter-cursor-templates/generator/templates/skills/build/SKILL.md.tmpl new file mode 100644 index 0000000..36afbd1 --- /dev/null +++ b/flutter-cursor-templates/generator/templates/skills/build/SKILL.md.tmpl @@ -0,0 +1,568 @@ +# Build — {{PROJECT_NAME}} + +Implements any feature end-to-end: deep research → TDD → integration tests → external setup checklist → verified PR. +Stack: **{{STATE_MANAGEMENT}}** / **{{ARCHITECTURE}}** / **{{BACKEND}}** / {{PLATFORMS_LIST}}. + +## Usage + +``` +/build +``` + +**Examples:** +- `/build implement notification module end-to-end` +- `/build add biometric auth` +- `/build integrate Stripe payments` +- `/build add deep linking for shared product pages` +- `/build implement analytics event tracking` + +--- + +## FEATURE_REGISTRY + +The AI reads this registry to classify the request and load the right research, test scenarios, and setup steps. + +```yaml +feature_registry: + + notifications: + keywords: [notification, push, fcm, apns, alert, badge, silent push, + remote notification, local notification, firebase messaging] + pub_packages: [firebase_messaging, flutter_local_notifications, firebase_core] + rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc] + unit_test_scenarios: + - parse notification payload to domain model + - FCM token generation and storage + - token refresh triggers re-registration + - notification permission denied returns graceful fallback + - notification service initialisation is idempotent + widget_test_scenarios: + - notification banner renders correct title and body + - tap on notification navigates to correct route + - badge count updates on new message + integration_test_scenarios: + - foreground notification receipt and display + - background notification receipt (app backgrounded, not killed) + - killed-state notification receipt (cold launch from notification tap) + - tap-to-open in foreground state routes correctly + - tap-to-open in background state routes correctly + - tap-to-open in killed state routes correctly + - notification payload parsing end-to-end + - deep link routing from notification data field + - multiple simultaneous notifications (ordering and dedup) + external_setup: + firebase: [Enable Cloud Messaging, download google-services.json, download GoogleService-Info.plist] + ios: [Push Notifications capability, Background Modes remote notifications, APNs .p8 key upload to Firebase] + android: [POST_NOTIFICATIONS permission, RECEIVE_BOOT_COMPLETED permission, google-services plugin] + files_to_touch: + new: [lib/features/notifications/, integration_test/notifications/] + modified: [lib/main.dart, android/app/src/main/AndroidManifest.xml, ios/Runner/Info.plist, ios/Runner/AppDelegate.swift, pubspec.yaml] + + auth: + keywords: [auth, authentication, login, sign in, sign out, logout, + biometric, face id, touch id, fingerprint, oauth, jwt, + session, token refresh, social login, google sign-in] + pub_packages: [firebase_auth, supabase_flutter, local_auth, flutter_secure_storage, google_sign_in] + rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc] + unit_test_scenarios: + - login success stores token securely + - login failure (wrong password) returns typed error + - login failure (network) returns typed error + - token refresh succeeds and updates stored token + - token refresh failure triggers logout + - biometric auth success grants access + - biometric auth failure falls back to password + - biometric not enrolled returns correct error state + - logout clears all stored credentials + - session persistence: token loaded on cold start + widget_test_scenarios: + - login form renders correctly + - validation errors shown inline + - loading state disables submit button + - biometric prompt shown when available + integration_test_scenarios: + - complete login flow (email and password) + - login then logout then login again + - invalid credentials shows error and stays on login + - biometric login flow on enrolled device + - biometric fallback to password + - token refresh in background while user navigates + - cold start with stored valid session skips login + - cold start with expired session redirects to login + - social auth OAuth redirect and return + - deep link into protected route redirects to login then back + external_setup: + firebase: [Enable Email/Password provider, Enable Google provider, download updated config files] + ios: [NSFaceIDUsageDescription in Info.plist, LocalAuthentication.framework in Xcode] + android: [USE_BIOMETRIC permission, USE_FINGERPRINT permission, minSdkVersion 23 for biometric] + files_to_touch: + new: [lib/features/auth/, integration_test/auth/] + modified: [lib/main.dart, lib/core/di/injection.dart, pubspec.yaml] + + payments: + keywords: [payment, stripe, in-app purchase, iap, checkout, subscription, + billing, apple pay, google pay, card, transaction, refund, revenue cat] + pub_packages: [flutter_stripe, purchases_flutter, in_app_purchase] + rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc] + unit_test_scenarios: + - checkout request builds correct PaymentIntent params + - payment success updates order state + - payment failure (card declined) returns typed error + - webhook event parsed to domain model + - refund request constructs correct API call + - subscription status checked on app resume + widget_test_scenarios: + - checkout form renders with correct amount + - payment loading state shown during processing + - success confirmation screen renders + - error state with retry option + integration_test_scenarios: + - end-to-end checkout with test card (Stripe test mode) + - checkout with 3D Secure challenge + - card declined shows error then retry succeeds + - Apple Pay / Google Pay sheet appears (device capability check) + - subscription purchase and entitlement unlock + - subscription restore flow + - refund flow from order history + external_setup: + stripe: [publishable and secret keys in flavor .env files, webhook endpoint in Stripe Dashboard, Apple Pay domain registration] + ios: [In-App Purchase capability, Apple Pay capability and merchant identifier, com.apple.developer.in-app-payments entitlement] + android: [Google Pay Console setup, BILLING permission for in_app_purchase] + files_to_touch: + new: [lib/features/payments/, integration_test/payments/] + modified: [ios/Runner/Runner.entitlements, android/app/src/main/AndroidManifest.xml, pubspec.yaml] + + deep_links: + keywords: [deep link, deep linking, universal link, app link, deferred deep link, + dynamic link, branch, uri scheme, custom scheme, url scheme] + pub_packages: [go_router, app_links, uni_links] + rules_to_load: [platform-ios.mdc, platform-android.mdc] + unit_test_scenarios: + - URI parsed to correct route and parameters + - unknown URI falls back to home + - authenticated-only route redirects to login when unauthenticated + - deep link preserves query parameters + widget_test_scenarios: + - navigation guard redirects unauthenticated deep link + integration_test_scenarios: + - cold start from Universal Link routes to correct screen + - cold start from custom URI scheme routes correctly + - backgrounded app receives deep link and navigates + - foreground app receives deep link and navigates + - deep link to authenticated route redirects to login then original destination + - deep link with path parameters loads correct content + - invalid or malformed deep link shows 404 screen + external_setup: + ios: [Associated Domains capability applinks:yourdomain.com, host apple-app-site-association file at /.well-known/] + android: [intent-filter with android:autoVerify=true in AndroidManifest.xml, host assetlinks.json at /.well-known/] + files_to_touch: + new: [lib/core/routing/deep_link_handler.dart, integration_test/deep_links/] + modified: [lib/core/routing/router.dart, android/app/src/main/AndroidManifest.xml, ios/Runner/Runner.entitlements, ios/Runner/Info.plist] + + analytics: + keywords: [analytics, tracking, event, mixpanel, amplitude, firebase analytics, + segment, posthog, screen view, funnel, cohort] + pub_packages: [firebase_analytics, mixpanel_flutter, amplitude_flutter, posthog_flutter] + rules_to_load: [security-standards.mdc] + unit_test_scenarios: + - analytics service logs correct event name and params + - PII fields stripped before event sent + - analytics disabled in dev flavor + - screen name logged on navigation + widget_test_scenarios: + - RouteObserver triggers screen_view event + integration_test_scenarios: + - user action triggers expected event (verify via debug view) + - screen transitions log screen_view with correct names + - opt-out disables all tracking + external_setup: + firebase: [Enable Analytics in Firebase Console, enable DebugView for local testing, configure conversion events] + files_to_touch: + new: [lib/core/analytics/analytics_service.dart, lib/core/analytics/analytics_events.dart, integration_test/analytics/] + modified: [lib/main.dart, pubspec.yaml] + + storage: + keywords: [storage, file upload, download, cloud storage, firebase storage, + supabase storage, s3, image upload, document, file picker, + offline, cache, hive, isar, objectbox, sqflite, drift] + pub_packages: [firebase_storage, supabase_flutter, hive_flutter, isar, drift, file_picker, image_picker, path_provider] + rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc] + unit_test_scenarios: + - upload returns public URL on success + - upload failure returns typed error + - local cache read before remote fetch (offline-first) + - cache invalidation on TTL expiry + - large file upload uses resumable upload + widget_test_scenarios: + - file picker button triggers picker + - upload progress indicator shown + - image preview renders after selection + integration_test_scenarios: + - end-to-end file upload and retrieval + - offline mode: local cache serves data + - background upload completes when connectivity restored + - file size limit enforced + external_setup: + firebase: [Enable Firebase Storage, configure Storage security rules, set CORS policy for web if applicable] + ios: [NSPhotoLibraryUsageDescription in Info.plist, NSCameraUsageDescription in Info.plist] + android: [READ_EXTERNAL_STORAGE or READ_MEDIA_IMAGES permission] + files_to_touch: + new: [lib/features/storage/, integration_test/storage/] + modified: [android/app/src/main/AndroidManifest.xml, ios/Runner/Info.plist, pubspec.yaml] + + camera_media: + keywords: [camera, photo, video, image picker, qr code, barcode scanner, + ar, augmented reality, gallery, media, record, capture] + pub_packages: [camera, image_picker, mobile_scanner, qr_flutter, image_cropper, video_player] + rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc] + unit_test_scenarios: + - QR/barcode parsed to correct domain model + - image compressed before upload + - camera permission denied returns typed error + widget_test_scenarios: + - camera preview widget renders + - capture button triggers photo take + integration_test_scenarios: + - camera opens and captures photo on real device + - gallery picker selects image and returns + - QR scan decodes valid code correctly + - permission denied shows correct error UI + external_setup: + ios: [NSCameraUsageDescription, NSPhotoLibraryUsageDescription, NSMicrophoneUsageDescription for video] + android: [CAMERA permission, READ_MEDIA_IMAGES for gallery access] + files_to_touch: + new: [lib/features/camera/, integration_test/camera/] + modified: [android/app/src/main/AndroidManifest.xml, ios/Runner/Info.plist, pubspec.yaml] +``` + +--- + +## Phase 1: Context Loading + +**When the user types `/build `, do the following before writing a single line of code:** + +1. Read `project-brief.yaml` from the repo root. Extract and hold in context: + - `stack.state_management` → **{{STATE_MGMT_RAW}}** + - `stack.architecture` → **{{ARCH_RAW}}** + - `stack.routing` → **{{ROUTING_RAW}}** + - `stack.backend` → **{{BACKENDS_LIST}}** + - `stack.platforms` → **{{PLATFORMS_LIST}}** + - `stack.codegen` → **{{CODEGEN_LIST}}** + - `testing.depth` → **{{TESTING_DEPTH}}** + - `testing.e2e_tool` → **{{E2E_TOOL}}** + - `features.modules` (existing features) → **{{FEATURES_LIST}}** + - `features.special` → **{{SPECIAL_FEATURES}}** + - `environments.flavors` → **{{FLAVORS_LIST}}** + - `environments.cicd` → **{{CICD_RAW}}** + +2. Parse the user's free-text request. Match against `FEATURE_REGISTRY` keywords using longest-match. Multiple types are allowed if the request spans features. + +3. Load `.cursor/rules/` files listed under `rules_to_load` for the matched feature type. + +4. If the user's message contains a URL or GitHub repo reference, fetch and index it for pattern reference. + +5. Check `features.modules` — if the feature already exists in the list, ask: "This feature is already listed in project-brief.yaml. Building additional capability on top of it? (y/n)" + +6. Print the context summary table before proceeding: + +``` +| Field | Value | +|------------------|------------------------------| +| Feature type | [detected type] | +| State management | {{STATE_MANAGEMENT}} | +| Architecture | {{ARCHITECTURE}} | +| Backend | {{BACKEND}} | +| Platforms | {{PLATFORMS_LIST}} | +| E2E tool | {{E2E_TOOL}} | +| Rules loaded | [list from rules_to_load] | +| Existing modules | {{FEATURES_LIST}} | +``` + +--- + +## Phase 2: Deep Research + +**Before touching any file, print the full research output.** + +1. From the FEATURE_REGISTRY, enumerate all `pub_packages` for the detected type. For each, determine the correct version compatible with current Flutter stable (`flutter --version`). Print the full `pubspec.yaml` additions. + +2. Build the complete file manifest, adapting to `{{ARCHITECTURE}}`: + + **For {{ARCHITECTURE}} ({{ARCH_RAW}}):** + - `clean`: expand each feature layer explicitly → `domain/entities/`, `domain/repositories/`, `domain/usecases/`, `data/models/`, `data/datasources/`, `data/repositories/`, `presentation/` + - `feature_first`: `[feature]/[feature]_screen.dart`, `[feature]_provider.dart` or `[feature]_bloc.dart`, `[feature]_repository.dart`, `[feature]_model.dart`, `widgets/` + - `mvc`: `[feature]/model/`, `[feature]/view/`, `[feature]/controller/` + + **State management file naming ({{STATE_MANAGEMENT}} / {{STATE_MGMT_RAW}}):** + - `bloc`: `[feature]_bloc.dart`, `[feature]_event.dart`, `[feature]_state.dart` + - `riverpod`: `[feature]_provider.dart`, `[feature]_notifier.dart` + - `getx`: `[feature]_controller.dart`, `[feature]_binding.dart` + +3. List all modified files from `files_to_touch.modified` — show exactly what change goes in each file. + +4. List all external service configuration required per platform (from `external_setup`). Only show platforms present in `{{PLATFORMS_LIST}}`. + +5. Print research results in this format: + +``` +## Research Results + +### Packages to add to pubspec.yaml +- [package_name]: ^[version] + +### Files to create ({{ARCHITECTURE}} / {{STATE_MANAGEMENT}}) +lib/features/[name]/... (full list) + +### Files to modify +[file_path] — [what changes] + +### External services requiring configuration +- [Service] ([platform]) — [what to do] +``` + +Do not touch any file until the user has seen this output. + +--- + +## Phase 3: TDD Implementation + +> **Invoke skill: `superpowers:test-driven-development`** + +Follow Red → Green → Refactor strictly. Show actual terminal output at each step. + +### Step 3a — Red: Write all failing tests first + +1. Mirror the feature directory under `test/features/[feature]/`. +2. Write unit tests for **every scenario** in `FEATURE_REGISTRY.unit_test_scenarios` for the detected type. +3. Write widget tests for **every scenario** in `FEATURE_REGISTRY.widget_test_scenarios`. +4. Use `mocktail` for all dependencies. +5. Test naming convention: `'given [precondition], when [action], then [expected outcome]'` +6. Use the state management test pattern for **{{STATE_MANAGEMENT}}**: + ``` + {{TEST_PATTERN}} + ``` +7. Run and confirm red: + ``` + flutter test test/features/[feature]/ --no-pub + ``` + **Paste the actual output here before proceeding.** + +### Step 3b — Green: Implement + +1. Create domain entities and repository interfaces. +2. Create data-layer implementations wiring to `{{BACKEND}}`. +3. Create presentation layer using **{{STATE_MANAGEMENT}}** patterns. +4. Register in DI container — follow `{{ARCH_IMPORT_RULES}}`. +5. Wire the route in `{{ROUTING}}` router. +6. If `{{CODEGEN_LIST}}` is not `none`: run `dart run build_runner build --delete-conflicting-outputs` after adding models. +7. Run tests and confirm green: + ``` + flutter test test/features/[feature]/ --no-pub + ``` + **Paste the actual output here before proceeding.** + +### Step 3c — Refactor + +1. Review for duplication, naming, and layer boundary violations per `.cursor/rules/flutter-core.mdc`. +2. Run and confirm clean: + ``` + flutter analyze + ``` + **Paste the actual output here before proceeding.** +3. Run tests once more to confirm still green. + +--- + +## Phase 4: Integration Test Generation + +**Generate `integration_test/[feature_type]/` before asking the user to run anything.** + +### Integration test file structure + +Each test file follows this template: + +```dart +// integration_test/[feature]/[scenario]_test.dart +// Generated by /build — {{PROJECT_NAME}} +// Feature: [feature_type] | Scenario: [scenario_name] +// Run with: flutter test integration_test/[feature]/[scenario]_test.dart -d + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:{{PACKAGE_ID}}/main.dart' as app; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group('[FeatureType] — [Scenario Name]', () { + setUp(() async { + // Seed state / configure mocks / reset storage + }); + + tearDown(() async { + // Cleanup + }); + + testWidgets( + 'given [precondition], when [action], then [expected outcome]', + (tester) async { + app.main(); + await tester.pumpAndSettle(); + // test body + }, + ); + }); +} +``` + +**For `{{E2E_TOOL}}` (patrol):** swap `flutter_test` imports for `package:patrol` and use Patrol's `$` NativeAutomator selector syntax. + +### Files to generate + +Create one file per logical scenario cluster from `FEATURE_REGISTRY.integration_test_scenarios`. +Also generate `integration_test/[feature]/README.md` with the full test matrix and run commands. + +### PAUSE GATE — user must run on device + +After generating all files, print this table and **stop**. Wait for the user to paste device output before Phase 6. + +``` +## ACTION REQUIRED — Run Integration Tests on Real Device + +| File | Covers | Requires Hardware | +|------|--------|------------------| +| integration_test/[feature]/[scenario]_test.dart | [what it covers] | Yes/No | +... + +Run on iOS: + flutter test integration_test/[feature]/ -d + +Run on Android: + flutter test integration_test/[feature]/ -d + +Paste the output here to continue. +``` + +--- + +## Phase 5: External Setup Checklist + +**Print before asking the user to verify anything. Group by service/platform. Only show sections for platforms in `{{PLATFORMS_LIST}}`.** + +### Format for console/service steps (numbered list): + +```markdown +### Firebase (backend: {{BACKEND}}) +- [ ] 1. Open Firebase Console → [exact menu path] +- [ ] 2. [Specific action] +- [ ] 3. Download updated config file → place at [exact path] +``` + +### Format for native platform steps (table): + +```markdown +### iOS Setup +| Step | Where | What | How to Verify | +|------|-------|------|---------------| +| [Step name] | [Xcode location / file path] | [Exact change] | [Verification method] | +``` + +```markdown +### Android Setup +- [ ] 1. Open `android/app/src/main/AndroidManifest.xml` + - Add: `` +- [ ] 2. [Next step with exact value] +``` + +### Flavor scoping note (flavors: {{FLAVORS_LIST}}) + +Config files and API keys must be scoped per flavor. Never place production keys in `dev` flavor files. Follow the pattern established in `.cursor/rules/` for flavor-based configuration. + +--- + +## Phase 6: Verification Gate + +> **Invoke skill: `superpowers:verification-before-completion`** + +**Do not claim completion without pasting real output for every item below.** + +``` +VERIFICATION REQUIRED — paste real output for each: + +[ ] flutter test test/features/[feature]/ --no-pub + Required: "All N tests passed." + +[ ] flutter analyze + Required: "No issues found!" + +[ ] Integration test device output (from Phase 4 pause gate) + Required: actual device test output + +[ ] lefthook run pre-commit + Required: all hooks passed +``` + +If any check fails: invoke `superpowers:systematic-debugging` to diagnose before retrying. +Do not proceed to Phase 7 until all four checks are green. + +--- + +## Phase 7: PR Preparation + +> **Invoke skill: `superpowers:finishing-a-development-branch`** + +1. **Commit message** (conventional commits format): + ``` + feat([feature_type]): implement [feature] end-to-end + ``` + +2. **PR description template:** + ```markdown + ## What + [One sentence: what feature was implemented] + + ## Test coverage + - Unit/widget tests: N passing + - Integration test matrix: + | Scenario | iOS | Android | + |----------|-----|---------| + | [scenario] | ✅ | ✅ | + + ## External setup completed + - [ ] [Service 1 setup] + - [ ] [Service 2 setup] + + ## Notes + [Any follow-up TODOs or known limitations] + ``` + +3. **CI/CD advice for {{CICD_TOOL}} ({{CICD_RAW}}):** + Ensure workflow secrets are scoped per flavor ({{FLAVORS_LIST}}). Never expose production keys in dev environment secrets. Verify the CI workflow runs `flutter test` and `flutter analyze` before deploy steps. + +--- + +## Rules applied every phase + +Always active — read before writing any code: +- `.cursor/rules/flutter-core.mdc` +- `.cursor/rules/security-standards.mdc` +- `.cursor/rules/project-context.mdc` +- Feature-type-specific rules from `FEATURE_REGISTRY.rules_to_load` + +Architecture import rules for **{{ARCHITECTURE}}**: +{{ARCH_IMPORT_RULES}} + +--- + +## Code generation notes + +**Codegen tools configured: {{CODEGEN_LIST}}** + +After adding any new model or injectable class, run: +``` +dart run build_runner build --delete-conflicting-outputs +``` + +Commit generated files (`.g.dart`, `.freezed.dart`, `injection.config.dart`) — do not gitignore them. + +**Template version:** {{TEMPLATE_VERSION}} diff --git a/flutter-cursor-templates/generator/test/generator_test.dart b/flutter-cursor-templates/generator/test/generator_test.dart index 7ee4789..b8a6efe 100644 --- a/flutter-cursor-templates/generator/test/generator_test.dart +++ b/flutter-cursor-templates/generator/test/generator_test.dart @@ -243,6 +243,12 @@ void main() { expect(files.length, equals(unique.length), reason: 'Duplicate template files detected'); }); + + test('build skill always included in resolved list', () { + expect(Resolver.resolve(_blocCleanBrief), contains('skills/build')); + expect(Resolver.resolve(_riverpodFFBrief), contains('skills/build')); + expect(Resolver.resolve(_getxMvcBrief), contains('skills/build')); + }); }); // ─── Renderer / placeholder tests ─────────────────────────────────────────── diff --git a/flutter-cursor-templates/generator/test/golden/bloc-clean-firebase/skills/build/SKILL.md b/flutter-cursor-templates/generator/test/golden/bloc-clean-firebase/skills/build/SKILL.md new file mode 100644 index 0000000..5c1a902 --- /dev/null +++ b/flutter-cursor-templates/generator/test/golden/bloc-clean-firebase/skills/build/SKILL.md @@ -0,0 +1,574 @@ +# Build — TestApp + +Implements any feature end-to-end: deep research → TDD → integration tests → external setup checklist → verified PR. +Stack: **BLoC / Cubit** / **Clean Architecture** / **Firebase** / ios, android. + +## Usage + +``` +/build +``` + +**Examples:** +- `/build implement notification module end-to-end` +- `/build add biometric auth` +- `/build integrate Stripe payments` +- `/build add deep linking for shared product pages` +- `/build implement analytics event tracking` + +--- + +## FEATURE_REGISTRY + +The AI reads this registry to classify the request and load the right research, test scenarios, and setup steps. + +```yaml +feature_registry: + + notifications: + keywords: [notification, push, fcm, apns, alert, badge, silent push, + remote notification, local notification, firebase messaging] + pub_packages: [firebase_messaging, flutter_local_notifications, firebase_core] + rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc] + unit_test_scenarios: + - parse notification payload to domain model + - FCM token generation and storage + - token refresh triggers re-registration + - notification permission denied returns graceful fallback + - notification service initialisation is idempotent + widget_test_scenarios: + - notification banner renders correct title and body + - tap on notification navigates to correct route + - badge count updates on new message + integration_test_scenarios: + - foreground notification receipt and display + - background notification receipt (app backgrounded, not killed) + - killed-state notification receipt (cold launch from notification tap) + - tap-to-open in foreground state routes correctly + - tap-to-open in background state routes correctly + - tap-to-open in killed state routes correctly + - notification payload parsing end-to-end + - deep link routing from notification data field + - multiple simultaneous notifications (ordering and dedup) + external_setup: + firebase: [Enable Cloud Messaging, download google-services.json, download GoogleService-Info.plist] + ios: [Push Notifications capability, Background Modes remote notifications, APNs .p8 key upload to Firebase] + android: [POST_NOTIFICATIONS permission, RECEIVE_BOOT_COMPLETED permission, google-services plugin] + files_to_touch: + new: [lib/features/notifications/, integration_test/notifications/] + modified: [lib/main.dart, android/app/src/main/AndroidManifest.xml, ios/Runner/Info.plist, ios/Runner/AppDelegate.swift, pubspec.yaml] + + auth: + keywords: [auth, authentication, login, sign in, sign out, logout, + biometric, face id, touch id, fingerprint, oauth, jwt, + session, token refresh, social login, google sign-in] + pub_packages: [firebase_auth, supabase_flutter, local_auth, flutter_secure_storage, google_sign_in] + rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc] + unit_test_scenarios: + - login success stores token securely + - login failure (wrong password) returns typed error + - login failure (network) returns typed error + - token refresh succeeds and updates stored token + - token refresh failure triggers logout + - biometric auth success grants access + - biometric auth failure falls back to password + - biometric not enrolled returns correct error state + - logout clears all stored credentials + - session persistence: token loaded on cold start + widget_test_scenarios: + - login form renders correctly + - validation errors shown inline + - loading state disables submit button + - biometric prompt shown when available + integration_test_scenarios: + - complete login flow (email and password) + - login then logout then login again + - invalid credentials shows error and stays on login + - biometric login flow on enrolled device + - biometric fallback to password + - token refresh in background while user navigates + - cold start with stored valid session skips login + - cold start with expired session redirects to login + - social auth OAuth redirect and return + - deep link into protected route redirects to login then back + external_setup: + firebase: [Enable Email/Password provider, Enable Google provider, download updated config files] + ios: [NSFaceIDUsageDescription in Info.plist, LocalAuthentication.framework in Xcode] + android: [USE_BIOMETRIC permission, USE_FINGERPRINT permission, minSdkVersion 23 for biometric] + files_to_touch: + new: [lib/features/auth/, integration_test/auth/] + modified: [lib/main.dart, lib/core/di/injection.dart, pubspec.yaml] + + payments: + keywords: [payment, stripe, in-app purchase, iap, checkout, subscription, + billing, apple pay, google pay, card, transaction, refund, revenue cat] + pub_packages: [flutter_stripe, purchases_flutter, in_app_purchase] + rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc] + unit_test_scenarios: + - checkout request builds correct PaymentIntent params + - payment success updates order state + - payment failure (card declined) returns typed error + - webhook event parsed to domain model + - refund request constructs correct API call + - subscription status checked on app resume + widget_test_scenarios: + - checkout form renders with correct amount + - payment loading state shown during processing + - success confirmation screen renders + - error state with retry option + integration_test_scenarios: + - end-to-end checkout with test card (Stripe test mode) + - checkout with 3D Secure challenge + - card declined shows error then retry succeeds + - Apple Pay / Google Pay sheet appears (device capability check) + - subscription purchase and entitlement unlock + - subscription restore flow + - refund flow from order history + external_setup: + stripe: [publishable and secret keys in flavor .env files, webhook endpoint in Stripe Dashboard, Apple Pay domain registration] + ios: [In-App Purchase capability, Apple Pay capability and merchant identifier, com.apple.developer.in-app-payments entitlement] + android: [Google Pay Console setup, BILLING permission for in_app_purchase] + files_to_touch: + new: [lib/features/payments/, integration_test/payments/] + modified: [ios/Runner/Runner.entitlements, android/app/src/main/AndroidManifest.xml, pubspec.yaml] + + deep_links: + keywords: [deep link, deep linking, universal link, app link, deferred deep link, + dynamic link, branch, uri scheme, custom scheme, url scheme] + pub_packages: [go_router, app_links, uni_links] + rules_to_load: [platform-ios.mdc, platform-android.mdc] + unit_test_scenarios: + - URI parsed to correct route and parameters + - unknown URI falls back to home + - authenticated-only route redirects to login when unauthenticated + - deep link preserves query parameters + widget_test_scenarios: + - navigation guard redirects unauthenticated deep link + integration_test_scenarios: + - cold start from Universal Link routes to correct screen + - cold start from custom URI scheme routes correctly + - backgrounded app receives deep link and navigates + - foreground app receives deep link and navigates + - deep link to authenticated route redirects to login then original destination + - deep link with path parameters loads correct content + - invalid or malformed deep link shows 404 screen + external_setup: + ios: [Associated Domains capability applinks:yourdomain.com, host apple-app-site-association file at /.well-known/] + android: [intent-filter with android:autoVerify=true in AndroidManifest.xml, host assetlinks.json at /.well-known/] + files_to_touch: + new: [lib/core/routing/deep_link_handler.dart, integration_test/deep_links/] + modified: [lib/core/routing/router.dart, android/app/src/main/AndroidManifest.xml, ios/Runner/Runner.entitlements, ios/Runner/Info.plist] + + analytics: + keywords: [analytics, tracking, event, mixpanel, amplitude, firebase analytics, + segment, posthog, screen view, funnel, cohort] + pub_packages: [firebase_analytics, mixpanel_flutter, amplitude_flutter, posthog_flutter] + rules_to_load: [security-standards.mdc] + unit_test_scenarios: + - analytics service logs correct event name and params + - PII fields stripped before event sent + - analytics disabled in dev flavor + - screen name logged on navigation + widget_test_scenarios: + - RouteObserver triggers screen_view event + integration_test_scenarios: + - user action triggers expected event (verify via debug view) + - screen transitions log screen_view with correct names + - opt-out disables all tracking + external_setup: + firebase: [Enable Analytics in Firebase Console, enable DebugView for local testing, configure conversion events] + files_to_touch: + new: [lib/core/analytics/analytics_service.dart, lib/core/analytics/analytics_events.dart, integration_test/analytics/] + modified: [lib/main.dart, pubspec.yaml] + + storage: + keywords: [storage, file upload, download, cloud storage, firebase storage, + supabase storage, s3, image upload, document, file picker, + offline, cache, hive, isar, objectbox, sqflite, drift] + pub_packages: [firebase_storage, supabase_flutter, hive_flutter, isar, drift, file_picker, image_picker, path_provider] + rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc] + unit_test_scenarios: + - upload returns public URL on success + - upload failure returns typed error + - local cache read before remote fetch (offline-first) + - cache invalidation on TTL expiry + - large file upload uses resumable upload + widget_test_scenarios: + - file picker button triggers picker + - upload progress indicator shown + - image preview renders after selection + integration_test_scenarios: + - end-to-end file upload and retrieval + - offline mode: local cache serves data + - background upload completes when connectivity restored + - file size limit enforced + external_setup: + firebase: [Enable Firebase Storage, configure Storage security rules, set CORS policy for web if applicable] + ios: [NSPhotoLibraryUsageDescription in Info.plist, NSCameraUsageDescription in Info.plist] + android: [READ_EXTERNAL_STORAGE or READ_MEDIA_IMAGES permission] + files_to_touch: + new: [lib/features/storage/, integration_test/storage/] + modified: [android/app/src/main/AndroidManifest.xml, ios/Runner/Info.plist, pubspec.yaml] + + camera_media: + keywords: [camera, photo, video, image picker, qr code, barcode scanner, + ar, augmented reality, gallery, media, record, capture] + pub_packages: [camera, image_picker, mobile_scanner, qr_flutter, image_cropper, video_player] + rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc] + unit_test_scenarios: + - QR/barcode parsed to correct domain model + - image compressed before upload + - camera permission denied returns typed error + widget_test_scenarios: + - camera preview widget renders + - capture button triggers photo take + integration_test_scenarios: + - camera opens and captures photo on real device + - gallery picker selects image and returns + - QR scan decodes valid code correctly + - permission denied shows correct error UI + external_setup: + ios: [NSCameraUsageDescription, NSPhotoLibraryUsageDescription, NSMicrophoneUsageDescription for video] + android: [CAMERA permission, READ_MEDIA_IMAGES for gallery access] + files_to_touch: + new: [lib/features/camera/, integration_test/camera/] + modified: [android/app/src/main/AndroidManifest.xml, ios/Runner/Info.plist, pubspec.yaml] +``` + +--- + +## Phase 1: Context Loading + +**When the user types `/build `, do the following before writing a single line of code:** + +1. Read `project-brief.yaml` from the repo root. Extract and hold in context: + - `stack.state_management` → **bloc** + - `stack.architecture` → **clean** + - `stack.routing` → **gorouter** + - `stack.backend` → **firebase** + - `stack.platforms` → **ios, android** + - `stack.codegen` → **freezed** + - `testing.depth` → **unit_widget** + - `testing.e2e_tool` → **patrol** + - `features.modules` (existing features) → **auth, home, products** + - `features.special` → **** + - `environments.flavors` → **dev, prod** + - `environments.cicd` → **github_actions** + +2. Parse the user's free-text request. Match against `FEATURE_REGISTRY` keywords using longest-match. Multiple types are allowed if the request spans features. + +3. Load `.cursor/rules/` files listed under `rules_to_load` for the matched feature type. + +4. If the user's message contains a URL or GitHub repo reference, fetch and index it for pattern reference. + +5. Check `features.modules` — if the feature already exists in the list, ask: "This feature is already listed in project-brief.yaml. Building additional capability on top of it? (y/n)" + +6. Print the context summary table before proceeding: + +``` +| Field | Value | +|------------------|------------------------------| +| Feature type | [detected type] | +| State management | BLoC / Cubit | +| Architecture | Clean Architecture | +| Backend | Firebase | +| Platforms | ios, android | +| E2E tool | patrol | +| Rules loaded | [list from rules_to_load] | +| Existing modules | auth, home, products | +``` + +--- + +## Phase 2: Deep Research + +**Before touching any file, print the full research output.** + +1. From the FEATURE_REGISTRY, enumerate all `pub_packages` for the detected type. For each, determine the correct version compatible with current Flutter stable (`flutter --version`). Print the full `pubspec.yaml` additions. + +2. Build the complete file manifest, adapting to `Clean Architecture`: + + **For Clean Architecture (clean):** + - `clean`: expand each feature layer explicitly → `domain/entities/`, `domain/repositories/`, `domain/usecases/`, `data/models/`, `data/datasources/`, `data/repositories/`, `presentation/` + - `feature_first`: `[feature]/[feature]_screen.dart`, `[feature]_provider.dart` or `[feature]_bloc.dart`, `[feature]_repository.dart`, `[feature]_model.dart`, `widgets/` + - `mvc`: `[feature]/model/`, `[feature]/view/`, `[feature]/controller/` + + **State management file naming (BLoC / Cubit / bloc):** + - `bloc`: `[feature]_bloc.dart`, `[feature]_event.dart`, `[feature]_state.dart` + - `riverpod`: `[feature]_provider.dart`, `[feature]_notifier.dart` + - `getx`: `[feature]_controller.dart`, `[feature]_binding.dart` + +3. List all modified files from `files_to_touch.modified` — show exactly what change goes in each file. + +4. List all external service configuration required per platform (from `external_setup`). Only show platforms present in `ios, android`. + +5. Print research results in this format: + +``` +## Research Results + +### Packages to add to pubspec.yaml +- [package_name]: ^[version] + +### Files to create (Clean Architecture / BLoC / Cubit) +lib/features/[name]/... (full list) + +### Files to modify +[file_path] — [what changes] + +### External services requiring configuration +- [Service] ([platform]) — [what to do] +``` + +Do not touch any file until the user has seen this output. + +--- + +## Phase 3: TDD Implementation + +> **Invoke skill: `superpowers:test-driven-development`** + +Follow Red → Green → Refactor strictly. Show actual terminal output at each step. + +### Step 3a — Red: Write all failing tests first + +1. Mirror the feature directory under `test/features/[feature]/`. +2. Write unit tests for **every scenario** in `FEATURE_REGISTRY.unit_test_scenarios` for the detected type. +3. Write widget tests for **every scenario** in `FEATURE_REGISTRY.widget_test_scenarios`. +4. Use `mocktail` for all dependencies. +5. Test naming convention: `'given [precondition], when [action], then [expected outcome]'` +6. Use the state management test pattern for **BLoC / Cubit**: + ``` + blocTest(description, build: () => MyCubit(), act: (c) => c.method(), expect: () => [MyState()]) + ``` +7. Run and confirm red: + ``` + flutter test test/features/[feature]/ --no-pub + ``` + **Paste the actual output here before proceeding.** + +### Step 3b — Green: Implement + +1. Create domain entities and repository interfaces. +2. Create data-layer implementations wiring to `Firebase`. +3. Create presentation layer using **BLoC / Cubit** patterns. +4. Register in DI container — follow `- presentation/ MUST NOT import from data/ +- domain/ MUST NOT import from data/ or presentation/ +- data/ CAN import from domain/ (implements interfaces) +- Use dependency injection to invert data → domain dependency`. +5. Wire the route in `GoRouter` router. +6. If `freezed` is not `none`: run `dart run build_runner build --delete-conflicting-outputs` after adding models. +7. Run tests and confirm green: + ``` + flutter test test/features/[feature]/ --no-pub + ``` + **Paste the actual output here before proceeding.** + +### Step 3c — Refactor + +1. Review for duplication, naming, and layer boundary violations per `.cursor/rules/flutter-core.mdc`. +2. Run and confirm clean: + ``` + flutter analyze + ``` + **Paste the actual output here before proceeding.** +3. Run tests once more to confirm still green. + +--- + +## Phase 4: Integration Test Generation + +**Generate `integration_test/[feature_type]/` before asking the user to run anything.** + +### Integration test file structure + +Each test file follows this template: + +```dart +// integration_test/[feature]/[scenario]_test.dart +// Generated by /build — TestApp +// Feature: [feature_type] | Scenario: [scenario_name] +// Run with: flutter test integration_test/[feature]/[scenario]_test.dart -d + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:com.test.testapp/main.dart' as app; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group('[FeatureType] — [Scenario Name]', () { + setUp(() async { + // Seed state / configure mocks / reset storage + }); + + tearDown(() async { + // Cleanup + }); + + testWidgets( + 'given [precondition], when [action], then [expected outcome]', + (tester) async { + app.main(); + await tester.pumpAndSettle(); + // test body + }, + ); + }); +} +``` + +**For `patrol` (patrol):** swap `flutter_test` imports for `package:patrol` and use Patrol's `$` NativeAutomator selector syntax. + +### Files to generate + +Create one file per logical scenario cluster from `FEATURE_REGISTRY.integration_test_scenarios`. +Also generate `integration_test/[feature]/README.md` with the full test matrix and run commands. + +### PAUSE GATE — user must run on device + +After generating all files, print this table and **stop**. Wait for the user to paste device output before Phase 6. + +``` +## ACTION REQUIRED — Run Integration Tests on Real Device + +| File | Covers | Requires Hardware | +|------|--------|------------------| +| integration_test/[feature]/[scenario]_test.dart | [what it covers] | Yes/No | +... + +Run on iOS: + flutter test integration_test/[feature]/ -d + +Run on Android: + flutter test integration_test/[feature]/ -d + +Paste the output here to continue. +``` + +--- + +## Phase 5: External Setup Checklist + +**Print before asking the user to verify anything. Group by service/platform. Only show sections for platforms in `ios, android`.** + +### Format for console/service steps (numbered list): + +```markdown +### Firebase (backend: Firebase) +- [ ] 1. Open Firebase Console → [exact menu path] +- [ ] 2. [Specific action] +- [ ] 3. Download updated config file → place at [exact path] +``` + +### Format for native platform steps (table): + +```markdown +### iOS Setup +| Step | Where | What | How to Verify | +|------|-------|------|---------------| +| [Step name] | [Xcode location / file path] | [Exact change] | [Verification method] | +``` + +```markdown +### Android Setup +- [ ] 1. Open `android/app/src/main/AndroidManifest.xml` + - Add: `` +- [ ] 2. [Next step with exact value] +``` + +### Flavor scoping note (flavors: dev, prod) + +Config files and API keys must be scoped per flavor. Never place production keys in `dev` flavor files. Follow the pattern established in `.cursor/rules/` for flavor-based configuration. + +--- + +## Phase 6: Verification Gate + +> **Invoke skill: `superpowers:verification-before-completion`** + +**Do not claim completion without pasting real output for every item below.** + +``` +VERIFICATION REQUIRED — paste real output for each: + +[ ] flutter test test/features/[feature]/ --no-pub + Required: "All N tests passed." + +[ ] flutter analyze + Required: "No issues found!" + +[ ] Integration test device output (from Phase 4 pause gate) + Required: actual device test output + +[ ] lefthook run pre-commit + Required: all hooks passed +``` + +If any check fails: invoke `superpowers:systematic-debugging` to diagnose before retrying. +Do not proceed to Phase 7 until all four checks are green. + +--- + +## Phase 7: PR Preparation + +> **Invoke skill: `superpowers:finishing-a-development-branch`** + +1. **Commit message** (conventional commits format): + ``` + feat([feature_type]): implement [feature] end-to-end + ``` + +2. **PR description template:** + ```markdown + ## What + [One sentence: what feature was implemented] + + ## Test coverage + - Unit/widget tests: N passing + - Integration test matrix: + | Scenario | iOS | Android | + |----------|-----|---------| + | [scenario] | ✅ | ✅ | + + ## External setup completed + - [ ] [Service 1 setup] + - [ ] [Service 2 setup] + + ## Notes + [Any follow-up TODOs or known limitations] + ``` + +3. **CI/CD advice for GitHub Actions (github_actions):** + Ensure workflow secrets are scoped per flavor (dev, prod). Never expose production keys in dev environment secrets. Verify the CI workflow runs `flutter test` and `flutter analyze` before deploy steps. + +--- + +## Rules applied every phase + +Always active — read before writing any code: +- `.cursor/rules/flutter-core.mdc` +- `.cursor/rules/security-standards.mdc` +- `.cursor/rules/project-context.mdc` +- Feature-type-specific rules from `FEATURE_REGISTRY.rules_to_load` + +Architecture import rules for **Clean Architecture**: +- presentation/ MUST NOT import from data/ +- domain/ MUST NOT import from data/ or presentation/ +- data/ CAN import from domain/ (implements interfaces) +- Use dependency injection to invert data → domain dependency + +--- + +## Code generation notes + +**Codegen tools configured: freezed** + +After adding any new model or injectable class, run: +``` +dart run build_runner build --delete-conflicting-outputs +``` + +Commit generated files (`.g.dart`, `.freezed.dart`, `injection.config.dart`) — do not gitignore them. + +**Template version:** 1.0.1 diff --git a/flutter-cursor-templates/generator/test/golden/riverpod-ff-supabase/skills/build/SKILL.md b/flutter-cursor-templates/generator/test/golden/riverpod-ff-supabase/skills/build/SKILL.md new file mode 100644 index 0000000..6ddcd8b --- /dev/null +++ b/flutter-cursor-templates/generator/test/golden/riverpod-ff-supabase/skills/build/SKILL.md @@ -0,0 +1,572 @@ +# Build — TaskFlow + +Implements any feature end-to-end: deep research → TDD → integration tests → external setup checklist → verified PR. +Stack: **Riverpod** / **Feature-First** / **Supabase** / ios, android, web. + +## Usage + +``` +/build +``` + +**Examples:** +- `/build implement notification module end-to-end` +- `/build add biometric auth` +- `/build integrate Stripe payments` +- `/build add deep linking for shared product pages` +- `/build implement analytics event tracking` + +--- + +## FEATURE_REGISTRY + +The AI reads this registry to classify the request and load the right research, test scenarios, and setup steps. + +```yaml +feature_registry: + + notifications: + keywords: [notification, push, fcm, apns, alert, badge, silent push, + remote notification, local notification, firebase messaging] + pub_packages: [firebase_messaging, flutter_local_notifications, firebase_core] + rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc] + unit_test_scenarios: + - parse notification payload to domain model + - FCM token generation and storage + - token refresh triggers re-registration + - notification permission denied returns graceful fallback + - notification service initialisation is idempotent + widget_test_scenarios: + - notification banner renders correct title and body + - tap on notification navigates to correct route + - badge count updates on new message + integration_test_scenarios: + - foreground notification receipt and display + - background notification receipt (app backgrounded, not killed) + - killed-state notification receipt (cold launch from notification tap) + - tap-to-open in foreground state routes correctly + - tap-to-open in background state routes correctly + - tap-to-open in killed state routes correctly + - notification payload parsing end-to-end + - deep link routing from notification data field + - multiple simultaneous notifications (ordering and dedup) + external_setup: + firebase: [Enable Cloud Messaging, download google-services.json, download GoogleService-Info.plist] + ios: [Push Notifications capability, Background Modes remote notifications, APNs .p8 key upload to Firebase] + android: [POST_NOTIFICATIONS permission, RECEIVE_BOOT_COMPLETED permission, google-services plugin] + files_to_touch: + new: [lib/features/notifications/, integration_test/notifications/] + modified: [lib/main.dart, android/app/src/main/AndroidManifest.xml, ios/Runner/Info.plist, ios/Runner/AppDelegate.swift, pubspec.yaml] + + auth: + keywords: [auth, authentication, login, sign in, sign out, logout, + biometric, face id, touch id, fingerprint, oauth, jwt, + session, token refresh, social login, google sign-in] + pub_packages: [firebase_auth, supabase_flutter, local_auth, flutter_secure_storage, google_sign_in] + rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc] + unit_test_scenarios: + - login success stores token securely + - login failure (wrong password) returns typed error + - login failure (network) returns typed error + - token refresh succeeds and updates stored token + - token refresh failure triggers logout + - biometric auth success grants access + - biometric auth failure falls back to password + - biometric not enrolled returns correct error state + - logout clears all stored credentials + - session persistence: token loaded on cold start + widget_test_scenarios: + - login form renders correctly + - validation errors shown inline + - loading state disables submit button + - biometric prompt shown when available + integration_test_scenarios: + - complete login flow (email and password) + - login then logout then login again + - invalid credentials shows error and stays on login + - biometric login flow on enrolled device + - biometric fallback to password + - token refresh in background while user navigates + - cold start with stored valid session skips login + - cold start with expired session redirects to login + - social auth OAuth redirect and return + - deep link into protected route redirects to login then back + external_setup: + firebase: [Enable Email/Password provider, Enable Google provider, download updated config files] + ios: [NSFaceIDUsageDescription in Info.plist, LocalAuthentication.framework in Xcode] + android: [USE_BIOMETRIC permission, USE_FINGERPRINT permission, minSdkVersion 23 for biometric] + files_to_touch: + new: [lib/features/auth/, integration_test/auth/] + modified: [lib/main.dart, lib/core/di/injection.dart, pubspec.yaml] + + payments: + keywords: [payment, stripe, in-app purchase, iap, checkout, subscription, + billing, apple pay, google pay, card, transaction, refund, revenue cat] + pub_packages: [flutter_stripe, purchases_flutter, in_app_purchase] + rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc] + unit_test_scenarios: + - checkout request builds correct PaymentIntent params + - payment success updates order state + - payment failure (card declined) returns typed error + - webhook event parsed to domain model + - refund request constructs correct API call + - subscription status checked on app resume + widget_test_scenarios: + - checkout form renders with correct amount + - payment loading state shown during processing + - success confirmation screen renders + - error state with retry option + integration_test_scenarios: + - end-to-end checkout with test card (Stripe test mode) + - checkout with 3D Secure challenge + - card declined shows error then retry succeeds + - Apple Pay / Google Pay sheet appears (device capability check) + - subscription purchase and entitlement unlock + - subscription restore flow + - refund flow from order history + external_setup: + stripe: [publishable and secret keys in flavor .env files, webhook endpoint in Stripe Dashboard, Apple Pay domain registration] + ios: [In-App Purchase capability, Apple Pay capability and merchant identifier, com.apple.developer.in-app-payments entitlement] + android: [Google Pay Console setup, BILLING permission for in_app_purchase] + files_to_touch: + new: [lib/features/payments/, integration_test/payments/] + modified: [ios/Runner/Runner.entitlements, android/app/src/main/AndroidManifest.xml, pubspec.yaml] + + deep_links: + keywords: [deep link, deep linking, universal link, app link, deferred deep link, + dynamic link, branch, uri scheme, custom scheme, url scheme] + pub_packages: [go_router, app_links, uni_links] + rules_to_load: [platform-ios.mdc, platform-android.mdc] + unit_test_scenarios: + - URI parsed to correct route and parameters + - unknown URI falls back to home + - authenticated-only route redirects to login when unauthenticated + - deep link preserves query parameters + widget_test_scenarios: + - navigation guard redirects unauthenticated deep link + integration_test_scenarios: + - cold start from Universal Link routes to correct screen + - cold start from custom URI scheme routes correctly + - backgrounded app receives deep link and navigates + - foreground app receives deep link and navigates + - deep link to authenticated route redirects to login then original destination + - deep link with path parameters loads correct content + - invalid or malformed deep link shows 404 screen + external_setup: + ios: [Associated Domains capability applinks:yourdomain.com, host apple-app-site-association file at /.well-known/] + android: [intent-filter with android:autoVerify=true in AndroidManifest.xml, host assetlinks.json at /.well-known/] + files_to_touch: + new: [lib/core/routing/deep_link_handler.dart, integration_test/deep_links/] + modified: [lib/core/routing/router.dart, android/app/src/main/AndroidManifest.xml, ios/Runner/Runner.entitlements, ios/Runner/Info.plist] + + analytics: + keywords: [analytics, tracking, event, mixpanel, amplitude, firebase analytics, + segment, posthog, screen view, funnel, cohort] + pub_packages: [firebase_analytics, mixpanel_flutter, amplitude_flutter, posthog_flutter] + rules_to_load: [security-standards.mdc] + unit_test_scenarios: + - analytics service logs correct event name and params + - PII fields stripped before event sent + - analytics disabled in dev flavor + - screen name logged on navigation + widget_test_scenarios: + - RouteObserver triggers screen_view event + integration_test_scenarios: + - user action triggers expected event (verify via debug view) + - screen transitions log screen_view with correct names + - opt-out disables all tracking + external_setup: + firebase: [Enable Analytics in Firebase Console, enable DebugView for local testing, configure conversion events] + files_to_touch: + new: [lib/core/analytics/analytics_service.dart, lib/core/analytics/analytics_events.dart, integration_test/analytics/] + modified: [lib/main.dart, pubspec.yaml] + + storage: + keywords: [storage, file upload, download, cloud storage, firebase storage, + supabase storage, s3, image upload, document, file picker, + offline, cache, hive, isar, objectbox, sqflite, drift] + pub_packages: [firebase_storage, supabase_flutter, hive_flutter, isar, drift, file_picker, image_picker, path_provider] + rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc] + unit_test_scenarios: + - upload returns public URL on success + - upload failure returns typed error + - local cache read before remote fetch (offline-first) + - cache invalidation on TTL expiry + - large file upload uses resumable upload + widget_test_scenarios: + - file picker button triggers picker + - upload progress indicator shown + - image preview renders after selection + integration_test_scenarios: + - end-to-end file upload and retrieval + - offline mode: local cache serves data + - background upload completes when connectivity restored + - file size limit enforced + external_setup: + firebase: [Enable Firebase Storage, configure Storage security rules, set CORS policy for web if applicable] + ios: [NSPhotoLibraryUsageDescription in Info.plist, NSCameraUsageDescription in Info.plist] + android: [READ_EXTERNAL_STORAGE or READ_MEDIA_IMAGES permission] + files_to_touch: + new: [lib/features/storage/, integration_test/storage/] + modified: [android/app/src/main/AndroidManifest.xml, ios/Runner/Info.plist, pubspec.yaml] + + camera_media: + keywords: [camera, photo, video, image picker, qr code, barcode scanner, + ar, augmented reality, gallery, media, record, capture] + pub_packages: [camera, image_picker, mobile_scanner, qr_flutter, image_cropper, video_player] + rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc] + unit_test_scenarios: + - QR/barcode parsed to correct domain model + - image compressed before upload + - camera permission denied returns typed error + widget_test_scenarios: + - camera preview widget renders + - capture button triggers photo take + integration_test_scenarios: + - camera opens and captures photo on real device + - gallery picker selects image and returns + - QR scan decodes valid code correctly + - permission denied shows correct error UI + external_setup: + ios: [NSCameraUsageDescription, NSPhotoLibraryUsageDescription, NSMicrophoneUsageDescription for video] + android: [CAMERA permission, READ_MEDIA_IMAGES for gallery access] + files_to_touch: + new: [lib/features/camera/, integration_test/camera/] + modified: [android/app/src/main/AndroidManifest.xml, ios/Runner/Info.plist, pubspec.yaml] +``` + +--- + +## Phase 1: Context Loading + +**When the user types `/build `, do the following before writing a single line of code:** + +1. Read `project-brief.yaml` from the repo root. Extract and hold in context: + - `stack.state_management` → **riverpod** + - `stack.architecture` → **feature_first** + - `stack.routing` → **gorouter** + - `stack.backend` → **supabase** + - `stack.platforms` → **ios, android, web** + - `stack.codegen` → **freezed, json_serializable** + - `testing.depth` → **unit_widget** + - `testing.e2e_tool` → **patrol** + - `features.modules` (existing features) → **auth, tasks, profile** + - `features.special` → **** + - `environments.flavors` → **dev, prod** + - `environments.cicd` → **github_actions** + +2. Parse the user's free-text request. Match against `FEATURE_REGISTRY` keywords using longest-match. Multiple types are allowed if the request spans features. + +3. Load `.cursor/rules/` files listed under `rules_to_load` for the matched feature type. + +4. If the user's message contains a URL or GitHub repo reference, fetch and index it for pattern reference. + +5. Check `features.modules` — if the feature already exists in the list, ask: "This feature is already listed in project-brief.yaml. Building additional capability on top of it? (y/n)" + +6. Print the context summary table before proceeding: + +``` +| Field | Value | +|------------------|------------------------------| +| Feature type | [detected type] | +| State management | Riverpod | +| Architecture | Feature-First | +| Backend | Supabase | +| Platforms | ios, android, web | +| E2E tool | patrol | +| Rules loaded | [list from rules_to_load] | +| Existing modules | auth, tasks, profile | +``` + +--- + +## Phase 2: Deep Research + +**Before touching any file, print the full research output.** + +1. From the FEATURE_REGISTRY, enumerate all `pub_packages` for the detected type. For each, determine the correct version compatible with current Flutter stable (`flutter --version`). Print the full `pubspec.yaml` additions. + +2. Build the complete file manifest, adapting to `Feature-First`: + + **For Feature-First (feature_first):** + - `clean`: expand each feature layer explicitly → `domain/entities/`, `domain/repositories/`, `domain/usecases/`, `data/models/`, `data/datasources/`, `data/repositories/`, `presentation/` + - `feature_first`: `[feature]/[feature]_screen.dart`, `[feature]_provider.dart` or `[feature]_bloc.dart`, `[feature]_repository.dart`, `[feature]_model.dart`, `widgets/` + - `mvc`: `[feature]/model/`, `[feature]/view/`, `[feature]/controller/` + + **State management file naming (Riverpod / riverpod):** + - `bloc`: `[feature]_bloc.dart`, `[feature]_event.dart`, `[feature]_state.dart` + - `riverpod`: `[feature]_provider.dart`, `[feature]_notifier.dart` + - `getx`: `[feature]_controller.dart`, `[feature]_binding.dart` + +3. List all modified files from `files_to_touch.modified` — show exactly what change goes in each file. + +4. List all external service configuration required per platform (from `external_setup`). Only show platforms present in `ios, android, web`. + +5. Print research results in this format: + +``` +## Research Results + +### Packages to add to pubspec.yaml +- [package_name]: ^[version] + +### Files to create (Feature-First / Riverpod) +lib/features/[name]/... (full list) + +### Files to modify +[file_path] — [what changes] + +### External services requiring configuration +- [Service] ([platform]) — [what to do] +``` + +Do not touch any file until the user has seen this output. + +--- + +## Phase 3: TDD Implementation + +> **Invoke skill: `superpowers:test-driven-development`** + +Follow Red → Green → Refactor strictly. Show actual terminal output at each step. + +### Step 3a — Red: Write all failing tests first + +1. Mirror the feature directory under `test/features/[feature]/`. +2. Write unit tests for **every scenario** in `FEATURE_REGISTRY.unit_test_scenarios` for the detected type. +3. Write widget tests for **every scenario** in `FEATURE_REGISTRY.widget_test_scenarios`. +4. Use `mocktail` for all dependencies. +5. Test naming convention: `'given [precondition], when [action], then [expected outcome]'` +6. Use the state management test pattern for **Riverpod**: + ``` + final container = ProviderContainer(overrides: [myProvider.overrideWithValue(mockValue)]); addTearDown(container.dispose); + ``` +7. Run and confirm red: + ``` + flutter test test/features/[feature]/ --no-pub + ``` + **Paste the actual output here before proceeding.** + +### Step 3b — Green: Implement + +1. Create domain entities and repository interfaces. +2. Create data-layer implementations wiring to `Supabase`. +3. Create presentation layer using **Riverpod** patterns. +4. Register in DI container — follow `- features/auth/ MUST NOT import from features/home/ etc. +- Shared code lives in core/ or shared/ only +- Each feature is self-contained: screen + provider + model + repo`. +5. Wire the route in `GoRouter` router. +6. If `freezed, json_serializable` is not `none`: run `dart run build_runner build --delete-conflicting-outputs` after adding models. +7. Run tests and confirm green: + ``` + flutter test test/features/[feature]/ --no-pub + ``` + **Paste the actual output here before proceeding.** + +### Step 3c — Refactor + +1. Review for duplication, naming, and layer boundary violations per `.cursor/rules/flutter-core.mdc`. +2. Run and confirm clean: + ``` + flutter analyze + ``` + **Paste the actual output here before proceeding.** +3. Run tests once more to confirm still green. + +--- + +## Phase 4: Integration Test Generation + +**Generate `integration_test/[feature_type]/` before asking the user to run anything.** + +### Integration test file structure + +Each test file follows this template: + +```dart +// integration_test/[feature]/[scenario]_test.dart +// Generated by /build — TaskFlow +// Feature: [feature_type] | Scenario: [scenario_name] +// Run with: flutter test integration_test/[feature]/[scenario]_test.dart -d + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:com.test.taskflow/main.dart' as app; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group('[FeatureType] — [Scenario Name]', () { + setUp(() async { + // Seed state / configure mocks / reset storage + }); + + tearDown(() async { + // Cleanup + }); + + testWidgets( + 'given [precondition], when [action], then [expected outcome]', + (tester) async { + app.main(); + await tester.pumpAndSettle(); + // test body + }, + ); + }); +} +``` + +**For `patrol` (patrol):** swap `flutter_test` imports for `package:patrol` and use Patrol's `$` NativeAutomator selector syntax. + +### Files to generate + +Create one file per logical scenario cluster from `FEATURE_REGISTRY.integration_test_scenarios`. +Also generate `integration_test/[feature]/README.md` with the full test matrix and run commands. + +### PAUSE GATE — user must run on device + +After generating all files, print this table and **stop**. Wait for the user to paste device output before Phase 6. + +``` +## ACTION REQUIRED — Run Integration Tests on Real Device + +| File | Covers | Requires Hardware | +|------|--------|------------------| +| integration_test/[feature]/[scenario]_test.dart | [what it covers] | Yes/No | +... + +Run on iOS: + flutter test integration_test/[feature]/ -d + +Run on Android: + flutter test integration_test/[feature]/ -d + +Paste the output here to continue. +``` + +--- + +## Phase 5: External Setup Checklist + +**Print before asking the user to verify anything. Group by service/platform. Only show sections for platforms in `ios, android, web`.** + +### Format for console/service steps (numbered list): + +```markdown +### Firebase (backend: Supabase) +- [ ] 1. Open Firebase Console → [exact menu path] +- [ ] 2. [Specific action] +- [ ] 3. Download updated config file → place at [exact path] +``` + +### Format for native platform steps (table): + +```markdown +### iOS Setup +| Step | Where | What | How to Verify | +|------|-------|------|---------------| +| [Step name] | [Xcode location / file path] | [Exact change] | [Verification method] | +``` + +```markdown +### Android Setup +- [ ] 1. Open `android/app/src/main/AndroidManifest.xml` + - Add: `` +- [ ] 2. [Next step with exact value] +``` + +### Flavor scoping note (flavors: dev, prod) + +Config files and API keys must be scoped per flavor. Never place production keys in `dev` flavor files. Follow the pattern established in `.cursor/rules/` for flavor-based configuration. + +--- + +## Phase 6: Verification Gate + +> **Invoke skill: `superpowers:verification-before-completion`** + +**Do not claim completion without pasting real output for every item below.** + +``` +VERIFICATION REQUIRED — paste real output for each: + +[ ] flutter test test/features/[feature]/ --no-pub + Required: "All N tests passed." + +[ ] flutter analyze + Required: "No issues found!" + +[ ] Integration test device output (from Phase 4 pause gate) + Required: actual device test output + +[ ] lefthook run pre-commit + Required: all hooks passed +``` + +If any check fails: invoke `superpowers:systematic-debugging` to diagnose before retrying. +Do not proceed to Phase 7 until all four checks are green. + +--- + +## Phase 7: PR Preparation + +> **Invoke skill: `superpowers:finishing-a-development-branch`** + +1. **Commit message** (conventional commits format): + ``` + feat([feature_type]): implement [feature] end-to-end + ``` + +2. **PR description template:** + ```markdown + ## What + [One sentence: what feature was implemented] + + ## Test coverage + - Unit/widget tests: N passing + - Integration test matrix: + | Scenario | iOS | Android | + |----------|-----|---------| + | [scenario] | ✅ | ✅ | + + ## External setup completed + - [ ] [Service 1 setup] + - [ ] [Service 2 setup] + + ## Notes + [Any follow-up TODOs or known limitations] + ``` + +3. **CI/CD advice for GitHub Actions (github_actions):** + Ensure workflow secrets are scoped per flavor (dev, prod). Never expose production keys in dev environment secrets. Verify the CI workflow runs `flutter test` and `flutter analyze` before deploy steps. + +--- + +## Rules applied every phase + +Always active — read before writing any code: +- `.cursor/rules/flutter-core.mdc` +- `.cursor/rules/security-standards.mdc` +- `.cursor/rules/project-context.mdc` +- Feature-type-specific rules from `FEATURE_REGISTRY.rules_to_load` + +Architecture import rules for **Feature-First**: +- features/auth/ MUST NOT import from features/home/ etc. +- Shared code lives in core/ or shared/ only +- Each feature is self-contained: screen + provider + model + repo + +--- + +## Code generation notes + +**Codegen tools configured: freezed, json_serializable** + +After adding any new model or injectable class, run: +``` +dart run build_runner build --delete-conflicting-outputs +``` + +Commit generated files (`.g.dart`, `.freezed.dart`, `injection.config.dart`) — do not gitignore them. + +**Template version:** 1.0.1 diff --git a/flutter-cursor-templates/templates/skills/build/SKILL.md.tmpl b/flutter-cursor-templates/templates/skills/build/SKILL.md.tmpl new file mode 100644 index 0000000..36afbd1 --- /dev/null +++ b/flutter-cursor-templates/templates/skills/build/SKILL.md.tmpl @@ -0,0 +1,568 @@ +# Build — {{PROJECT_NAME}} + +Implements any feature end-to-end: deep research → TDD → integration tests → external setup checklist → verified PR. +Stack: **{{STATE_MANAGEMENT}}** / **{{ARCHITECTURE}}** / **{{BACKEND}}** / {{PLATFORMS_LIST}}. + +## Usage + +``` +/build +``` + +**Examples:** +- `/build implement notification module end-to-end` +- `/build add biometric auth` +- `/build integrate Stripe payments` +- `/build add deep linking for shared product pages` +- `/build implement analytics event tracking` + +--- + +## FEATURE_REGISTRY + +The AI reads this registry to classify the request and load the right research, test scenarios, and setup steps. + +```yaml +feature_registry: + + notifications: + keywords: [notification, push, fcm, apns, alert, badge, silent push, + remote notification, local notification, firebase messaging] + pub_packages: [firebase_messaging, flutter_local_notifications, firebase_core] + rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc] + unit_test_scenarios: + - parse notification payload to domain model + - FCM token generation and storage + - token refresh triggers re-registration + - notification permission denied returns graceful fallback + - notification service initialisation is idempotent + widget_test_scenarios: + - notification banner renders correct title and body + - tap on notification navigates to correct route + - badge count updates on new message + integration_test_scenarios: + - foreground notification receipt and display + - background notification receipt (app backgrounded, not killed) + - killed-state notification receipt (cold launch from notification tap) + - tap-to-open in foreground state routes correctly + - tap-to-open in background state routes correctly + - tap-to-open in killed state routes correctly + - notification payload parsing end-to-end + - deep link routing from notification data field + - multiple simultaneous notifications (ordering and dedup) + external_setup: + firebase: [Enable Cloud Messaging, download google-services.json, download GoogleService-Info.plist] + ios: [Push Notifications capability, Background Modes remote notifications, APNs .p8 key upload to Firebase] + android: [POST_NOTIFICATIONS permission, RECEIVE_BOOT_COMPLETED permission, google-services plugin] + files_to_touch: + new: [lib/features/notifications/, integration_test/notifications/] + modified: [lib/main.dart, android/app/src/main/AndroidManifest.xml, ios/Runner/Info.plist, ios/Runner/AppDelegate.swift, pubspec.yaml] + + auth: + keywords: [auth, authentication, login, sign in, sign out, logout, + biometric, face id, touch id, fingerprint, oauth, jwt, + session, token refresh, social login, google sign-in] + pub_packages: [firebase_auth, supabase_flutter, local_auth, flutter_secure_storage, google_sign_in] + rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc] + unit_test_scenarios: + - login success stores token securely + - login failure (wrong password) returns typed error + - login failure (network) returns typed error + - token refresh succeeds and updates stored token + - token refresh failure triggers logout + - biometric auth success grants access + - biometric auth failure falls back to password + - biometric not enrolled returns correct error state + - logout clears all stored credentials + - session persistence: token loaded on cold start + widget_test_scenarios: + - login form renders correctly + - validation errors shown inline + - loading state disables submit button + - biometric prompt shown when available + integration_test_scenarios: + - complete login flow (email and password) + - login then logout then login again + - invalid credentials shows error and stays on login + - biometric login flow on enrolled device + - biometric fallback to password + - token refresh in background while user navigates + - cold start with stored valid session skips login + - cold start with expired session redirects to login + - social auth OAuth redirect and return + - deep link into protected route redirects to login then back + external_setup: + firebase: [Enable Email/Password provider, Enable Google provider, download updated config files] + ios: [NSFaceIDUsageDescription in Info.plist, LocalAuthentication.framework in Xcode] + android: [USE_BIOMETRIC permission, USE_FINGERPRINT permission, minSdkVersion 23 for biometric] + files_to_touch: + new: [lib/features/auth/, integration_test/auth/] + modified: [lib/main.dart, lib/core/di/injection.dart, pubspec.yaml] + + payments: + keywords: [payment, stripe, in-app purchase, iap, checkout, subscription, + billing, apple pay, google pay, card, transaction, refund, revenue cat] + pub_packages: [flutter_stripe, purchases_flutter, in_app_purchase] + rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc] + unit_test_scenarios: + - checkout request builds correct PaymentIntent params + - payment success updates order state + - payment failure (card declined) returns typed error + - webhook event parsed to domain model + - refund request constructs correct API call + - subscription status checked on app resume + widget_test_scenarios: + - checkout form renders with correct amount + - payment loading state shown during processing + - success confirmation screen renders + - error state with retry option + integration_test_scenarios: + - end-to-end checkout with test card (Stripe test mode) + - checkout with 3D Secure challenge + - card declined shows error then retry succeeds + - Apple Pay / Google Pay sheet appears (device capability check) + - subscription purchase and entitlement unlock + - subscription restore flow + - refund flow from order history + external_setup: + stripe: [publishable and secret keys in flavor .env files, webhook endpoint in Stripe Dashboard, Apple Pay domain registration] + ios: [In-App Purchase capability, Apple Pay capability and merchant identifier, com.apple.developer.in-app-payments entitlement] + android: [Google Pay Console setup, BILLING permission for in_app_purchase] + files_to_touch: + new: [lib/features/payments/, integration_test/payments/] + modified: [ios/Runner/Runner.entitlements, android/app/src/main/AndroidManifest.xml, pubspec.yaml] + + deep_links: + keywords: [deep link, deep linking, universal link, app link, deferred deep link, + dynamic link, branch, uri scheme, custom scheme, url scheme] + pub_packages: [go_router, app_links, uni_links] + rules_to_load: [platform-ios.mdc, platform-android.mdc] + unit_test_scenarios: + - URI parsed to correct route and parameters + - unknown URI falls back to home + - authenticated-only route redirects to login when unauthenticated + - deep link preserves query parameters + widget_test_scenarios: + - navigation guard redirects unauthenticated deep link + integration_test_scenarios: + - cold start from Universal Link routes to correct screen + - cold start from custom URI scheme routes correctly + - backgrounded app receives deep link and navigates + - foreground app receives deep link and navigates + - deep link to authenticated route redirects to login then original destination + - deep link with path parameters loads correct content + - invalid or malformed deep link shows 404 screen + external_setup: + ios: [Associated Domains capability applinks:yourdomain.com, host apple-app-site-association file at /.well-known/] + android: [intent-filter with android:autoVerify=true in AndroidManifest.xml, host assetlinks.json at /.well-known/] + files_to_touch: + new: [lib/core/routing/deep_link_handler.dart, integration_test/deep_links/] + modified: [lib/core/routing/router.dart, android/app/src/main/AndroidManifest.xml, ios/Runner/Runner.entitlements, ios/Runner/Info.plist] + + analytics: + keywords: [analytics, tracking, event, mixpanel, amplitude, firebase analytics, + segment, posthog, screen view, funnel, cohort] + pub_packages: [firebase_analytics, mixpanel_flutter, amplitude_flutter, posthog_flutter] + rules_to_load: [security-standards.mdc] + unit_test_scenarios: + - analytics service logs correct event name and params + - PII fields stripped before event sent + - analytics disabled in dev flavor + - screen name logged on navigation + widget_test_scenarios: + - RouteObserver triggers screen_view event + integration_test_scenarios: + - user action triggers expected event (verify via debug view) + - screen transitions log screen_view with correct names + - opt-out disables all tracking + external_setup: + firebase: [Enable Analytics in Firebase Console, enable DebugView for local testing, configure conversion events] + files_to_touch: + new: [lib/core/analytics/analytics_service.dart, lib/core/analytics/analytics_events.dart, integration_test/analytics/] + modified: [lib/main.dart, pubspec.yaml] + + storage: + keywords: [storage, file upload, download, cloud storage, firebase storage, + supabase storage, s3, image upload, document, file picker, + offline, cache, hive, isar, objectbox, sqflite, drift] + pub_packages: [firebase_storage, supabase_flutter, hive_flutter, isar, drift, file_picker, image_picker, path_provider] + rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc] + unit_test_scenarios: + - upload returns public URL on success + - upload failure returns typed error + - local cache read before remote fetch (offline-first) + - cache invalidation on TTL expiry + - large file upload uses resumable upload + widget_test_scenarios: + - file picker button triggers picker + - upload progress indicator shown + - image preview renders after selection + integration_test_scenarios: + - end-to-end file upload and retrieval + - offline mode: local cache serves data + - background upload completes when connectivity restored + - file size limit enforced + external_setup: + firebase: [Enable Firebase Storage, configure Storage security rules, set CORS policy for web if applicable] + ios: [NSPhotoLibraryUsageDescription in Info.plist, NSCameraUsageDescription in Info.plist] + android: [READ_EXTERNAL_STORAGE or READ_MEDIA_IMAGES permission] + files_to_touch: + new: [lib/features/storage/, integration_test/storage/] + modified: [android/app/src/main/AndroidManifest.xml, ios/Runner/Info.plist, pubspec.yaml] + + camera_media: + keywords: [camera, photo, video, image picker, qr code, barcode scanner, + ar, augmented reality, gallery, media, record, capture] + pub_packages: [camera, image_picker, mobile_scanner, qr_flutter, image_cropper, video_player] + rules_to_load: [security-standards.mdc, platform-ios.mdc, platform-android.mdc] + unit_test_scenarios: + - QR/barcode parsed to correct domain model + - image compressed before upload + - camera permission denied returns typed error + widget_test_scenarios: + - camera preview widget renders + - capture button triggers photo take + integration_test_scenarios: + - camera opens and captures photo on real device + - gallery picker selects image and returns + - QR scan decodes valid code correctly + - permission denied shows correct error UI + external_setup: + ios: [NSCameraUsageDescription, NSPhotoLibraryUsageDescription, NSMicrophoneUsageDescription for video] + android: [CAMERA permission, READ_MEDIA_IMAGES for gallery access] + files_to_touch: + new: [lib/features/camera/, integration_test/camera/] + modified: [android/app/src/main/AndroidManifest.xml, ios/Runner/Info.plist, pubspec.yaml] +``` + +--- + +## Phase 1: Context Loading + +**When the user types `/build `, do the following before writing a single line of code:** + +1. Read `project-brief.yaml` from the repo root. Extract and hold in context: + - `stack.state_management` → **{{STATE_MGMT_RAW}}** + - `stack.architecture` → **{{ARCH_RAW}}** + - `stack.routing` → **{{ROUTING_RAW}}** + - `stack.backend` → **{{BACKENDS_LIST}}** + - `stack.platforms` → **{{PLATFORMS_LIST}}** + - `stack.codegen` → **{{CODEGEN_LIST}}** + - `testing.depth` → **{{TESTING_DEPTH}}** + - `testing.e2e_tool` → **{{E2E_TOOL}}** + - `features.modules` (existing features) → **{{FEATURES_LIST}}** + - `features.special` → **{{SPECIAL_FEATURES}}** + - `environments.flavors` → **{{FLAVORS_LIST}}** + - `environments.cicd` → **{{CICD_RAW}}** + +2. Parse the user's free-text request. Match against `FEATURE_REGISTRY` keywords using longest-match. Multiple types are allowed if the request spans features. + +3. Load `.cursor/rules/` files listed under `rules_to_load` for the matched feature type. + +4. If the user's message contains a URL or GitHub repo reference, fetch and index it for pattern reference. + +5. Check `features.modules` — if the feature already exists in the list, ask: "This feature is already listed in project-brief.yaml. Building additional capability on top of it? (y/n)" + +6. Print the context summary table before proceeding: + +``` +| Field | Value | +|------------------|------------------------------| +| Feature type | [detected type] | +| State management | {{STATE_MANAGEMENT}} | +| Architecture | {{ARCHITECTURE}} | +| Backend | {{BACKEND}} | +| Platforms | {{PLATFORMS_LIST}} | +| E2E tool | {{E2E_TOOL}} | +| Rules loaded | [list from rules_to_load] | +| Existing modules | {{FEATURES_LIST}} | +``` + +--- + +## Phase 2: Deep Research + +**Before touching any file, print the full research output.** + +1. From the FEATURE_REGISTRY, enumerate all `pub_packages` for the detected type. For each, determine the correct version compatible with current Flutter stable (`flutter --version`). Print the full `pubspec.yaml` additions. + +2. Build the complete file manifest, adapting to `{{ARCHITECTURE}}`: + + **For {{ARCHITECTURE}} ({{ARCH_RAW}}):** + - `clean`: expand each feature layer explicitly → `domain/entities/`, `domain/repositories/`, `domain/usecases/`, `data/models/`, `data/datasources/`, `data/repositories/`, `presentation/` + - `feature_first`: `[feature]/[feature]_screen.dart`, `[feature]_provider.dart` or `[feature]_bloc.dart`, `[feature]_repository.dart`, `[feature]_model.dart`, `widgets/` + - `mvc`: `[feature]/model/`, `[feature]/view/`, `[feature]/controller/` + + **State management file naming ({{STATE_MANAGEMENT}} / {{STATE_MGMT_RAW}}):** + - `bloc`: `[feature]_bloc.dart`, `[feature]_event.dart`, `[feature]_state.dart` + - `riverpod`: `[feature]_provider.dart`, `[feature]_notifier.dart` + - `getx`: `[feature]_controller.dart`, `[feature]_binding.dart` + +3. List all modified files from `files_to_touch.modified` — show exactly what change goes in each file. + +4. List all external service configuration required per platform (from `external_setup`). Only show platforms present in `{{PLATFORMS_LIST}}`. + +5. Print research results in this format: + +``` +## Research Results + +### Packages to add to pubspec.yaml +- [package_name]: ^[version] + +### Files to create ({{ARCHITECTURE}} / {{STATE_MANAGEMENT}}) +lib/features/[name]/... (full list) + +### Files to modify +[file_path] — [what changes] + +### External services requiring configuration +- [Service] ([platform]) — [what to do] +``` + +Do not touch any file until the user has seen this output. + +--- + +## Phase 3: TDD Implementation + +> **Invoke skill: `superpowers:test-driven-development`** + +Follow Red → Green → Refactor strictly. Show actual terminal output at each step. + +### Step 3a — Red: Write all failing tests first + +1. Mirror the feature directory under `test/features/[feature]/`. +2. Write unit tests for **every scenario** in `FEATURE_REGISTRY.unit_test_scenarios` for the detected type. +3. Write widget tests for **every scenario** in `FEATURE_REGISTRY.widget_test_scenarios`. +4. Use `mocktail` for all dependencies. +5. Test naming convention: `'given [precondition], when [action], then [expected outcome]'` +6. Use the state management test pattern for **{{STATE_MANAGEMENT}}**: + ``` + {{TEST_PATTERN}} + ``` +7. Run and confirm red: + ``` + flutter test test/features/[feature]/ --no-pub + ``` + **Paste the actual output here before proceeding.** + +### Step 3b — Green: Implement + +1. Create domain entities and repository interfaces. +2. Create data-layer implementations wiring to `{{BACKEND}}`. +3. Create presentation layer using **{{STATE_MANAGEMENT}}** patterns. +4. Register in DI container — follow `{{ARCH_IMPORT_RULES}}`. +5. Wire the route in `{{ROUTING}}` router. +6. If `{{CODEGEN_LIST}}` is not `none`: run `dart run build_runner build --delete-conflicting-outputs` after adding models. +7. Run tests and confirm green: + ``` + flutter test test/features/[feature]/ --no-pub + ``` + **Paste the actual output here before proceeding.** + +### Step 3c — Refactor + +1. Review for duplication, naming, and layer boundary violations per `.cursor/rules/flutter-core.mdc`. +2. Run and confirm clean: + ``` + flutter analyze + ``` + **Paste the actual output here before proceeding.** +3. Run tests once more to confirm still green. + +--- + +## Phase 4: Integration Test Generation + +**Generate `integration_test/[feature_type]/` before asking the user to run anything.** + +### Integration test file structure + +Each test file follows this template: + +```dart +// integration_test/[feature]/[scenario]_test.dart +// Generated by /build — {{PROJECT_NAME}} +// Feature: [feature_type] | Scenario: [scenario_name] +// Run with: flutter test integration_test/[feature]/[scenario]_test.dart -d + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:{{PACKAGE_ID}}/main.dart' as app; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group('[FeatureType] — [Scenario Name]', () { + setUp(() async { + // Seed state / configure mocks / reset storage + }); + + tearDown(() async { + // Cleanup + }); + + testWidgets( + 'given [precondition], when [action], then [expected outcome]', + (tester) async { + app.main(); + await tester.pumpAndSettle(); + // test body + }, + ); + }); +} +``` + +**For `{{E2E_TOOL}}` (patrol):** swap `flutter_test` imports for `package:patrol` and use Patrol's `$` NativeAutomator selector syntax. + +### Files to generate + +Create one file per logical scenario cluster from `FEATURE_REGISTRY.integration_test_scenarios`. +Also generate `integration_test/[feature]/README.md` with the full test matrix and run commands. + +### PAUSE GATE — user must run on device + +After generating all files, print this table and **stop**. Wait for the user to paste device output before Phase 6. + +``` +## ACTION REQUIRED — Run Integration Tests on Real Device + +| File | Covers | Requires Hardware | +|------|--------|------------------| +| integration_test/[feature]/[scenario]_test.dart | [what it covers] | Yes/No | +... + +Run on iOS: + flutter test integration_test/[feature]/ -d + +Run on Android: + flutter test integration_test/[feature]/ -d + +Paste the output here to continue. +``` + +--- + +## Phase 5: External Setup Checklist + +**Print before asking the user to verify anything. Group by service/platform. Only show sections for platforms in `{{PLATFORMS_LIST}}`.** + +### Format for console/service steps (numbered list): + +```markdown +### Firebase (backend: {{BACKEND}}) +- [ ] 1. Open Firebase Console → [exact menu path] +- [ ] 2. [Specific action] +- [ ] 3. Download updated config file → place at [exact path] +``` + +### Format for native platform steps (table): + +```markdown +### iOS Setup +| Step | Where | What | How to Verify | +|------|-------|------|---------------| +| [Step name] | [Xcode location / file path] | [Exact change] | [Verification method] | +``` + +```markdown +### Android Setup +- [ ] 1. Open `android/app/src/main/AndroidManifest.xml` + - Add: `` +- [ ] 2. [Next step with exact value] +``` + +### Flavor scoping note (flavors: {{FLAVORS_LIST}}) + +Config files and API keys must be scoped per flavor. Never place production keys in `dev` flavor files. Follow the pattern established in `.cursor/rules/` for flavor-based configuration. + +--- + +## Phase 6: Verification Gate + +> **Invoke skill: `superpowers:verification-before-completion`** + +**Do not claim completion without pasting real output for every item below.** + +``` +VERIFICATION REQUIRED — paste real output for each: + +[ ] flutter test test/features/[feature]/ --no-pub + Required: "All N tests passed." + +[ ] flutter analyze + Required: "No issues found!" + +[ ] Integration test device output (from Phase 4 pause gate) + Required: actual device test output + +[ ] lefthook run pre-commit + Required: all hooks passed +``` + +If any check fails: invoke `superpowers:systematic-debugging` to diagnose before retrying. +Do not proceed to Phase 7 until all four checks are green. + +--- + +## Phase 7: PR Preparation + +> **Invoke skill: `superpowers:finishing-a-development-branch`** + +1. **Commit message** (conventional commits format): + ``` + feat([feature_type]): implement [feature] end-to-end + ``` + +2. **PR description template:** + ```markdown + ## What + [One sentence: what feature was implemented] + + ## Test coverage + - Unit/widget tests: N passing + - Integration test matrix: + | Scenario | iOS | Android | + |----------|-----|---------| + | [scenario] | ✅ | ✅ | + + ## External setup completed + - [ ] [Service 1 setup] + - [ ] [Service 2 setup] + + ## Notes + [Any follow-up TODOs or known limitations] + ``` + +3. **CI/CD advice for {{CICD_TOOL}} ({{CICD_RAW}}):** + Ensure workflow secrets are scoped per flavor ({{FLAVORS_LIST}}). Never expose production keys in dev environment secrets. Verify the CI workflow runs `flutter test` and `flutter analyze` before deploy steps. + +--- + +## Rules applied every phase + +Always active — read before writing any code: +- `.cursor/rules/flutter-core.mdc` +- `.cursor/rules/security-standards.mdc` +- `.cursor/rules/project-context.mdc` +- Feature-type-specific rules from `FEATURE_REGISTRY.rules_to_load` + +Architecture import rules for **{{ARCHITECTURE}}**: +{{ARCH_IMPORT_RULES}} + +--- + +## Code generation notes + +**Codegen tools configured: {{CODEGEN_LIST}}** + +After adding any new model or injectable class, run: +``` +dart run build_runner build --delete-conflicting-outputs +``` + +Commit generated files (`.g.dart`, `.freezed.dart`, `injection.config.dart`) — do not gitignore them. + +**Template version:** {{TEMPLATE_VERSION}}