skills constant

Map<String, String> const skills

Implementation

static const Map<String, String> skills = {
  'copilot-instructions.md': r'''
# Flutter Base Project – GitHub Copilot Instructions

You are a senior Dart/Flutter engineer working on this project.
Adhere to all conventions established in the codebase when generating, correcting, or refactoring code.

---

## Project at a Glance

| Concern              | Solution                                                             |
| -------------------- | -------------------------------------------------------------------- |
| State management     | `flutter_bloc` (Bloc + Cubit)                                        |
| State immutability   | `Equatable` — all states and events extend it                        |
| Dependency injection | Constructor injection in Blocs; repos use `DioUtil()` singleton      |
| Navigation           | `NavigationService` singleton + `CustomRouter.generateRoute`         |
| Networking           | `DioUtil` (Dio wrapper) only — never use the `http` package          |
| Secure storage       | `SecureStorageHelper` for tokens; `SharedPreferenceHelper` for flags |
| Routing constants    | `RouteConstants` (not inline strings or `static const id`)           |
| String constants     | `AppStrings`                                                         |
| Icon/asset constants | `AppIcons`                                                           |
| Colour constants     | `AppColors`                                                          |
| Dimension constants  | `AppDimensions`                                                      |
| Result type          | `Result<T>` / `Success<T>` / `Failure` from `core/types/`            |
| Barrel import        | `import '../../../../core/router/export.dart';`                      |

---

## Dart / Flutter General Rules

### Language

- Write all code and documentation in **English**.
- Always declare explicit types for variables, parameters, and return values.
Avoid `dynamic` or `var` unless unavoidable.
- Use `final` for everything that does not reassign.
- Prefer `const` constructors and widget instantiations wherever possible.

### Naming

| Entity                          | Convention                             | Example                 |
| ------------------------------- | -------------------------------------- | ----------------------- |
| Classes                         | PascalCase                             | `HomeBloc`              |
| Variables / functions / methods | camelCase                              | `fetchUserProfile()`    |
| Files and directories           | snake_case                             | `home_bloc.dart`        |
| Constants (compile-time)        | SCREAMING_SNAKE_CASE in constant class | `AppStrings.APP_NAME`   |
| Boolean variables               | Verb prefix                            | `isLoading`, `hasError` |

- Start every method/function name with a descriptive **verb**.
- Use complete words; abbreviate only established terms (`API`, `URL`, `OTP`, `FCM`).

### Functions & Methods

- Keep functions **< 20 statements**; extract helpers when a function grows beyond that.
- Use **early returns** instead of deeply nested `if/else` blocks.
- Prefer **named parameters** with `required` for functions that accept multiple arguments.

### Classes

- Follow SOLID principles; one responsibility per class.
- Keep classes **< 200 lines** with **< 10 public methods**.
- Prefer **composition** over inheritance.

---

## Feature-First Architecture

Every feature lives under `lib/features/<feature_name>/` and follows this structure:

```
features/
└── feature_name/
  ├── bloc/          # Bloc + Event + State (split into separate part files)
  ├── model/         # Request/response DTOs  (fromJson / toJson)
  ├── repo/          # Repository — wraps DioUtil calls
  ├── view/          # Page widgets (one per route)
  └── widget/        # Small, reusable widgets scoped to this feature
```

Shared code lives in `lib/core/`:

```
core/
├── components/        # Reusable UI widgets (buttons, dialogs…)
├── constants/         # AppStrings, AppColors, AppIcons, AppDimensions, enums
├── data/
│   ├── network/       # DioUtil, ApiConfig, ApiResponse, SecureStorageHelper
│   └── shared_preferences/
├── models/            # Cross-feature domain models (e.g. UserModel)
├── router/            # CustomRouter, RouteConstants, export.dart (barrel)
├── services/          # NavigationService
├── theme/             # AppTheme
├── types/             # Result<T>, Success<T>, Failure<T>
└── utils/             # DebugUtils, ApplicationUtils, AppBlocObserver, mixins
```

> **Important:** always import via the barrel file:
>
> ```dart
> import '../../../../core/router/export.dart';
> ```

---

## Bloc Pattern

### File Layout

Each Bloc is split into three `part` files:

```
feature/bloc/
├── feature_bloc.dart   ← Bloc class
├── feature_event.dart  ← Events  (part of 'feature_bloc.dart')
└── feature_state.dart  ← State   (part of 'feature_bloc.dart')
```

### State

- Base state extends `Equatable`.
- Use an **enum** for status: at minimum `initial`, `loading`, `success`, `error`.
- Provide a full **`copyWith`** method.
- Override `List<Object?> get props` — include every field.
- `@immutable final class` for the state.
- Do **not** store passwords, tokens, or credentials in state.

### Events

- `@immutable final class` for every event.
- Events extend the sealed base `extends <Feature>Event`.
- Name events `<Verb><Noun>Event`: `SubmitLoginEvent`, `LoadHomeDataEvent`.
- `props` must **never** include passwords or credentials.

### Bloc Class

- Constructor-inject repo with a nullable fallback:
`FeatureBloc({FeatureRepo? repo}) : _repo = repo ?? FeatureRepo()`.
- Register handlers: `on<EventType>(_onEventName)`.
- Handler signature: `Future<void> _onEventName(Event event, Emitter<State> emit) async`.
- Always emit `loading` **first** inside an async handler.
- Always emit a terminal state (`success` or `error`) in **every** code path including `catch`.
- Log errors: `DebugUtils.showPrint('FeatureBloc._onX error: $e')`.
- **Never** perform navigation or show dialogs inside a Bloc.

---

## Repository Pattern

- Constructor-inject `DioUtil` with a nullable fallback:
`FeatureRepo({DioUtil? dioUtil}) : _dioUtil = dioUtil ?? DioUtil()`.
- **Reads** → return `Future<T?>` (call `showSnackBar` on failure, return `null`).
- **Writes** → return `Future<Result<T>>` (`Success<T>` / `Failure`).
- All endpoint paths must come from `ApiConfig` constants.
- Wrap every network call in `try/catch`; log with `DebugUtils.showPrint`.
- Never `rethrow` unless the caller is designed to handle it.

---

## Models (DTOs)

- `@immutable`, all fields `final`.
- `factory fromJson(Map<String, dynamic>)` + `toJson()`.
- Null-safe defaults: `?? ''`, `?? 0`, `?? false`, `?? []`.
- Never declare a field as `dynamic` — cast JSON values explicitly.
- Request models in `feature/model/` named `<Noun>RequestModel`.
- Response models in `feature/model/` named `<Noun>ResponseModel`.

---

## Navigation

- Use `NavigationService` for all programmatic navigation.
- Use `RouteConstants` for all route name strings — never inline strings.
- Pass typed argument objects, not raw `Map`s.

---

## UI / Widgets

- All widgets must use `const` constructors where possible.
- Use `flutter_screenutil` (`16.w`, `20.h`, `14.sp`) for all dimensions.
**Never** use `MediaQuery` for sizing; **never** use raw doubles.
- Use `AppColors` for every colour — never hardcode hex/RGB.
Never use `.withOpacity()` — encode alpha into the constant.
- Use `AppStrings` for every user-facing string.
- Use `AppIcons` for every asset path.
- Use `AppDimensions` for all spacing/size constants.
- All `TextStyle`s must use named `FontStyles` getters from
`lib/core/constants/fonts/font_styles.dart` — no inline `TextStyle(...)`.
- Use `TextWidget` for all text display — no raw `Text(...)` in screen widgets.
- Use `CustomButton` for primary actions.
- Use `BlocBuilder` for state-driven UI; `BlocListener` for side-effects.
- Use `BlocConsumer` only when both building and listening are needed.
- Never use `print()` — use `DebugUtils.showPrint()`.
- Feedback to users: `showSnackBar(message, SnackType.success/failed)`.
- Extract logical sub-trees into `StatelessWidget` classes in `widget/`.
- Always pass a `key` to list items.

---

## Error Handling & Result Type

Use `Result<T>` / `Success<T>` / `Failure` from `core/types/result.dart` for any write
operation. Switch on the result in the Bloc handler:

```dart
switch (result) {
case Success(:final data):
  emit(state.copyWith(status: FeatureStatus.success, data: data));
case Failure(:final message):
  emit(state.copyWith(status: FeatureStatus.error, message: message));
}
```

---

## Security

### Secrets & Keys

- **Never** hardcode API keys, tokens, or secrets in Dart source.
- Inject all secrets at build time via `--dart-define=KEY=value`; read with
`String.fromEnvironment('KEY')`.
- **`AppConfig.environment`** is set at build time — never change it at runtime.

### Local Storage

- Use `SecureStorageHelper` for **all** security-sensitive values: auth token,
refresh token, FCM token.
- Use `SharedPreferenceHelper` only for non-sensitive app state (theme, onboarding).
- **Never** store a password or credential in `SharedPreferences` or Bloc state.

### Bloc State

- `props` must never include passwords, tokens, or payment secrets.
- Clear any credential field immediately after the handler that consumed it:
`emit(state.copyWith(password: ''))`.

### WebView

- Validate every URL loaded against `ApiConfig` domain constants before loading.
- Reject URLs whose host is not in the allowlist.

### Network

- All API calls must be HTTPS. Never disable certificate validation.
- URL selection is done via `AppConfig.environment` + `ApiConfig.baseUrl` only.

---

## Reliability

- Every repository method must terminate at a `DioUtil` call — no self-recursion.
- Every `async` function calling the network must have a `try/catch`.
- Emit `ApiMsgStrings.somethingWentWrong` to the UI — never expose raw exception messages.
- Always emit `loading` first; always emit a terminal state in every code path.

### Linting & Formatting
- All code must conform exactly to the project's `analysis_options.yaml` (e.g., `flutter_lints` or `very_good_analysis`).
- Never generate code that requires `// ignore:` comments.
- Always append trailing commas `,` to Flutter widget properties to ensure proper `dart format` structure.

### Observability
- `DebugUtils.showPrint` is for debug strings only. For `catch(e, stackTrace)` blocks, pass the exception and stack trace to your designated crash reporter service facade.

### Cubit vs Bloc
- Default to `Cubit` for straightforward state changes. Only use `Bloc` when you specifically need `Event` transformers (e.g., debounce, throttle, droppable).

### UI & E2E Testing
- **Maestro (E2E Flows)**: Use Maestro instead of Flutter integration tests for testing full user journeys, especially those involving native OS permission dialogs (Camera, Biometrics, Vault).
- **Semantics**: Every intractable UI component must be wrapped in `Semantics(identifier: 'specific_name')` to ensure Maestro can reliably tap it during E2E flows. Do not rely on fixed X/Y coordinates.
- **Widget Tests**: Create isolated Widget Tests strictly for custom UI components mapping to Mock Blocs. Mock network images or animations during tests to prevent flaky failures.

---


---

## App Store & Play Store Compliance
To ensure the app passes Apple App Store and Google Play Store reviews, strictly adhere to the following when generating code:

### Permissions & Privacy
- **Just In Time Requesting**: Never request permissions (Camera, Location, Contacts, etc.) at app launch. Request them only when the user initiates a feature that requires them.
- **Rationale Strings**: Ensure that `Info.plist` and `AndroidManifest.xml` have clear, user-friendly rationale strings explaining *why* the permission is needed.
- **Account Deletion**: If generating user profile settings, always include an option to **Delete Account** natively within the app.

### UI / UX & Platform Behaviors
- **Safe Areas**: Always wrap top-level scaffold contents or floating elements in a `SafeArea` to avoid overlaps with the iOS notch, dynamic island, or Android system bars.
- **Back Navigation**: Ensure proper `WillPopScope` or `PopScope` implementations for Android hardware back buttons so users are not trapped on a screen.
- **Accessibility**: Use `Semantics` widgets for custom controls. Ensure text scales correctly with the system text size without overflowing (use `TextOverflow.ellipsis` or scalable flexible layouts).
- **Dark Mode**: Never hardcode colors that would break in Dark Mode. Always use `Theme.of(context).colorScheme` or the predefined `AppColors`.

### Authentication & Payments
- **Sign in with Apple**: If adding any third-party SSO (Google, Facebook), you *must* concurrently implement Sign in with Apple for iOS.
- **Digital Goods**: If generating features that unlock digital content or subscriptions, do not use external payment gateways (like Stripe); use native In-App Purchases (IAP).

## CI/CD & Git Hooks (Pre-Commit)
To enforce code formatting, linting, and reliability before code reaches the repository, a strict pre-commit hook is active.
- **Formatting**: Generated code and tests must output cleanly via `dart format . --set-exit-if-changed`.
- **Linting**: Code must pass `flutter analyze` without any warnings.
- **Tests**: Code must not break existing test cases and must pass `flutter test`.
Never generate code that attempts to circumvent or ignore these rules, as the native git hooks will block all non-compliant commits.

---

## Maintainability

### Single Source of Truth

| Class            | File                                     | Purpose                     |
| ---------------- | ---------------------------------------- | --------------------------- |
| `AppStrings`     | `core/res/strings/app_strings.dart`      | All user-facing strings     |
| `AppColors`      | `core/res/colors/colors.dart`            | All colour values           |
| `AppIcons`       | `core/res/drawables/icons.dart`          | SVG/PNG asset paths         |
| `AppDimensions`  | `core/res/size/size_config.dart`         | Spacing/size constants      |
| `RouteConstants` | `core/routes/app_router.dart`            | Navigation route strings    |
| `ApiConfig`      | `core/data/network/api_config.dart`      | API base URLs and endpoints |
| `ApiMsgStrings`  | `core/res/strings/api_msg_strings.dart`  | API error/success messages  |

### Dependency Direction

- Features must not import each other. Shared models and services live in `core/`.
- Blocs must not import views or widgets.
- Repos must not import Blocs or views.
- `core/` must not import from `features/`.

### Code Size Limits

- Files: **< 300 lines**.
- Functions / methods: **< 20 statements**.
- Classes: **< 200 lines**, **< 10 public methods**.

### Environment Switching

Control via `--dart-define=ENV=dev|uat|prod` at build time.
Never merge code where `baseUrl` is hardcoded to a dev or UAT value.

---

## Coverage

- Every public Bloc event handler must have a corresponding `blocTest`.
- Minimum per handler: (1) happy path, (2) null/empty response, (3) exception thrown.
- Use `flutter_test` + `bloc_test` + `mocktail`. Mock repos via constructor injection.
- Never use real network calls in unit tests.
- Follow **Arrange–Act–Assert**; prefix test variables `input`, `mock`, `actual`, `expected`.

---

## Automation – Prompt Files

Use the prompt files in `.github/skills/` as the entry point for structured work:

| Task                                         | Prompt file                          |
| -------------------------------------------- | ------------------------------------ |
| **Full e2e (Figma + AC + API → prod-ready)** | `create-feature-e2e.prompt.md`       |
| Maestro E2E Test Flows                       | `create-maestro-flow.prompt.md`      |
| Deep Link / Routing Architecture             | `create-deep-link.prompt.md`         |
| New feature (full stack, no Figma)           | `create-feature.prompt.md`           |
| Screen from Figma only                       | `create-screen-from-figma.prompt.md` |
| Bloc only                                    | `create-bloc.prompt.md`              |
| Repository only                              | `create-repo.prompt.md`              |
| Model DTOs from JSON                         | `create-model.prompt.md`             |
| Tests for a Bloc                             | `write-tests.prompt.md`              |
| Security / quality audit                     | `security-review.prompt.md`          |
| App Store / Play Store compliance check      | `app-store-compliance.prompt.md`     |

''',
  'skills/create-maestro-flow.prompt.md': r'''
---
agent: agent
description: Generate comprehensive, modular Maestro E2E test flows (YAML) for testing critical user journeys, strictly adhering to Flutter Semantics and native OS interaction rules.
argument-hint: "Flow name, target feature, and step-by-step user journey description"
---

# Create Maestro E2E Flow

Flow Name: `${input:flowName}` | Feature: `${input:targetFeature}`
Journey:
```
${input:journeyDescription}
```

## File Locations

All Maestro E2E flows belong in the root-level `.maestro/` directory.

```
.maestro/
├── common/             (For reusable sub-flows like login.yaml)
└── flows/
  └── ${input:flowName}.yaml
```

---

## Step 1 – The Boilerplate

Every Maestro YAML file must start with the `appId` declaration and basic setup, heavily relying on environment variables so tests can run on both Android and iOS dynamically.

```yaml
appId: ${APP_ID}
env:
# Define default variables if they aren't passed by the CLI
EMAIL: "test@example.com"
---
# ── Setup / Initialization ──
- clearState: true
- launchApp:
  clearState: true
```

---

## Step 2 – Reusable Flow Stitching (`runFlow`)

If the journey requires the user to be logged in, **never** manually rewrite the login steps. Always use `- runFlow` to stitch common modules together.

```yaml
# ── Pre-requisites ──
- runFlow:
  file: ../common/login_flow.yaml
  env:
    USER: ${EMAIL}
```

---

## Step 3 – The Journey Steps (The "Flutter" Rules)

Translate the `${input:journeyDescription}` into Maestro commands.

### CRITICAL Flutter Rules:
1. **Never use brittle X/Y coordinates**: Do not use `- tapOn: point: 100,200`.
2. **Prefer Semantics over raw text**: Flutter paints text onto a canvas. Maestro often struggles to read custom fonts. ONLY use `- tapOn: "Text"` if it is standard. Otherwise, rely on Flutter `Semantics` labels: `- tapOn: id: "btn_submit_document"`.
3. **If targeting an ID**, you **MUST** ensure the corresponding Flutter widget actually has a semantic identifier. (e.g. `Semantics(identifier: 'btn_submit_document', child: ...)`).
4. **Native OS Dialogs**: FormFill Vault extensively uses OS permissions (Camera, Storage, Biometrics). If the journey triggers a permission, blindly assume the native OS dialog appears and accept it.

```yaml
# ── The Action ──
- tapOn: id: "btn_add_document"

# ── Handle OS Permissions (Android/iOS independent) ──
- tapOn: "Allow.*"   # Regex matches "Allow", "Allow Access", "While using the app", etc.

# ── Dynamic Input ──
- tapOn: id: "input_document_title"
- inputText: "Aadhaar Card"
- hideKeyboard

# ── Scrolling & Asserting ──
- scrollUntilVisible:
  element: id: "btn_save_vault"
  direction: DOWN
- tapOn: id: "btn_save_vault"

# ── Validate Success ──
- assertVisible: "Document saved successfully"
```

---

## Step 4 – Assertions and Teardown

Every E2E Flow must end with an incontrovertible visual assertion that the goal was achieved, followed by an optional teardown.

```yaml
# ── Conclusion ──
- extendedWaitUntil:
  visible: id: "vault_list_item_aadhaar"
  timeout: 5000 # Wait up to 5 seconds for the DB to save and UI to refresh
- assertVisible: "Aadhaar Card"
```

---

## Output Validation & Rules

Before generating the final YAML, ensure:
1. All `id:` targets use snake_case strings.
2. You explicitly output a reminder to the developer: *"Please ensure `Semantics(identifier: '...')` was added to your Flutter Widgets, otherwise Maestro cannot tap these IDs."*
3. You include the exact terminal command to run the flow: e.g., `maestro test .maestro/flows/${input:flowName}.yaml -e APP_ID=com.example.formfill`

''',
  'skills/app-store-compliance.prompt.md': r'''
# App Store & Play Store Compliance Review

Review the provided code or feature implementation to ensure it strictly complies with both the **Apple App Store Review Guidelines** and the **Google Play Store Policies**. If any violations are found, point them out explicitly and suggest refactors.

## Core Reference Documentation
- **Apple App Store Review Guidelines**: [developer.apple.com/app-store/review/guidelines/](https://developer.apple.com/app-store/review/guidelines/)
- **Apple Human Interface Guidelines (HIG)**: [developer.apple.com/design/human-interface-guidelines](https://developer.apple.com/design/human-interface-guidelines/)
- **Google Play Developer Policy Center**: [play.google.com/about/developer-content-policy](https://play.google.com/about/developer-content-policy/)
- **Material Design Guidelines**: [m3.material.io](https://m3.material.io/)

---

## 1. Permissions & Privacy
*Ref: Apple Guidelines 5.1.1 (Data Collection and Storage) | Google Play User Data Policy*

- **Just-In-Time Requesting**: Verify that permissions (Camera, Location, Contacts, Photo Library, Microphone, etc.) are NOT requested at app launch. They must only be requested when the user explicitly triggers a feature that requires them.
- **Rationale Strings**: Ensure that `Info.plist` and `AndroidManifest.xml` have clear, user-friendly strings explaining exactly *why* the permission is needed (e.g., `NSCameraUsageDescription`, `NSPhotoLibraryUsageDescription`).
- **Account Deletion**: If the code scaffolded involves user profiles or account settings, verify that a clear, native option to **Delete Account** is included. *Ref: Apple Guideline 5.1.1(v) | Google Play Data Deletion requirement.*

## 2. UI / UX & Platform Behaviors
*Ref: Apple Guidelines 4.0 (Design) | Google Play Core App Quality*

- **Safe Areas**: Verify that top-level screens or floating elements are wrapped in a `SafeArea` widget to prevent overlapping with the iOS notch, dynamic island, or Android system bars.
- **Hardware Back Navigation**: Check that Android hardware back button behavior is respected (using `WillPopScope` or the newer `PopScope` API) so users do not get trapped.
- **Accessibility (a11y)**: Check that custom controls use `Semantics` widgets appropriately. Verify text scaling is supported without overflowing (e.g., using flexible layouts, wrapping, or `TextOverflow.ellipsis`).
- **Dark Mode Support**: Ensure colors are not hardcoded in ways that break when the system swaps to Dark Mode. Colors should come from `Theme.of(context).colorScheme` or the predefined `AppColors` palette.

## 3. Monetization & Authentication
*Ref: Apple Guidelines 3.1.1 (In-App Purchase) & 4.8 (Sign in with Apple) | Google Play Payments Policy*

- **Sign in with Apple**: If the code adds third-party single sign-on (SSO) like Google or Facebook, ensure that **Sign in with Apple** is also implemented. Apple will reject the app otherwise.
- **Native Payments (IAP)**: If the feature unlocks premium digital content, subscriptions, or features, ensure that native In-App Purchases (IAP) are used rather than third-party gateways (like Stripe or PayPal) which violate both platform's core policies for digital goods.

## 4. Content & Data Handling
*Ref: Apple Guidelines 1.2 (User-Generated Content) | Google Play Spam and Minimum Functionality*

- **User-Generated Content (UGC)**: If the app displays UGC, verify there is functionality to report abusive content and block users.
- **External Links**: If the app links to external websites, ensure these links do not bypass native purchasing or hide prohibited content.
- **Secure Storage**: Ensure sensitive data (passwords, tokens) is never logged in plaintext and is only saved using `SecureStorageHelper` rather than basic `SharedPreferences`.

---

**Instructions to AI:**
1. Read through the candidate code.
2. Cross-reference the implementation against the 4 categories and the provided reference links above.
3. List any potential Compliance Risks.
4. Output the corrected, store-compliant code.

''',
  'skills/create-feature.prompt.md': r'''
---
agent: agent
description: Scaffold a complete feature folder (bloc/model/repo/view/widget) from acceptance criteria following project architecture standards.
argument-hint: "Feature name in snake_case, PascalCase variant, parent flow folder, and acceptance criteria"
---

# Create Feature

Feature: `${input:featureName}` | PascalCase: `${input:featurePascalName}` | Flow: `${input:parentFolder}`

## Acceptance Criteria

```
${input:acceptanceCriteria}
```

> **Before writing any code**, parse the acceptance criteria and derive:
>
> | Concern                   | How to derive from AC                                                                      |
> | ------------------------- | ------------------------------------------------------------------------------------------ |
> | **Events**                | One event per user action or system trigger (`SubmitBookingEvent`, `LoadProfileDataEvent`) |
> | **State status variants** | One enum value per observable UI state beyond `initial/loading/success/error`              |
> | **API calls**             | One repo method per data exchange described                                                |
> | **Edge cases**            | Every "when X fails / empty / unauthorised" clause → error state + snackbar                |
> | **Navigation**            | Each "navigates to …" clause → `NavigationService` call in `BlocListener`                  |
> | **Validation**            | Each "must / cannot / required" clause → guard before API call                             |

**Read before writing any string/colour/icon/style/route:**
[app_strings.dart](../../lib/core/res/strings/app_strings.dart) ·
[colors.dart](../../lib/core/res/colors/colors.dart) ·
[icons.dart](../../lib/core/res/drawables/icons.dart) ·
[font_style.dart](../../lib/core/res/fonts/font_style.dart) ·
[size_config.dart](../../lib/core/res/size/size_config.dart) ·
[app_router.dart](../../lib/core/routes/app_router.dart) ·
[api_config.dart](../../lib/core/data/network/api_config.dart)

## Folder structure

```
lib/features/${input:parentFolder}/${input:featureName}/
├── bloc/  ${input:featureName}_{bloc,event,state}.dart
├── model/ ${input:featureName}_{request,response}_model.dart  (if API needed)
├── repo/  ${input:featureName}_repo.dart
├── view/  ${input:featureName}_view.dart
└── widget/
```

## Per-file rules

**Bloc** — barrel import only; `part` both sibling files; constructor-inject repo
(`?? ${input:featurePascalName}Repo()`); `on<E>(_handler)` per event; emit `loading` (with `clearMessage: true`) →
`try`/`catch (e, stackTrace)` → emit `success`/`error`; log with `DebugUtils.showPrint` passing `stackTrace`; no navigation or UI.

**Event** — `part of` bloc; `@immutable sealed class ${input:featurePascalName}Event extends Equatable`;
each event `@immutable final class <Verb><Noun>Event`; `props` for all non-sensitive fields.

**State** — `part of` bloc; `enum ${input:featurePascalName}Status { initial, loading, success, error }`;
`@immutable final class` state extends `Equatable`; all `final` fields; full `copyWith` with `clearMessage` flag; `props`.

**Repo** — barrel import; constructor-inject `DioUtil ?? DioUtil()`; ALL operations (reads & writes) → `Future<Result<T>>`
(strictly decoupled from UI — no snackbars); all paths from `ApiConfig`;
`try`/`catch (e, stackTrace)` + inner JSON parsing `try`/`catch` + `DebugUtils.showPrint`.

**Models** — `@immutable`, all `final`; `fromJson` factory + `toJson`; null-safe defaults;
never declare a field as `dynamic`.

**View** — wrap scaffold body in `SafeArea` + `SingleChildScrollView`; `BlocProvider` at root fires initial event if needed; `BlocListener` for
side-effects (navigation, snackbars); `BlocBuilder` for UI; every string → `AppStrings`,
colour → `AppColors`, text → `TextWidget`, button → `CustomButton` (wrap custom tap targets in `Semantics(button: true)`), dims → `AppDimensions`.

- ScreenUtil; extract sub-trees to `widget/`.

**Route** — add `static const` to `RouteConstants`; add `case` in `CustomRouter.generateRoute`.

**Exports** — add cross-feature exports to `export.dart`.

**AC coverage check** — after generating all files, verify every AC clause is handled.

**Git Hooks Compliance** — unconditionally pass `flutter analyze`; NEVER output deprecated widgets (e.g. `WillPopScope`).

No `TODO` comments — implement minimally but completely.

''',
  'skills/create-screen-from-figma.prompt.md': r'''
---
agent: agent
description: Generate a Flutter screen and widgets from a Figma node, mapping Figma design tokens to project constants (AppColors, FontStyles, AppDimensions, AppIcons, AppStrings).
argument-hint: "Figma file key, Figma node ID, feature name, output path"
---

# Create Screen from Figma

Feature: `${input:featureName}` | PascalCase: `${input:featurePascalName}`
Figma file key: `${input:figmaFileKey}`
Figma node ID: `${input:figmaNodeId}`

---

## Step 1 – Fetch Figma data

Call Figma MCP `get_figma_data`:

- `fileKey`: `${input:figmaFileKey}`
- `nodeId`: `${input:figmaNodeId}`
- Retrieve the full node tree including children, styles, and component variants.

Parse the response and extract every design token present:

| Token type               | Figma value      | Notes                                |
| ------------------------ | ---------------- | ------------------------------------ |
| fill colour              | `#RRGGBB` / rgba | map to `AppColors.*`                 |
| stroke colour            | `#RRGGBB` / rgba | map to `AppColors.*`                 |
| font family              | string           | must match `FontFamily.*`            |
| font size                | number           | map to nearest `FontStyles.*` getter |
| font weight              | number           | map to nearest `FontStyles.*` getter |
| line height              | number/%         | map to nearest `FontStyles.*` getter |
| corner radius            | number           | map to `AppDimensions.radius*`       |
| horizontal padding       | number           | map to `AppDimensions.*`             |
| vertical padding/spacing | number           | map to `AppDimensions.*`             |
| icon/image node          | nodeId           | schedule for download                |
| visible text             | string           | map to `AppStrings.*`                |

---

## Step 2 – Build Design Token Map & download assets

For each token extracted in Step 1, produce:

| Token type | Figma value    | Project constant         | Action                 |
| ---------- | -------------- | ------------------------ | ---------------------- |
| colour     | `#2196F3`      | `AppColors.primary`      | use                    |
| colour     | `#FF0000`      | —                        | **add** to `AppColors` |
| text       | "Welcome back" | `AppStrings.welcomeBack` | use / **add**          |
| icon       | node `1234:56` | `AppIcons.someIcon`      | **download** + add     |

**Rules:**

- A token already present in a project constant → **use** it.
- A token not yet in project constants → **add** it to the relevant constants file, following naming conventions (see project `copilot-instructions.md`). Never hardcode values in widget files.
- Colours: never use `.withOpacity()` — encode alpha into the constant hex value.
- For icon/image nodes: call Figma MCP `download_figma_images` with those node IDs and save to `assets/icons/` or `assets/images/`. Register new asset paths under `flutter: assets:` in `pubspec.yaml` and add a constant to `AppIcons`.

---

## Step 3 – Apply constants

Update the following files as needed (only add what is new — never modify existing constants):

- `lib/core/res/colors/colors.dart` — new `AppColors.*` entries
- `lib/core/res/strings/app_strings.dart` — new `AppStrings.*` entries
- `lib/core/res/drawables/icons.dart` — new `AppIcons.*` entries
- `lib/core/res/fonts/font_style.dart` — new `FontStyles.*` getter (only if a genuinely new style is needed; prefer composing `.copyWith()` on existing getters in widget code)
- `lib/core/res/size/size_config.dart` — new `AppDimensions.*` getter (only if spacing value has no existing equivalent)
- `pubspec.yaml` — new asset entries

---

## Step 4 – Generate the view

Create `lib/features/${input:parentFolder}/${input:featureName}/view/${input:featureName}_view.dart`.

**Layout rules (derived from Figma node tree):**

- **SafeArea & Scrolling**: Wrap the outermost screen scaffold body in a `SafeArea` to respect iPhone Notches and Android system bars. Assume ALL screens need to scroll on smaller devices; wrap the primary `Column` in a `SingleChildScrollView` to prevent pixel overflows.
- **Input Intelligence**: If a Figma component visually represents an input area (a border box with placeholder text), intelligently replace it with a native `TextFormField` rather than generating a static painted `Container`.
- Replicate the Figma layout hierarchy: frames → `Column`/`Row`/`Stack`; auto-layout direction → `Column`/`Row`; auto-layout gap → `SizedBox(height: ...)` / `SizedBox(width: ...)`.
- **Text Overflow**: Wrap text-heavy `Row` children in `Expanded` or use `TextOverflow.ellipsis` to gracefully handle large system font scalings.
- All sizes use ScreenUtil: widths → `.w`, heights → `.h`, font sizes → `.sp`, radii → direct `AppDimensions.*` getter value.
- All colours → `AppColors.*`. All text → `TextWidget(text: ..., style: FontStyles.*)`. All buttons → `CustomButton(...)`.
- Complex or repeated sub-trees (≥ 3 widgets) → extract to `widget/` (Step 5).
- Strictly follow all _View_ rules from `create-feature.prompt.md`.

**BLoC wiring:**

- Wrap screen root in `BlocProvider(create: (_) => FeatureBloc())`.
- State-driven UI → `BlocBuilder<FeatureBloc, FeatureState>`.
- Side effects (navigation, snackbars) → `BlocListener<FeatureBloc, FeatureState>`.
- Both → `BlocConsumer`.
- Dispatch events matching the Feature Design Table from `create-feature.prompt.md`.

---

## Step 5 – Extract widgets

For each identified sub-tree:

1. Create `lib/features/${input:parentFolder}/${input:featureName}/widget/<widget_name>_widget.dart`.
2. Widget is `StatelessWidget` wherever possible; `StatefulWidget` only for locally animated or form-field sub-trees.
3. **Accessibility & Maestro**: Every extracted button, custom interactive container, or touch target MUST be wrapped in a `Semantics(identifier: 'specific_btn_name', button: true)` widget to comply with screen readers AND allow Maestro E2E tests to locate them. It must also guarantee a tap target of at least 48x48.
4. Accept only the minimal typed parameters needed — no passing of entire state objects.
5. **Performance Purity**: Aggressively apply `const` keywords to every generic widget to ensure 60fps scrolling.
6. Pass a `key` parameter.

---

## Final UI Check

- **Git Hooks Compliance**: The final generated code must unconditionally pass `flutter analyze` and `dart format`.
- **No Deprecations**: NEVER output deprecated layout widgets (e.g., use `PopScope` over `WillPopScope`).
- No `TODO` comments in generated files — implement completely or leave the slot empty with a `// placeholder` comment explaining what goes there.

''',
  'skills/create-repo.prompt.md': r'''
---
agent: agent
description: Create a Repository class for a feature following the project's DioUtil + Result<T> conventions.
argument-hint: "Feature name in snake_case, PascalCase variant, and list of API methods needed with HTTP verb and endpoint"
---

# Create Repository

Feature: `${input:featureName}` | PascalCase: `${input:featurePascalName}`

API methods needed:

```
${input:apiMethods}
```

## File location

```
lib/features/<parentFolder>/${input:featureName}/repo/${input:featureName}_repo.dart
```

## Template

```dart
import '../../../../core/router/export.dart';

class ${input:featurePascalName}Repo {
${input:featurePascalName}Repo({DioUtil? dioUtil}) : _dioUtil = dioUtil ?? DioUtil();

final DioUtil _dioUtil;

// WRITE operation (POST/PUT/DELETE) — returns Result<T>
Future<Result<ResponseType>> someWriteMethod({
  required RequestModel requestModel,
}) async {
  try {
    final Map<String, dynamic>? response = await _dioUtil.postApi(
      url: ApiConfig.someEndpoint,
      body: requestModel.toJson(),
      showMessage: false,
    );
    if (response == null) {
      return const Failure(message: ApiMsgStrings.somethingWentWrong);
    }

    // Wrap parsing in try/catch to gracefully handle JSON mapping crashes
    try {
      return Success(data: ResponseType.fromJson(response));
    } catch (e, stackTrace) {
      DebugUtils.showPrint('${input:featurePascalName}Repo.someWriteMethod JSON Parsing error: $e\n$stackTrace');
      return const Failure(message: ApiMsgStrings.somethingWentWrong);
    }
  } catch (e, stackTrace) {
    DebugUtils.showPrint('${input:featurePascalName}Repo.someWriteMethod network error: $e\n$stackTrace');
    return const Failure(message: ApiMsgStrings.somethingWentWrong);
  }
}

// READ operation (GET) — returns Result<T> (DECOUPLED FROM UI)
Future<Result<ResponseType>> someReadMethod() async {
  try {
    final Map<String, dynamic>? response = await _dioUtil.getApi(
      url: ApiConfig.someEndpoint,
    );
    if (response == null) {
      return const Failure(message: ApiMsgStrings.somethingWentWrong);
    }

    // Wrap parsing in try/catch to gracefully handle JSON mapping crashes
    try {
      return Success(data: ResponseType.fromJson(response));
    } catch (e, stackTrace) {
      DebugUtils.showPrint('${input:featurePascalName}Repo.someReadMethod JSON Parsing error: $e\n$stackTrace');
      return const Failure(message: ApiMsgStrings.somethingWentWrong);
    }
  } catch (e, stackTrace) {
    DebugUtils.showPrint('${input:featurePascalName}Repo.someReadMethod network error: $e\n$stackTrace');
    return const Failure(message: ApiMsgStrings.somethingWentWrong);
  }
}
}
```

## Rules

- **Constructor-inject** `DioUtil` with `?? DioUtil()` fallback for testability.
- **Unified Return Type**: ALL operations (READs & WRITEs) must exclusively return `Future<Result<T>>`.
- **API Response Parsing Safety**: Always wrap `ResponseType.fromJson(response)` in an inner `try/catch` to gracefully catch and report `TypeError` or `FormatException`.
- All endpoint paths come from `ApiConfig` constants — never inline URL strings.
- **Catch Blocks & Observability**: Always catch `(e, stackTrace)` so that the full context is logged and can be handled by a crash reporter. Use `DebugUtils.showPrint('ClassName.methodName error: $e\n$stackTrace')`.
- **Never** `rethrow` exceptions into the void. Always convert exceptions to `Failure(message)`.
- **Clean Architecture Ban**: You must **never** import `Bloc`, `event`, `state`, `snackbars`, `dialogs`, or `view` files into the repository. The data layer must remain 100% headless and completely decoupled from UI execution.
- Use barrel import `import '../../../../core/router/export.dart'`.
- Every repo method must terminate at a `DioUtil` call — no self-recursion or retry loops.

''',
  'skills/create-feature-e2e.prompt.md': r'''
---
agent: agent
description: End-to-end feature scaffold driven by acceptance criteria, a Figma design, and API request/response models. Orchestrates all sub-prompts in sequence to produce the complete bloc/model/repo/view/widget/test suite.
argument-hint: "Feature name, PascalCase variant, parent flow folder, Figma file key, Figma node ID, acceptance criteria, API request JSON, API response JSON"
---

# End-to-End Feature

Feature: `${input:featureName}` | PascalCase: `${input:featurePascalName}` | Flow: `${input:parentFolder}`
Figma file key: `${input:figmaFileKey}`
Figma node ID: `${input:figmaNodeId}`

## Acceptance Criteria

```
${input:acceptanceCriteria}
```

## API Contract

**Request:**

```json
${input:requestJson}
```

**Response:**

```json
${input:responseJson}
```

---

## How this prompt works

This is an **orchestrator**. Each phase below delegates to a dedicated sub-prompt that owns
the detailed rules for that concern. Read each linked sub-prompt **in full** before executing
its phase — the rules there are the authoritative source of truth. Do not re-interpret or
relax them here.

Work through phases **strictly in order**. Do not start Phase N+1 until Phase N is complete.

**CRITICAL:** To prevent token truncation and maintain code quality, you **MUST** naturally pause at the end of every Phase. Output your progress and explicitly ask the user to type "continue" before proceeding to the next Phase.

---

## Phase 1 – Parse & Plan (no code)

Before touching any file:

1. **Derive the Feature Design Table** from the acceptance criteria using the derivation rules
 in [create-feature.prompt.md](./create-feature.prompt.md) (the _Acceptance Criteria_ section).

 | #   | AC Clause | Event name | State status variant | Repo method | Edge case / guard |
 | --- | --------- | ---------- | -------------------- | ----------- | ----------------- |

2. **Fetch the Figma design** — call Figma MCP `get_figma_data`:
 - `fileKey`: `${input:figmaFileKey}`
 - `nodeId`: `${input:figmaNodeId}`

3. **Build the Design Token Map** following Steps 1–2 in
 [create-screen-from-figma.prompt.md](./create-screen-from-figma.prompt.md).

 | Token type | Figma value | Project constant | Action (use / add) |
 | ---------- | ----------- | ---------------- | ------------------ |

4. **Download assets** via Figma MCP `download_figma_images` for any icon/image nodes
 identified in the token map.

5. **Derive the model fields** from `${input:requestJson}` and `${input:responseJson}`.

Present the Feature Design Table, Design Token Map, and model field list. Do not proceed
until all three are complete.

---

## Phase 2 – Constants & Assets

Apply the token-mapping rules from
[create-screen-from-figma.prompt.md](./create-screen-from-figma.prompt.md) (Step 3).

Update only what is new — never modify existing constants:

- `lib/core/constants/colors/colors.dart`
- `lib/core/constants/strings/strings.dart`
- `lib/core/constants/drawable/icons.dart`
- `lib/core/constants/fonts/font_styles.dart`
- `lib/core/constants/app_dimensions.dart`
- `pubspec.yaml` (new asset paths only)

---

## Phase 3 – Models

Follow every rule in [create-model.prompt.md](./create-model.prompt.md).

Context:

- `modelName` → `${input:featurePascalName}`
- `modelType` → derive from API contract (request / response / both)
- `jsonOrFields` → use `${input:requestJson}` and `${input:responseJson}`
- Output path → `lib/features/${input:parentFolder}/${input:featureName}/model/`

**Code Generation Rule:** If using `json_serializable` or `freezed`, ensure the model declares the appropriate `part` file. After providing the code, remind the user to run `dart run build_runner build --delete-conflicting-outputs`.

---

## Phase 4 – Repository

Follow every rule in [create-repo.prompt.md](./create-repo.prompt.md).

Context:

- `featureName` → `${input:featureName}`
- `featurePascalName` → `${input:featurePascalName}`
- `apiMethods` → the _Repo method_ + _Edge case_ columns from the Feature Design Table
- API endpoint constant name → add to `lib/core/data/network/api_config.dart` if not present

---

## Phase 5 – Bloc

Follow every rule in [create-bloc.prompt.md](./create-bloc.prompt.md).

Context:

- `featureName` → `${input:featureName}`
- `featurePascalName` → `${input:featurePascalName}`
- `description` → the full _Event name_ + _State status variant_ + _Edge case_ columns
of the Feature Design Table

The `${input:featurePascalName}Status` enum must include every variant listed in the Feature
Design Table in addition to the baseline `initial, loading, success, error`.

---

## Phase 6 – View & Widgets

Follow the view-generation rules in
[create-screen-from-figma.prompt.md](./create-screen-from-figma.prompt.md) (Steps 4–5)
**combined with** the _View_ bullet from
[create-feature.prompt.md](./create-feature.prompt.md) (the _Per-file rules_ → View section).

Additional wiring from AC:

- Every _"navigates to …"_ clause → `BlocListener` branch calling `NavigationService` with `RouteConstants.*`
- Every UI state clause → `BlocBuilder` / `BlocSelector` branch
- Bloc events dispatched from the UI must match the Feature Design Table exactly

Extract every sub-tree of ≥ 3 widgets or reused in siblings into a `widget/` file per
the rules in [create-screen-from-figma.prompt.md](./create-screen-from-figma.prompt.md) (Step 5).

---

## Phase 7 – Route & Exports

Follow the _Route_ and _Exports_ bullets from
[create-feature.prompt.md](./create-feature.prompt.md) (the _Per-file rules_ section):

- Add `static const String ${input:featureName} = '/${input:featureName}';` to `RouteConstants`
- Add a `case RouteConstants.${input:featureName}:` to `CustomRouter.generateRoute`
- Add cross-feature exports to `lib/core/router/export.dart`

---

## Phase 8 – Tests

Follow every rule in [write-tests.prompt.md](./write-tests.prompt.md).

Context:

- Bloc file → `lib/features/${input:parentFolder}/${input:featureName}/bloc/${input:featureName}_bloc.dart`
- Test file → `test/features/${input:featureName}/bloc/${input:featureName}_bloc_test.dart`
- Every row of the Feature Design Table must map to at least one `blocTest` case
- Minimum 3 tests per handler: happy path, failure/null response, exception thrown

---

## Phase 9 – Security, Quality & Compliance Gate

Run [security-review.prompt.md](./security-review.prompt.md) **AND** [app-store-compliance.prompt.md](./app-store-compliance.prompt.md) scoped to all files generated
or modified in Phases 2–8.

Additionally verify:

- [ ] Every AC clause is covered by at least one test from Phase 8
- [ ] Every Figma token from Phase 1 is accessed through a project constant — no inlined hex/rgba/dp values in widget code
- [ ] Every row of the Feature Design Table is implemented across Bloc, Repo, and View
- [ ] `props` in all events/states with passwords or tokens → those fields are excluded
- [ ] No `http` package usage — only `DioUtil`
- [ ] No `print()` — only `DebugUtils.showPrint()`
- [ ] All sensitive values stored in `SecureStorageHelper`, not `SharedPreferenceHelper`
- [ ] All routes use `RouteConstants.*` — no inline string literals
- [ ] All colours use `AppColors.*` — no hardcoded hex
- [ ] All strings use `AppStrings.*` — no hardcoded user-visible text
- [ ] All dimensions use `AppDimensions.*` + ScreenUtil — no raw doubles
- [ ] File sizes: views < 300 lines, blocs < 200 lines, repos < 200 lines
- [ ] **Git Hooks Check**: Ensure generated code strictly follows `dart format .` and passes `flutter analyze` unconditionally. Never output deprecated widgets.

Do not end until all nine phases are complete and every checklist item passes.

''',
  'skills/create-deep-link.prompt.md': r'''
---
agent: agent
description: Generate deep-linking logic, string parsing, and route constants to securely link external URLs natively into the application's BLoC architecture.
argument-hint: "Deep link path structure (e.g., /document/:id), target route, expected parameters, and trigger actions"
---

# Create Deep Link & Route Engine

Deep Link Path: `${input:deepLinkPath}` | Target Feature: `${input:targetFeature}`
Parameters Expected: `${input:parameters}`

## File Locations

Update the following files to register the deep link:
```
lib/core/routes/app_router.dart
```

---

## Step 1 – Strict Route Constants

Update the routing constants string to explicitly define the parameter structure. Never hardcode routing strings across the app.

```dart
// Inside lib/core/routes/app_router.dart (or equivalent constants file)
static const String ${input:targetFeature}Route = '/${input:targetFeature}';
static const String ${input:targetFeature}DeepLink = '/${input:targetFeature}/:id';
```

---

## Step 2 – Strong-Typed Arguments

When passing parameters from a Deep Link to a screen, **never** pass raw `Map<String, dynamic>`. Always scaffold a strongly-typed Arguments class to prevent runtime crashes.

```dart
@immutable
final class ${input:targetFeature}Args extends Equatable {
const ${input:targetFeature}Args({required this.id});

final String id;

// Defensive parsing from deep-link intent strings
factory ${input:targetFeature}Args.fromUri(Uri uri) {
  return ${input:targetFeature}Args(
    id: uri.pathSegments.last, // Example extraction
  );
}

@override
List<Object?> get props => [id];
}
```

---

## Step 3 – The Routing Interceptor

Inside the `onGenerateRoute` or `CustomRouter.generateRoute` method, add the matching case. If the link is malformed, securely redirect the user to an Error or Home page.

```dart
case RouteConstants.${input:targetFeature}Route:
final args = settings.arguments;

// 1. Validate the arguments strictly
if (args is! ${input:targetFeature}Args) {
  DebugUtils.showPrint('DeepLink Error: Invalid or missing arguments for ${input:targetFeature}');
  return MaterialPageRoute(builder: (_) => const GenericErrorView());
}

// 2. Wrap the destination view in the Feature Bloc and fire the Load event immediately
return MaterialPageRoute(
  builder: (_) => BlocProvider(
    create: (context) => ${input:targetFeature}Bloc(
       repo: ${input:targetFeature}Repo(),
    )..add(Load${input:targetFeature}Event(id: args.id)), // Automatically triggers data fetch
    child: const ${input:targetFeature}View(),
  ),
);
```

---

## Rules

- **Strong Typing**: URL parameters are notoriously unreliable. Treat every deep-link parameter as a potentially malicious/malformed string. Always parse securely using `.tryParse` if looking for integers.
- **Fail Gracefully**: If a user clicks a broken or expired deep link, it must **never crash**. Use a fallback route (e.g. `GenericErrorView` or Home) and log the failure using `DebugUtils`.
- **Pre-fire BLoC Events**: When deep-linking directly into a specific feature (like a Document View), standard practice is to inject the ID into the `BlocProvider` and immediately `.add(LoadEvent(id))` so the screen loads seamlessly.
- **`dynamic` Ban**: Never extract routing settings using `as dynamic`. Use strict `if (args is MyArgs)` type-checking.
- **Observability**: Any failed deep link attempts must be logged to Crashlytics via `DebugUtils.showPrint()`.
- **Testing**: Instruct the developer to test this link using `adb shell am start -W -a android.intent.action.VIEW -d "url"` or `xcrun simctl openurl booted "url"`.

''',
  'skills/security-review.prompt.md': r'''
---
agent: agent
description: Perform a 6-pillar security and quality audit on a feature or file. Returns a finding table with severity ratings.
argument-hint: "Feature name or file path to audit"
---

# Security & Quality Review

Target: `${input:target}`

## Audit pillars

Evaluate every finding against all six pillars. Assign severity using the colour key below,
then emit a consolidated finding table followed by a prioritised fix list.

### 1 — Security

Check against OWASP Top-10 adapted for mobile/Flutter:

| Check                    | What to look for                                                                  |
| ------------------------ | --------------------------------------------------------------------------------- |
| Secrets in source        | Hardcoded API keys, tokens, passwords, base URLs in `.dart` files                 |
| Insecure storage         | Auth tokens / credentials in `SharedPreferences` instead of `SecureStorageHelper` |
| Credential in Bloc state | Password or token field in `props` or stored beyond the handler that consumed it  |
| Plaintext credentials    | Passwords or secrets logged with `print`, `debugPrint`, or `DebugUtils`           |
| WebView URL validation   | URL loaded without checking against `ApiConfig` domain allowlist                  |
| HTTP instead of HTTPS    | Any `http://` in endpoint constants                                               |
| Certificate validation   | `badCertificateCallback` set to `true` or similar bypass                          |
| Injection surface        | Dynamic SQL / shell strings built from user input                                 |
| `dart-define` bypass     | `AppConfig.environment` mutated at runtime                                        |

### 2 — Reliability

| Check                    | What to look for                                                 |
| ------------------------ | ---------------------------------------------------------------- |
| Missing `try/catch`      | `async` function that calls network without `catch`              |
| No terminal state        | Handler that can exit without emitting `success` or `error`      |
| `loading` not first      | Handler emits API call before emitting `loading`                 |
| Raw exception to UI      | `e.toString()` or exception message exposed in state / snackbar  |
| Self-recursion in repo   | Repo method that calls itself                                    |
| `rethrow` without reason | `rethrow` where caller is not designed to handle                 |
| Unawaited futures        | `async` call without `await` or explicit discard (`unawaited()`) |
| Missing stackTrace       | `catch (e)` block that fails to capture and log the `stackTrace` |
| `BuildContext` async gap | Using `context` after an `await` without `if (!context.mounted) return;` |
| Memory Leaks             | Missing `dispose()` method for `TextEditingController`, `ScrollController`, or Streams |

### 3 — Maintainability

| Check                      | What to look for                                                |
| -------------------------- | --------------------------------------------------------------- |
| Hardcoded strings          | User-visible strings not in `AppStrings`                        |
| Hardcoded colours / hex    | Colour literals not in `AppColors`                              |
| Raw `Text(...)` widget     | `Text(...)` used instead of `TextWidget`                        |
| Inline `TextStyle`         | `TextStyle(...)` not from `FontStyles` getters                  |
| `MediaQuery` for sizing    | `MediaQuery.of(context).size` used for layout dimensions        |
| Raw doubles for dimensions | `SizedBox(height: 16)` etc. not from `AppDimensions`/ScreenUtil |
| Inline route strings       | Route name strings not from `RouteConstants`                    |
| Cross-feature imports      | Feature A importing directly from Feature B (not via `core/`)   |
| Circular dependency        | `core/` importing from `features/`                              |
| File size                  | Any file exceeding 300 lines                                    |
| Function size              | Any function exceeding 20 statements                            |
| `print` / `debugPrint`     | Raw print calls not using `DebugUtils`                          |
| `dynamic` field            | Model or state fields typed as `dynamic`                        |
| UI in Data Layer           | Repositories returning `Future<T?>` instead of `Result<T>` or importing UI elements like Snackbars |
| Deprecated Widgets         | Usage of widgets triggering `flutter analyze` warnings (e.g. `WillPopScope` instead of `PopScope`) |
| Routing in BLoC            | `Navigator` or `NavigationService` calls happening inside a `_bloc.dart` instead of UI     |
| Missing Semantics          | Custom `GestureDetector` buttons lacking `Semantics` wrappers for screen readers           |
| Obscured / Tiny Targets    | Clickable areas that do not meet 48x48 dp minimum guidelines                               |

### 4 — Test coverage

| Check                 | What to look for                                                     |
| --------------------- | -------------------------------------------------------------------- |
| Missing bloc test     | Public event handler without corresponding `blocTest`                |
| Incomplete test cases | Handler tested with fewer than 3 cases (happy / failure / exception) |
| Real network in tests | Unit tests that instantiate real `DioUtil` or call actual endpoints  |
| `var` / missing types | Test variables not explicitly typed                                  |

### 5 — Duplication

| Check                  | What to look for                                                     |
| ---------------------- | -------------------------------------------------------------------- |
| Repeated UI snippets   | Identical widget sub-trees not extracted to `widget/`                |
| Duplicate API logic    | Same endpoint called from multiple repos                             |
| Copy-paste state/event | State or event boilerplate copy-pasted and not DRY'd via shared base |

### 6 — Performance & Architecture Purity

| Check                  | What to look for                                                     |
| ---------------------- | -------------------------------------------------------------------- |
| Missing `const`        | Neglecting `const` constructors for UI elements, causing useless widget rebuilds |
| `build()` pollution    | Expensive loops, synchronous JSON parsing, or heavy logic inside the `build()` method |
| Main Isolate blocking  | Parsing massive JSON strings natively instead of safely using `Isolate.run()` |
| Massive ListViews      | Using `Column` or `ListView` instead of `ListView.builder` for unbounded lists |

---

## Output format

### Finding table

| #   | File                | Line | Pillar   | Severity    | Finding           | Fix                     |
| --- | ------------------- | ---- | -------- | ----------- | ----------------- | ----------------------- |
| 1   | `path/to/file.dart` | 42   | Security | 🔴 Critical | Hardcoded API key | Move to `--dart-define` |

Severity key:

| Icon | Level    | Definition                                            |
| ---- | -------- | ----------------------------------------------------- |
| 🔴   | Critical | Immediate security breach or data loss risk           |
| 🟠   | High     | Likely failure in production or data exposure         |
| 🟡   | Medium   | Degrades reliability, maintainability, or testability |
| 🟢   | Low      | Style / convention divergence; low impact             |

### Fix list (prioritised)

List fixes in descending severity order. For each fix provide:

- **File path** (relative to `lib/`)
- **What to change** (concise instruction or code snippet)
- **Why** (one sentence referencing the relevant rule from the copilot instructions)

''',
  'skills/write-tests.prompt.md': r'''
---
agent: agent
description: Write bloc_test unit tests for a Bloc class covering happy path, null/empty response, and exception thrown for each handler.
argument-hint: "Feature name, Bloc class path, and list of event handlers to cover"
---

# Write Bloc Tests

Feature: `${input:featureName}` | Bloc: `${input:featurePascalName}Bloc`

Handlers to cover:

```
${input:handlers}
```

## File location

```
test/features/${input:featureName}/bloc/${input:featureName}_bloc_test.dart
```

## Test template

```dart
import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';

import 'package:flutter_base_project/features/${input:featureName}/bloc/${input:featureName}_bloc.dart';
import 'package:flutter_base_project/features/${input:featureName}/repo/${input:featureName}_repo.dart';
import 'package:flutter_base_project/core/types/result.dart';

// ── Mocks & Fakes ──────────────────────────────────────────────────────────

class Mock${input:featurePascalName}Repo extends Mock implements ${input:featurePascalName}Repo {}
class FakeRequestModel extends Fake implements RequestModel {} // Replace with actual request model type

// ── Helpers ────────────────────────────────────────────────────────────────

${input:featurePascalName}Bloc buildBloc(${input:featurePascalName}Repo repo) =>
  ${input:featurePascalName}Bloc(repo: repo);

// ── Tests ──────────────────────────────────────────────────────────────────

void main() {
late Mock${input:featurePascalName}Repo mockRepo;

setUpAll(() {
  // CRITICAL: Prevent mocktail crash when passing custom models through any()
  registerFallbackValue(FakeRequestModel());
});

setUp(() {
  mockRepo = Mock${input:featurePascalName}Repo();
});

group('${input:featurePascalName}Bloc', () {
  group('SubmitXxxEvent', () {
    // ── arrange ──
    const inputEvent = SubmitXxxEvent(someField: 'value');

    blocTest<${input:featurePascalName}Bloc, ${input:featurePascalName}State>(
      'emits [loading, success] on happy path',
      setUp: () {
        when(() => mockRepo.someWriteMethod(
              requestModel: any(named: 'requestModel'),
            )).thenAnswer((_) async => const Success(data: null));
      },
      build: () => buildBloc(mockRepo),
      act: (bloc) => bloc.add(inputEvent),
      expect: () => [
        const ${input:featurePascalName}State(status: ${input:featurePascalName}Status.loading, message: ''),
        const ${input:featurePascalName}State(status: ${input:featurePascalName}Status.success, message: ''),
      ],
    );

    blocTest<${input:featurePascalName}Bloc, ${input:featurePascalName}State>(
      'emits [loading, error] when repo returns Failure',
      setUp: () {
        when(() => mockRepo.someWriteMethod(
              requestModel: any(named: 'requestModel'),
            )).thenAnswer(
          (_) async => const Failure(message: 'Something went wrong.'),
        );
      },
      build: () => buildBloc(mockRepo),
      act: (bloc) => bloc.add(inputEvent),
      expect: () => [
        const ${input:featurePascalName}State(status: ${input:featurePascalName}Status.loading, message: ''),
        const ${input:featurePascalName}State(
          status: ${input:featurePascalName}Status.error,
          message: 'Something went wrong.',
        ),
      ],
    );

    blocTest<${input:featurePascalName}Bloc, ${input:featurePascalName}State>(
      'emits [loading, error] when repo throws exception',
      setUp: () {
        when(() => mockRepo.someWriteMethod(
              requestModel: any(named: 'requestModel'),
            )).thenThrow(Exception('network error'));
      },
      build: () => buildBloc(mockRepo),
      act: (bloc) => bloc.add(inputEvent),
      expect: () => [
        const ${input:featurePascalName}State(status: ${input:featurePascalName}Status.loading, message: ''),
        isA<${input:featurePascalName}State>()
            .having((s) => s.status, 'status', ${input:featurePascalName}Status.error),
      ],
    );
  });

  // Repeat group per handler listed in ${input:handlers}
});
}
```

## Rules

- **Minimum 3 tests per handler:** happy path, failure response, exception thrown.
- Use `bloc_test` `blocTest<B, S>` — never test `.state` after `add` manually.
- Mock repos with `mocktail` via constructor injection: `${input:featurePascalName}Bloc(repo: mockRepo)`.
- **Never** use real network calls in tests.
- Follow **Arrange–Act–Assert**; prefix variables: `inputEvent`, `mockRepo`, `actualState`, `expectedState`.
- Variable names: `input*` for inputs, `mock*` for mocks, `actual*` for captured values, `expected*` for expected values.
- Each `blocTest` description must start with: `'emits [<states>] when <condition>'`.
- Test file: `test/features/${input:featureName}/bloc/${input:featureName}_bloc_test.dart`.
- Register fallback values with `registerFallbackValue` in `setUpAll` if needed by mocktail.
- **Git Hooks Compliance**: The generated test file MUST strictly pass `dart format .` and `flutter analyze` without any custom deprecation warnings.

''',
  'skills/create-bloc.prompt.md': r'''
---
agent: agent
description: Create a Bloc class, Event file, and State file for an existing or new feature following project BLoC conventions.
argument-hint: "Feature name in snake_case, PascalCase variant, and description of events/state needed"
---

# Create Bloc

Feature: `${input:featureName}` | PascalCase: `${input:featurePascalName}`

Events / state described:

```
${input:description}
```

## File layout

```
lib/features/<parentFolder>/${input:featureName}/bloc/
├── ${input:featureName}_bloc.dart   ← Bloc class (main file)
├── ${input:featureName}_event.dart  ← part of bloc
└── ${input:featureName}_state.dart  ← part of bloc
```

## Bloc class — `${input:featureName}_bloc.dart`

```dart
import '../../../../core/router/export.dart';

part '${input:featureName}_event.dart';
part '${input:featureName}_state.dart';

class ${input:featurePascalName}Bloc extends Bloc<${input:featurePascalName}Event, ${input:featurePascalName}State> {
${input:featurePascalName}Bloc({${input:featurePascalName}Repo? repo})
    : _repo = repo ?? ${input:featurePascalName}Repo(),
      super(const ${input:featurePascalName}State()) {
  on<SubmitXxxEvent>(_onSubmitXxx);
  // add one on<> per event
}

final ${input:featurePascalName}Repo _repo;

Future<void> _onSubmitXxx(
  SubmitXxxEvent event,
  Emitter<${input:featurePascalName}State> emit,
) async {
  emit(state.copyWith(status: ${input:featurePascalName}Status.loading));
  try {
    final Result<void> result = await _repo.someWriteMethod();
    switch (result) {
      case Success():
        emit(state.copyWith(status: ${input:featurePascalName}Status.success));
      case Failure(:final message):
        emit(state.copyWith(status: ${input:featurePascalName}Status.error, message: message));
    }
  } catch (e, stackTrace) {
    DebugUtils.showPrint('${input:featurePascalName}Bloc._onSubmitXxx error: $e\n$stackTrace');
    // Forward the exception and stackTrace to your crash reporter here
    emit(state.copyWith(
      status: ${input:featurePascalName}Status.error,
      message: ApiMsgStrings.somethingWentWrong,
    ));
  }
}
}
```

## Event file — `${input:featureName}_event.dart`

```dart
part of '${input:featureName}_bloc.dart';

@immutable
sealed class ${input:featurePascalName}Event extends Equatable {
const ${input:featurePascalName}Event();
}

@immutable
final class SubmitXxxEvent extends ${input:featurePascalName}Event {
const SubmitXxxEvent({required this.someField});

final String someField;

@override
List<Object?> get props => [someField]; // never include passwords/tokens
}
```

## State file — `${input:featureName}_state.dart`

```dart
part of '${input:featureName}_bloc.dart';

enum ${input:featurePascalName}Status { initial, loading, success, error }

@immutable
final class ${input:featurePascalName}State extends Equatable {
const ${input:featurePascalName}State({
  this.status = ${input:featurePascalName}Status.initial,
  this.message = '',
  // add domain fields here
});

final ${input:featurePascalName}Status status;
final String message;

${input:featurePascalName}State copyWith({
  ${input:featurePascalName}Status? status,
  String? message,
  bool clearMessage = false,
}) =>
    ${input:featurePascalName}State(
      status: status ?? this.status,
      message: clearMessage ? '' : (message ?? this.message),
    );

@override
List<Object?> get props => [status, message];
}
```

## Rules

- Emit `loading` first in every async handler. Ensure you reset/clear previous error messages when doing so using the `clearMessage` flag.
- Always emit a terminal state (`success` or `error`) in **every** code path including `catch`.
- **Catch Blocks & Observability**: Always catch `(e, stackTrace)` so that the full context is logged and can be caught by a crash reporter.
- **Event Transformers**: If the event handles rapid user input (like searching or submitting a form button), explicitly apply `droppable()`, `restartable()`, or `debounceTime()` transformers from `bloc_concurrency`.
- **Form Validation**: If the feature handles many form fields, validate inputs inside the state before the API call to avoid unnecessary networking.
- Log errors: `DebugUtils.showPrint('${input:featurePascalName}Bloc.<handler> error: $e')`.
- **Never** perform navigation or show dialogs inside the Bloc.
- `props` must **never** include passwords, tokens, or credentials.
- Use barrel import `import '../../../../core/router/export.dart'` — not individual imports.

''',
  'skills/create-model.prompt.md': r'''
---
agent: agent
description: Generate Dart model DTOs (request and/or response) from a JSON payload or field description, following project model conventions.
argument-hint: "Model name in PascalCase, request/response/both, raw JSON or field list"
---

# Create Model

Model: `${input:modelName}` | Type: `${input:modelType}` (request / response / both)

JSON or field list:

```json
${input:jsonOrFields}
```

## File location

```
lib/features/<parentFolder>/<featureName>/model/
├── ${input:modelFileName}_request_model.dart   (if request or both)
└── ${input:modelFileName}_response_model.dart  (if response or both)
```

## Request model template

```dart
import '../../../../core/router/export.dart';

@immutable
final class ${input:modelName}RequestModel extends Equatable {
const ${input:modelName}RequestModel({
  required this.field1,
  // add all required fields
});

final String field1;

Map<String, dynamic> toJson() => {
      'field1': field1,
    };

@override
List<Object?> get props => [field1];
}
```

## Response model template

```dart
import '../../../../core/router/export.dart';

@immutable
final class ${input:modelName}ResponseModel extends Equatable {
const ${input:modelName}ResponseModel({
  required this.id,
  this.createdAt,
  // add all fields
});

factory ${input:modelName}ResponseModel.fromJson(Map<String, dynamic> json) =>
    ${input:modelName}ResponseModel(
      // parsed defensively:
      id: json['id']?.toString() ?? '',
      createdAt: DateTime.tryParse(json['created_at']?.toString() ?? ''),
      // map every JSON key
    );

final String id;
final DateTime? createdAt;

Map<String, dynamic> toJson() => {
      'id': id,
      'created_at': createdAt?.toIso8601String(),
    };

${input:modelName}ResponseModel copyWith({
  String? id,
  DateTime? createdAt,
}) =>
    ${input:modelName}ResponseModel(
      id: id ?? this.id,
      createdAt: createdAt ?? this.createdAt,
    );

@override
List<Object?> get props => [id, createdAt];
}
```

## Rules

- `@immutable` on every model class.
- All fields are `final`.
- Every response model has a `factory fromJson(Map<String, dynamic> json)`.
- Every model has a `toJson()` → `Map<String, dynamic>`.
- Null-safe defaults: `?? ''`, `?? 0`, `?? false`, `?? []`, `?? {}`.
- **Never** declare a field as `dynamic` — cast and parse JSON values defensively to prevent `TypeError` crashes:
- String: `json['key']?.toString() ?? ''`
- int: `int.tryParse(json['key']?.toString() ?? '') ?? 0`
- double: `double.tryParse(json['key']?.toString() ?? '') ?? 0.0`
- bool: `json['key'] is bool ? json['key'] as bool : (json['key']?.toString().toLowerCase() == 'true')`
- DateTime: `DateTime.tryParse(json['key']?.toString() ?? '')`
- List: `(json['key'] as List<dynamic>? ?? []).map((e) => ItemModel.fromJson(e as Map<String, dynamic>)).toList()`
- **Equatable**: All models must extend `Equatable` and implement `get props => [...]` so they trigger reliable UI rebuilds when embedded in a BLoC State.
- Request models live in `<feature>/model/` named `<Noun>RequestModel`.
- Response models live in `<feature>/model/` named `<Noun>ResponseModel`.
- Provide `copyWith` on response models that are stored in Bloc state.
- Use barrel import `import '../../../../core/router/export.dart'` only if needed for shared types.

''',
};