reis-mobile 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/.claude-plugin/marketplace.json +24 -0
  2. package/.claude-plugin/plugin.json +12 -0
  3. package/CHANGELOG.md +103 -0
  4. package/LICENSE +21 -0
  5. package/README.md +631 -0
  6. package/THIRD_PARTY_NOTICES.md +105 -0
  7. package/agents/flutter-architect.md +92 -0
  8. package/agents/flutter-performance-engineer.md +103 -0
  9. package/agents/flutter-test-engineer.md +100 -0
  10. package/agents/lead-mobile.md +107 -0
  11. package/agents/mobile-code-reviewer.md +66 -0
  12. package/agents/mobile-staff-engineer.md +85 -0
  13. package/agents/plugin-native-expert.md +97 -0
  14. package/bin/reis-mobile.mjs +256 -0
  15. package/commands/debate.md +135 -0
  16. package/commands/doctor.md +23 -0
  17. package/commands/reis-mobile.md +50 -0
  18. package/commands/review.md +53 -0
  19. package/core/config/language.mjs +83 -0
  20. package/core/context/context-engine.mjs +113 -0
  21. package/core/detection/project-probe.mjs +104 -0
  22. package/core/detection/stack-detector.mjs +204 -0
  23. package/core/diagnostics/doctor.mjs +181 -0
  24. package/core/install/claude-plugin.mjs +76 -0
  25. package/core/paths.mjs +4 -0
  26. package/core/registry/frontmatter.mjs +81 -0
  27. package/core/registry/registry.mjs +140 -0
  28. package/core/router/intent-detector.mjs +75 -0
  29. package/core/router/intents.mjs +91 -0
  30. package/core/router/router.mjs +83 -0
  31. package/core/security/redact.mjs +24 -0
  32. package/package.json +35 -0
  33. package/skills/architecture-feature-first/SKILL.md +116 -0
  34. package/skills/bloc/SKILL.md +354 -0
  35. package/skills/code-review/SKILL.md +89 -0
  36. package/skills/dart-3-updates/SKILL.md +223 -0
  37. package/skills/dart-add-unit-test/SKILL.md +126 -0
  38. package/skills/dart-collect-coverage/SKILL.md +145 -0
  39. package/skills/dart-fix-runtime-errors/SKILL.md +170 -0
  40. package/skills/dart-generate-test-mocks/SKILL.md +159 -0
  41. package/skills/dart-migrate-to-checks-package/SKILL.md +532 -0
  42. package/skills/dart-resolve-package-conflicts/SKILL.md +120 -0
  43. package/skills/dart-run-static-analysis/SKILL.md +108 -0
  44. package/skills/dart-use-pattern-matching/SKILL.md +240 -0
  45. package/skills/dart-use-pattern-matching/examples/json_patterns.dart +85 -0
  46. package/skills/detect-mobile-stack/SKILL.md +33 -0
  47. package/skills/effective-dart/SKILL.md +187 -0
  48. package/skills/firebase-ai/SKILL.md +93 -0
  49. package/skills/firebase-analytics/SKILL.md +129 -0
  50. package/skills/firebase-app-check/SKILL.md +124 -0
  51. package/skills/firebase-auth/SKILL.md +224 -0
  52. package/skills/firebase-cloud-firestore/SKILL.md +165 -0
  53. package/skills/firebase-cloud-functions/SKILL.md +148 -0
  54. package/skills/firebase-crashlytics/SKILL.md +182 -0
  55. package/skills/firebase-data-connect/SKILL.md +80 -0
  56. package/skills/firebase-database/SKILL.md +224 -0
  57. package/skills/firebase-in-app-messaging/SKILL.md +111 -0
  58. package/skills/firebase-messaging/SKILL.md +201 -0
  59. package/skills/firebase-remote-config/SKILL.md +131 -0
  60. package/skills/firebase-storage/SKILL.md +158 -0
  61. package/skills/flutter-add-integration-test/SKILL.md +167 -0
  62. package/skills/flutter-add-widget-preview/SKILL.md +149 -0
  63. package/skills/flutter-add-widget-test/SKILL.md +158 -0
  64. package/skills/flutter-adding-home-screen-widgets/SKILL.md +205 -0
  65. package/skills/flutter-animating-apps/SKILL.md +185 -0
  66. package/skills/flutter-app-architecture/SKILL.md +80 -0
  67. package/skills/flutter-apply-architecture-best-practices/SKILL.md +166 -0
  68. package/skills/flutter-build-responsive-layout/SKILL.md +143 -0
  69. package/skills/flutter-building-forms/SKILL.md +125 -0
  70. package/skills/flutter-building-layouts/SKILL.md +131 -0
  71. package/skills/flutter-building-plugins/SKILL.md +209 -0
  72. package/skills/flutter-caching-data/SKILL.md +171 -0
  73. package/skills/flutter-change-notifier/SKILL.md +138 -0
  74. package/skills/flutter-embedding-native-views/SKILL.md +200 -0
  75. package/skills/flutter-errors/SKILL.md +126 -0
  76. package/skills/flutter-fix-layout-issues/SKILL.md +134 -0
  77. package/skills/flutter-handling-concurrency/SKILL.md +182 -0
  78. package/skills/flutter-implement-json-serialization/SKILL.md +157 -0
  79. package/skills/flutter-improving-accessibility/SKILL.md +113 -0
  80. package/skills/flutter-interoperating-with-native-apis/SKILL.md +130 -0
  81. package/skills/flutter-login-usecase/SKILL.md +38 -0
  82. package/skills/flutter-managing-state/SKILL.md +206 -0
  83. package/skills/flutter-project-audit/SKILL.md +66 -0
  84. package/skills/flutter-reducing-app-size/SKILL.md +103 -0
  85. package/skills/flutter-secure-token-store/SKILL.md +35 -0
  86. package/skills/flutter-setting-up-on-macos/SKILL.md +78 -0
  87. package/skills/flutter-setup-declarative-routing/SKILL.md +259 -0
  88. package/skills/flutter-setup-localization/SKILL.md +214 -0
  89. package/skills/flutter-theming-apps/SKILL.md +163 -0
  90. package/skills/flutter-use-http-package/SKILL.md +178 -0
  91. package/skills/flutter-widget-review/SKILL.md +54 -0
  92. package/skills/flutter-working-with-databases/SKILL.md +203 -0
  93. package/skills/flutterfire-configure/SKILL.md +143 -0
  94. package/skills/mobile-security-audit/SKILL.md +101 -0
  95. package/skills/mockito/SKILL.md +149 -0
  96. package/skills/mocktail/SKILL.md +122 -0
  97. package/skills/patrol-e2e-testing/SKILL.md +224 -0
  98. package/skills/provider/SKILL.md +139 -0
  99. package/skills/riverpod/SKILL.md +246 -0
  100. package/skills/testing/SKILL.md +50 -0
  101. package/stacks/android/stack.json +8 -0
  102. package/stacks/flutter/stack.json +8 -0
  103. package/stacks/ios/stack.json +8 -0
  104. package/stacks/kotlin-multiplatform/stack.json +8 -0
  105. package/stacks/react-native/stack.json +8 -0
@@ -0,0 +1,120 @@
1
+ ---
2
+ name: dart-resolve-package-conflicts
3
+ description: Workflow for fixing package version conflicts. Use this when `pub get` fails due to incompatible package versions.
4
+ metadata:
5
+ model: models/gemini-3.1-pro-preview
6
+ last_modified: Fri, 24 Apr 2026 15:11:14 GMT
7
+ intents: [debug, dependency]
8
+ stacks: [flutter]
9
+ source: https://github.com/dart-lang/skills
10
+ license: BSD-3-Clause
11
+ ---
12
+ # Managing Dart Dependencies
13
+
14
+ ## Contents
15
+ - [Core Concepts](#core-concepts)
16
+ - [Version Constraints](#version-constraints)
17
+ - [Workflow: Auditing Dependencies](#workflow-auditing-dependencies)
18
+ - [Workflow: Upgrading Dependencies](#workflow-upgrading-dependencies)
19
+ - [Workflow: Resolving Version Conflicts](#workflow-resolving-version-conflicts)
20
+ - [Examples](#examples)
21
+
22
+ ## Core Concepts
23
+
24
+ Dart enforces a strict single-version rule for dependencies: a project and all its transitive dependencies must resolve to a single, shared version of any given package. This prevents runtime type mismatches but introduces the risk of "version lock."
25
+
26
+ To mitigate version lock, Dart relies on version constraints rather than pinned versions in the `pubspec.yaml`. The `pubspec.lock` file maintains the exact resolved versions for reproducible builds.
27
+
28
+ Understand the output columns of `dart pub outdated`:
29
+ * **Current:** The version currently recorded in `pubspec.lock`.
30
+ * **Upgradable:** The latest version allowed by the constraints in `pubspec.yaml`. `dart pub upgrade` resolves to this.
31
+ * **Resolvable:** The absolute latest version that can be resolved when factoring in all other dependencies in the project.
32
+ * **Latest:** The latest published version of the package (excluding prereleases).
33
+
34
+ ## Version Constraints
35
+
36
+ * **Use Caret Syntax:** Always use caret syntax (e.g., `^1.2.3`) for dependencies in `pubspec.yaml`. This allows `pub` to select newer, non-breaking versions (up to, but not including, the next major version) during resolution.
37
+ * **Tighten Dev Dependencies:** Set the lower bound of `dev_dependencies` to the exact version currently used. This reduces resolution complexity and prevents older, incompatible dev tools from being selected.
38
+ * **Enforce Lockfiles in CI:** Use `dart pub get --enforce-lockfile` in CI/CD pipelines to ensure the exact versions tested locally are used in production.
39
+
40
+ ## Workflow: Auditing Dependencies
41
+
42
+ Run this workflow periodically to identify stale packages that may impact stability or performance.
43
+
44
+ **Task Progress:**
45
+ - [ ] Run `dart pub outdated`.
46
+ - [ ] Review the **Upgradable** column to identify packages that can be updated without modifying `pubspec.yaml`.
47
+ - [ ] Review the **Resolvable** column to identify packages that require constraint modifications in `pubspec.yaml` to update.
48
+ - [ ] Identify any packages marked as retracted or discontinued.
49
+
50
+ ## Workflow: Upgrading Dependencies
51
+
52
+ Use conditional logic based on the audit results to upgrade dependencies.
53
+
54
+ **Task Progress:**
55
+ - [ ] **If updating to "Upgradable" versions:**
56
+ - [ ] Run `dart pub upgrade`.
57
+ - [ ] Run `dart pub upgrade --tighten` to automatically update the lower bounds in `pubspec.yaml` to match the newly resolved versions.
58
+ - [ ] **If updating to "Resolvable" versions (Major updates):**
59
+ - [ ] Manually edit `pubspec.yaml` to bump the version constraint to match the "Resolvable" column (e.g., change `^0.11.0` to `^0.12.1`).
60
+ - [ ] Run `dart pub upgrade` to resolve the new constraints and update `pubspec.lock`.
61
+ - [ ] **Feedback Loop:**
62
+ - [ ] Run `dart analyze` -> review errors -> fix breaking API changes.
63
+ - [ ] Run `dart test` -> review failures -> fix regressions.
64
+
65
+ ## Workflow: Resolving Version Conflicts
66
+
67
+ When `pub` cannot find a set of concrete versions that satisfy all constraints, or when dealing with a retracted package version, manipulate the lockfile surgically.
68
+
69
+ **NEVER** delete the entire `pubspec.lock` file and run `dart pub get`. This causes uncontrolled upgrades across the entire dependency graph.
70
+
71
+ **Task Progress:**
72
+ - [ ] Open `pubspec.lock`.
73
+ - [ ] Locate the specific YAML block for the conflicting or retracted package.
74
+ - [ ] Delete ONLY that package's entry from the lockfile.
75
+ - [ ] Run `dart pub get` to fetch the newest compatible, non-retracted version for that specific package.
76
+ - [ ] **Feedback Loop:**
77
+ - [ ] Run `dart pub deps` -> verify the dependency graph resolves correctly.
78
+ - [ ] If resolution fails, identify the transitive dependency causing the lock, update its constraint in `pubspec.yaml`, and retry.
79
+
80
+ ## Examples
81
+
82
+ ### Tightening Constraints
83
+ When `dart pub outdated` shows a package is resolvable to a higher minor/patch version, use the `--tighten` flag to update the `pubspec.yaml` automatically.
84
+
85
+ **Input (`pubspec.yaml`):**
86
+ ```yaml
87
+ dependencies:
88
+ http: ^0.13.0
89
+ ```
90
+
91
+ **Command:**
92
+ ```bash
93
+ dart pub upgrade --tighten http
94
+ ```
95
+
96
+ **Output (`pubspec.yaml`):**
97
+ ```yaml
98
+ dependencies:
99
+ http: ^0.13.5
100
+ ```
101
+
102
+ ### Surgical Lockfile Removal
103
+ If `package_a` is retracted or locked in a conflict, remove only its block from `pubspec.lock`.
104
+
105
+ **Before (`pubspec.lock`):**
106
+ ```yaml
107
+ packages:
108
+ package_a:
109
+ dependency: "direct main"
110
+ description:
111
+ name: package_a
112
+ url: "https://pub.dev"
113
+ source: hosted
114
+ version: "1.0.0" # Retracted version
115
+ package_b:
116
+ dependency: "direct main"
117
+ # ...
118
+ ```
119
+
120
+ **Action:** Delete the `package_a` block entirely. Leave `package_b` untouched. Run `dart pub get`.
@@ -0,0 +1,108 @@
1
+ ---
2
+ name: dart-run-static-analysis
3
+ description: Execute `dart analyze` to identify warnings and errors, and use `dart fix --apply` to automatically resolve mechanical lint issues. Use during development to ensure code quality and before committing changes.
4
+ metadata:
5
+ model: models/gemini-3.1-pro-preview
6
+ last_modified: Fri, 24 Apr 2026 15:09:34 GMT
7
+ intents: [review]
8
+ stacks: [flutter]
9
+ source: https://github.com/dart-lang/skills
10
+ license: BSD-3-Clause
11
+ ---
12
+ # Analyzing and Fixing Dart Code
13
+
14
+ ## Contents
15
+ - [Analysis Configuration](#analysis-configuration)
16
+ - [Diagnostic Suppression](#diagnostic-suppression)
17
+ - [Workflow: Executing Static Analysis](#workflow-executing-static-analysis)
18
+ - [Workflow: Applying Automated Fixes](#workflow-applying-automated-fixes)
19
+ - [Examples](#examples)
20
+
21
+ ## Analysis Configuration
22
+
23
+ Configure the Dart analyzer using the `analysis_options.yaml` file located at the package root.
24
+
25
+ - **Base Configuration:** Always include a standard rule set (e.g., `package:lints/recommended.yaml` or `package:flutter_lints/flutter.yaml`) using the `include:` directive.
26
+ - **Strict Type Checks:** Enable strict type checks under the `analyzer: language:` node to prevent implicit downcasts and dynamic inferences. Set `strict-casts: true`, `strict-inference: true`, and `strict-raw-types: true`.
27
+ - **Linter Rules:** Explicitly enable or disable specific rules under the `linter: rules:` node. Use a key-value map (`rule_name: true/false`) when overriding included rules, or a list (`- rule_name`) when defining a fresh set. Do not mix list and map syntax in the same `rules` block.
28
+ - **Formatter Configuration:** Configure `dart format` behavior under the `formatter:` node. Set `page_width` (default 80) and `trailing_commas` (`automate` or `preserve`).
29
+ - **Analyzer Plugins:** Enable custom diagnostics by adding plugins under the `analyzer: plugins:` node. Ensure the plugin package is added as a `dev_dependency` in `pubspec.yaml`.
30
+
31
+ ## Diagnostic Suppression
32
+
33
+ When a diagnostic (lint or warning) yields a false positive or applies to generated code, suppress it explicitly.
34
+
35
+ - **File-level Exclusion:** Use the `analyzer: exclude:` node in `analysis_options.yaml` to exclude entire files or directories (e.g., `**/*.g.dart`) using glob patterns.
36
+ - **File-level Suppression:** Add `// ignore_for_file: <diagnostic_code>` at the top of a Dart file to suppress specific diagnostics for the entire file. Use `// ignore_for_file: type=lint` to suppress all linter rules.
37
+ - **Line-level Suppression:** Add `// ignore: <diagnostic_code>` on the line directly above the offending code, or appended to the end of the offending line.
38
+ - **Pubspec Suppression:** Add `# ignore: <diagnostic_code>` above the offending line in `pubspec.yaml` files (e.g., `# ignore: sort_pub_dependencies`).
39
+ - **Plugin Diagnostics:** Prefix the diagnostic code with the plugin name when suppressing plugin-specific issues (e.g., `// ignore: some_plugin/some_code`).
40
+
41
+ ## Workflow: Executing Static Analysis
42
+
43
+ Use this workflow to identify type-related bugs, style violations, and potential runtime errors.
44
+
45
+ **Task Progress:**
46
+ - [ ] 1. Verify `analysis_options.yaml` exists at the project root.
47
+ - [ ] 2. Run the analyzer using the `analyze_files` MCP tool (if available) or the CLI command `dart analyze <target_directory>`.
48
+ - [ ] 3. Review the diagnostic output.
49
+ - [ ] 4. If info-level issues must be treated as failures, append the `--fatal-infos` flag.
50
+ - [ ] 5. Resolve reported errors manually or proceed to the Automated Fixes workflow.
51
+
52
+ ## Workflow: Applying Automated Fixes
53
+
54
+ Use this workflow to resolve outdated API usages, apply quick fixes, and migrate code (e.g., Dart 3 migrations).
55
+
56
+ **Task Progress:**
57
+ - [ ] 1. Execute a dry run to preview proposed changes using the `dart_fix` MCP tool or CLI command `dart fix --dry-run`.
58
+ - [ ] 2. Review the proposed fixes to ensure they align with the intended architecture.
59
+ - [ ] 3. If additional fixes are required, verify that the corresponding linter rules are enabled in `analysis_options.yaml`.
60
+ - [ ] 4. Apply the fixes using the `dart_fix` MCP tool or CLI command `dart fix --apply`.
61
+ - [ ] 5. Format the modified code using the `dart_format` MCP tool or CLI command `dart format .`.
62
+ - [ ] 6. Run the static analysis workflow to verify all diagnostics are resolved.
63
+
64
+ ## Examples
65
+
66
+ ### Comprehensive `analysis_options.yaml`
67
+
68
+ ```yaml
69
+ include: package:flutter_lints/recommended.yaml
70
+
71
+ analyzer:
72
+ exclude:
73
+ - "**/*.g.dart"
74
+ - "lib/generated/**"
75
+ language:
76
+ strict-casts: true
77
+ strict-inference: true
78
+ strict-raw-types: true
79
+ errors:
80
+ todo: ignore
81
+ invalid_assignment: warning
82
+ missing_return: error
83
+
84
+ linter:
85
+ rules:
86
+ avoid_shadowing_type_parameters: false
87
+ await_only_futures: true
88
+ use_super_parameters: true
89
+
90
+ formatter:
91
+ page_width: 100
92
+ trailing_commas: preserve
93
+ ```
94
+
95
+ ### Inline Diagnostic Suppression
96
+
97
+ ```dart
98
+ // Suppress for the entire file
99
+ // ignore_for_file: unused_local_variable, dead_code
100
+
101
+ void processData() {
102
+ // Suppress for a specific line
103
+ // ignore: invalid_assignment
104
+ int x = '';
105
+
106
+ const y = 10; // ignore: constant_identifier_names
107
+ }
108
+ ```
@@ -0,0 +1,240 @@
1
+ ---
2
+ name: dart-use-pattern-matching
3
+ description: >-
4
+ Applies Dart 3 pattern matching, switch expressions, and destructuring
5
+ idiomatically to validate data schemas, handle algebraic data types, and
6
+ decompose control flow. Use when refactoring complex if-else chains,
7
+ parsing polymorphic JSON or API responses, destructuring Records or Maps, or
8
+ enforcing exhaustiveness on sealed classes. Don't use for simple boolean
9
+ conditions, single-variable type promotion (use `is`), or basic collection
10
+ filtering.
11
+ metadata:
12
+ model: models/gemini-3.1-pro-preview
13
+ last_modified: Sun, 06 Sep 2026 06:43:00 GMT
14
+ routing: manual
15
+ stacks: [flutter]
16
+ source: https://github.com/dart-lang/skills
17
+ license: BSD-3-Clause
18
+ ---
19
+ # Implementing Dart Patterns
20
+
21
+ ## Contents
22
+ - [Pattern Selection Strategy](#pattern-selection-strategy)
23
+ - [Switch Statements vs. Expressions](#switch-statements-vs-expressions)
24
+ - [Core Pattern Implementations](#core-pattern-implementations)
25
+ - [Pragmatic Balance & Anti-Patterns](#pragmatic-balance--anti-patterns)
26
+ - [Workflows](#workflows)
27
+ - [Examples](#examples)
28
+
29
+ ## Pattern Selection Strategy
30
+
31
+ Apply specific pattern types based on the data structure and desired outcome. Follow these conditional guidelines:
32
+
33
+ * **If validating and extracting from deserialized data (e.g., JSON):** Use Map, List, and Object patterns to validate schema structure and destructure properties in a single step.
34
+ * **If handling polymorphic payloads or responses:** Use `switch` expressions over map discriminant keys to deserialize into `sealed` class hierarchies.
35
+ * **If handling multiple return values:** Use Record patterns to destructure fields directly into local variables.
36
+ * **If executing type-specific behavior (Algebraic Data Types):** Use Object patterns combined with `sealed` classes to ensure exhaustiveness.
37
+ * **If matching numeric ranges or conditions:** Use Relational (`>=`, `<=`) and Logical-and (`&&`) patterns within switch arms.
38
+ * **If multiple cases share logic:** Use Logical-or (`||`) patterns to share a single case body or guard clause.
39
+ * **If ignoring specific values:** Use the Wildcard pattern (`_`) or a non-matching Rest element (`...`) in collections.
40
+
41
+ ## Switch Statements vs. Expressions
42
+
43
+ Select the appropriate switch construct based on the execution context:
44
+
45
+ * **If producing a value:** Use a **switch expression**.
46
+ * Syntax: `switch (value) { pattern => expression, }`
47
+ * Rule: Each case must be a single expression. No implicit fallthrough. Must be exhaustive.
48
+ * **If executing statements or side effects:** Use a **switch statement**.
49
+ * Syntax: `switch (value) { case pattern: statements; }`
50
+ * Rule: Empty cases fall through to the next case. Non-empty cases implicitly break (no `break` keyword required).
51
+
52
+ ## Core Pattern Implementations
53
+
54
+ Implement patterns using the following syntax and rules:
55
+
56
+ * **Logical-or (`||`):** `pattern1 || pattern2`. Both branches must define the exact same set of variables.
57
+ * **Logical-and (`&&`):** `pattern1 && pattern2`. Branches must *not* define overlapping variables.
58
+ * **Relational:** `==`, `!=`, `<`, `>`, `<=`, `>=` followed by a constant expression.
59
+ * **Cast (`as`):** `pattern as Type`. Throws if the value does not match the type. Use to forcibly assert types during destructuring.
60
+ * **Null-check (`?`):** `pattern?`. Fails the match if the value is null. Binds the variable to the non-nullable base type.
61
+ * **Null-assert (`!`):** `pattern!`. Throws if the value is null.
62
+ * **Variable:** `var name` or `Type name`. Binds the matched value to a new local variable.
63
+ * **Wildcard (`_`):** Matches any value and discards it.
64
+ * **List:** `[pattern1, pattern2]`. Matches lists of exact length unless a Rest element (`...` or `...var rest`) is used.
65
+ * **Map:** `{"key": pattern}`. Matches maps containing the specified keys. Ignores unmatched keys.
66
+ * **Record:** `(pattern1, named: pattern2)`. Matches records of the exact shape. Use `:var name` to infer the getter name.
67
+ * **Object:** `ClassName(field: pattern)`. Matches instances of `ClassName`. Use `:var field` to infer the getter name.
68
+
69
+ ## Pragmatic Balance & Anti-Patterns
70
+
71
+ Pattern matching and switch expressions should simplify code, not add syntactic overhead. Observe the following boundaries:
72
+
73
+ ### 1. Prefer `is` Type Promotion over `if-case` for Single Promotable Variables
74
+ When checking or promoting a single variable, use standard `is` checks instead of `if-case` patterns that introduce shadow aliases.
75
+
76
+ * **Prefer:**
77
+ ```dart
78
+ // ✅ Promotes `key` directly in-place without extra variables
79
+ for (final MapEntry(:key, :value) in map.entries) {
80
+ if (key is String && value != null) {
81
+ process(key, value);
82
+ }
83
+ }
84
+ ```
85
+ * **Avoid:**
86
+ ```dart
87
+ // ❌ Anti-pattern: Introduces unnecessary alias variable `k`
88
+ for (final MapEntry(:key, :value) in map.entries) {
89
+ if (key case final String k when value != null) {
90
+ process(k, value);
91
+ }
92
+ }
93
+ ```
94
+
95
+ ### 2. Consolidate Nullable Types in Switch Arms
96
+ When mapping or returning values where both `null` and a type `T` are valid and handled identically, match the nullable type `T?` directly rather than creating redundant `null` arms.
97
+
98
+ * **Prefer:**
99
+ ```dart
100
+ // ✅ Clean nullable pattern match
101
+ switch (value) {
102
+ final String? s => s,
103
+ _ => throw FormatException('Invalid value: $value'),
104
+ }
105
+ ```
106
+ * **Avoid:**
107
+ ```dart
108
+ // ❌ Redundant separate null arm
109
+ switch (value) {
110
+ final String s => s,
111
+ null => null,
112
+ _ => throw FormatException('Invalid value: $value'),
113
+ }
114
+ ```
115
+
116
+ ### 3. Preserve Fast-Fail Validation (Do Not Silently Drop Data)
117
+ Do not use `if-case` in loops or deserialization to filter elements if malformed data should trigger an error or diagnostic warning.
118
+
119
+ * **Prefer:**
120
+ ```dart
121
+ // ✅ Fast-fail with explicit diagnostic error
122
+ for (final raw in rawTasks) {
123
+ if (raw is! Map<String, dynamic>) {
124
+ throw FormatException('Expected Map item, got ${raw.runtimeType}: $raw');
125
+ }
126
+ _applyTask(raw);
127
+ }
128
+ ```
129
+ * **Avoid:**
130
+ ```dart
131
+ // ❌ Silently ignores malformed items
132
+ for (final raw in rawTasks) {
133
+ if (raw case final Map<String, dynamic> taskMap) {
134
+ _applyTask(taskMap);
135
+ }
136
+ }
137
+ ```
138
+
139
+ ### 4. Avoid Single-Case or Boolean Switches
140
+ * Use `if (x is T)` instead of a `switch` statement with only 1 case and `default: break;`.
141
+ * Use standard conditional ternary operators (`condition ? a : b`) instead of `switch (condition) { true => a, false => b }`.
142
+
143
+ ### 5. Avoid Gratuitous Object Destructuring
144
+ Use standard property access (`user.name`) rather than object pattern destructuring (`final User(:name) = user;`) when reading a single property on a known non-null instance.
145
+
146
+ ### 6. Avoid `if-case` for Standalone Scalar Comparisons
147
+ Use standard boolean operators (`if (code >= 200 && code < 300)`) instead of `if (code case >= 200 && < 300)` for standalone conditions. Reserve relational patterns for multi-arm `switch` tables.
148
+
149
+ ## Workflows
150
+
151
+ ### Task Progress: Implementing Pattern Matching
152
+ Copy this checklist to track progress when implementing complex pattern matching logic:
153
+
154
+ - [ ] Identify the data structure being evaluated (JSON, Record, Class, Enum).
155
+ - [ ] Select the appropriate switch construct (Expression for values, Statement for side-effects).
156
+ - [ ] Define the required patterns (Object, Map, List, Record).
157
+ - [ ] Extract required data using Variable patterns (`var x`, `:var y`).
158
+ - [ ] Apply Guard clauses (`when condition`) for logic that cannot be expressed via patterns.
159
+ - [ ] Handle unmatched cases using a Wildcard (`_`) or `default` clause (if not using a sealed class).
160
+ - [ ] Run static analyzer for exhaustiveness and dead code (`dart analyze`).
161
+ - [ ] Verify runtime Map/JSON pattern behavior on omitted keys (`containsKey` semantics) vs explicit `null` values.
162
+
163
+ ### Feedback Loop 1: Exhaustiveness Checking (Static Verification)
164
+ When switching over `sealed` classes or enums, ensure all subtypes are handled at compile time:
165
+
166
+ 1. **Run analyzer:** Execute `dart analyze`.
167
+ 2. **Review errors:** Look for "The type 'X' is not exhaustively matched by the switch cases" or unreachable pattern arm warnings.
168
+ 3. **Fix:** Add the missing Object patterns for unhandled subtypes, or add an explicit wildcard (`_`) arm if a default fallback or error is acceptable.
169
+
170
+ ### Feedback Loop 2: Runtime Map & JSON Pattern Verification
171
+ Because `dart analyze` cannot statically verify dynamic `Map<String, dynamic>` keys, validate runtime pattern semantics explicitly:
172
+
173
+ 1. **Omitted Keys vs. Explicit `null`**: A map pattern `{'key': String? val}` checks `map.containsKey('key')`. If `'key'` is omitted from the JSON payload, the pattern fails to match at runtime even though `String?` is nullable. Extract optional keys from the validated map directly (`map['key'] as String?`).
174
+ 2. **Fast-Fail Fallback**: Ensure unmatched or malformed map structures hit an explicit `_ => throw FormatException(...)` arm rather than silently failing an `if-case` check.
175
+
176
+ ## Examples
177
+
178
+ ### Polymorphic JSON Deserialization (Discriminated Unions)
179
+ Use Map patterns with switch expressions to validate tagged JSON payloads and
180
+ construct `sealed` class hierarchies. See
181
+ [examples/json_patterns.dart](examples/json_patterns.dart) for an executable
182
+ implementation demonstrating tagged `ApiResponse` parsing into `SuccessResponse`
183
+ and `ErrorResponse`.
184
+
185
+ ### Nested JSON Validation and Optional Fields
186
+ Use nested Map and List patterns to validate required schema structure and
187
+ extract collections in a single step. See
188
+ [examples/json_patterns.dart](examples/json_patterns.dart) for an executable
189
+ implementation of `processUserPayload`.
190
+
191
+ Map patterns check for key existence (`containsKey`). If an optional JSON key
192
+ might be omitted entirely from the payload (rather than explicitly passed as
193
+ `'key': null`), destructure required keys via the pattern and extract optional
194
+ fields directly from the matched submap.
195
+
196
+ ### Algebraic Data Types (Sealed Classes)
197
+ Use Object patterns with switch expressions to handle family types exhaustively.
198
+
199
+ ```dart
200
+ sealed class Shape {}
201
+
202
+ class Square implements Shape {
203
+ final double length;
204
+ Square(this.length);
205
+ }
206
+
207
+ class Circle implements Shape {
208
+ final double radius;
209
+ Circle(this.radius);
210
+ }
211
+
212
+ // Switch expression guarantees exhaustiveness due to `sealed` modifier.
213
+ double calculateArea(Shape shape) => switch (shape) {
214
+ Square(length: var l) => l * l,
215
+ Circle(:var radius) => math.pi * radius * radius,
216
+ };
217
+ ```
218
+
219
+ ### Variable Swapping and Destructuring
220
+ Use variable assignment patterns to swap values or extract record fields without temporary variables.
221
+
222
+ ```dart
223
+ var (a, b) = ('left', 'right');
224
+ (b, a) = (a, b); // Swap values
225
+
226
+ // Destructuring a function return
227
+ var (name, age) = getUserInfo();
228
+ ```
229
+
230
+ ### Guard Clauses and Logical-or
231
+ Use `when` to evaluate arbitrary conditions after a pattern matches.
232
+
233
+ ```dart
234
+ switch (shape) {
235
+ case Square(length: var s) || Circle(radius: var s) when s > 0:
236
+ print('Valid positive shape with dimension $s');
237
+ case Square() || Circle():
238
+ print('Zero or negative dimension shape');
239
+ }
240
+ ```
@@ -0,0 +1,85 @@
1
+ // Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2
+ // for details. All rights reserved. Use of this source code is governed by a
3
+ // BSD-style license that can be found in the LICENSE file.
4
+
5
+ /// Examples of Dart pattern matching for JSON handling:
6
+ /// - Polymorphic JSON deserialization (discriminated unions)
7
+ /// - Nested JSON validation with optional and omitted keys
8
+ library;
9
+
10
+ /// Represents an API response in a sealed hierarchy.
11
+ sealed class ApiResponse {}
12
+
13
+ /// Successful API response carrying data.
14
+ class SuccessResponse implements ApiResponse {
15
+ final Map<String, dynamic> data;
16
+ SuccessResponse(this.data);
17
+ }
18
+
19
+ /// Error API response carrying an error message and code.
20
+ class ErrorResponse implements ApiResponse {
21
+ final String message;
22
+ final int code;
23
+ ErrorResponse(this.message, this.code);
24
+ }
25
+
26
+ /// Parses a raw JSON map into an [ApiResponse] subtype.
27
+ ApiResponse parseApiResponse(Map<String, dynamic> json) => switch (json) {
28
+ {'status': 'ok', 'data': Map<String, dynamic> data} => SuccessResponse(data),
29
+ {'status': 'error', 'message': String msg, 'code': int code} => ErrorResponse(
30
+ msg,
31
+ code,
32
+ ),
33
+ _ => throw FormatException('Invalid or unrecognized API response: $json'),
34
+ };
35
+
36
+ /// Validates a nested user payload with both required schema and optional keys.
37
+ void processUserPayload(Map<String, dynamic> json) {
38
+ if (json case {
39
+ 'id': String id,
40
+ 'profile':
41
+ {'name': String name, 'email': String email} &&
42
+ final Map<String, dynamic> profile,
43
+ 'tags': [
44
+ String primaryTag,
45
+ ...,
46
+ ], // Matches at least 1 element, ignores rest
47
+ }) {
48
+ // Read optional/omitted fields directly from the validated submap.
49
+ // Map patterns check for key existence (containsKey), so an omitted key
50
+ // fails matching even if typed as String?.
51
+ final avatarUrl = profile['avatarUrl'] as String?;
52
+ print(
53
+ 'User $name ($id, $email, avatar: $avatarUrl) - Primary tag: $primaryTag',
54
+ );
55
+ } else {
56
+ throw FormatException('Malformed user payload structure: $json');
57
+ }
58
+ }
59
+
60
+ void main() {
61
+ // Test polymorphic deserialization
62
+ final success = parseApiResponse({
63
+ 'status': 'ok',
64
+ 'data': {'userId': '123'},
65
+ });
66
+ print('Parsed success: $success');
67
+
68
+ final error = parseApiResponse({
69
+ 'status': 'error',
70
+ 'message': 'Not found',
71
+ 'code': 404,
72
+ });
73
+ print('Parsed error: $error');
74
+
75
+ // Test nested validation
76
+ processUserPayload({
77
+ 'id': 'u100',
78
+ 'profile': {
79
+ 'name': 'Alice',
80
+ 'email': 'alice@example.com',
81
+ 'avatarUrl': 'https://example.com/avatar.png',
82
+ },
83
+ 'tags': ['admin', 'staff'],
84
+ });
85
+ }
@@ -0,0 +1,33 @@
1
+ ---
2
+ name: detect-mobile-stack
3
+ description: Detects the mobile stack of the current project (Flutter, Android, iOS, React Native, Kotlin Multiplatform), its languages and target platforms, using the reis-mobile detector. Use when you need to know which mobile stack a project uses before reviewing, debugging or changing it, or when the user asks "what stack is this project?".
4
+ routing: manual
5
+ stacks: ["*"]
6
+ ---
7
+
8
+ # Detect Mobile Stack
9
+
10
+ Use the reis-mobile deterministic detector instead of guessing from the folder structure.
11
+
12
+ ## How to run
13
+
14
+ ```bash
15
+ node "${CLAUDE_PLUGIN_ROOT}/bin/reis-mobile.mjs" detect --json --dir "<project directory>"
16
+ ```
17
+
18
+ Result fields:
19
+
20
+ | Field | Meaning |
21
+ |---|---|
22
+ | `stack` | `flutter`, `react-native`, `kotlin-multiplatform`, `android`, `ios` or `unknown` |
23
+ | `variant` | `plugin` (Flutter), `expo` (React Native) or `null` |
24
+ | `languages` | Languages found, the main one first (includes native code in cross-platform apps) |
25
+ | `platforms` | Target platforms (`android`, `ios`, `web`, `macos`, `desktop`...) |
26
+ | `evidence` | Files that justify the detection |
27
+ | `candidates` | Every stack that matched, in priority order |
28
+
29
+ ## Interpretation
30
+
31
+ - Cross-platform stacks take priority: a Flutter app contains `android/` and `ios/`, but the stack is `flutter`.
32
+ - `unknown` means there is no mobile project at the given root. In monorepos, run it again with `--dir` pointing at the app.
33
+ - More than one item in `candidates` indicates a hybrid project (for example, KMP with an Android module). Consider both stacks when loading skills.