diff --git a/AGENTS.md b/AGENTS.md index f9dd906..b052fef 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -57,6 +57,7 @@ skills/ # every / ships SKILL.md + agents/openai.yaml (C platforms/web.md platforms/windows.md testing.md + wcag-criteria.md widget-mapping.md animations/SKILL.md animations/references/ @@ -137,10 +138,9 @@ Every `SKILL.md` follows this structure: - `argument-hint` _(optional)_ — placeholder hint shown to the user (e.g., `"[file-or-directory]"`) - `model` _(optional)_ — model to use **while the skill is active**. The override applies for the rest of the current turn and is not saved to settings, so a user on Opus who triggers a - skill pinned to `sonnet` stays on sonnet until their next prompt. Nine skills here set it. - Note the eval consequence: `create-project` pins `haiku`, so in a two-arm run its with-plugin - arm answers on a weaker model than the no-plugin arm, which understates its measured lift. - Routing is decided before the switch, so a pin never explains a routing miss + skill pinned to `sonnet` stays on sonnet until their next prompt. No skill here sets it. + A pin weaker than the session model lowers that skill's eval Δ; it never explains a + routing miss, since routing is decided before the switch - `effort` _(optional)_ — reasoning effort while the skill is active (`low`/`medium`/`high`/ `xhigh`/`max`), overriding the session level for that turn. Seven skills here set it 2. **H1 title** — human-readable skill name diff --git a/agents/flutter-reviewer.md b/agents/flutter-reviewer.md index 1b7d474..defaa10 100644 --- a/agents/flutter-reviewer.md +++ b/agents/flutter-reviewer.md @@ -72,8 +72,7 @@ report"). If it is unavailable, rely on the four preloaded standards alone. ### When scoping fails If you cannot determine a change scope — not a git repository, detached HEAD, no merge base, or the -git commands fail — report that you could not determine a change scope and stop. Do not guess and do -not review the whole repository. +git commands fail — report that you could not determine a change scope and stop. Do not guess. ## Output @@ -127,9 +126,3 @@ coverage. For example: separately by the plugin's PostToolUse `analyze.sh` hook when code is written, not here. Use the analyzer only to corroborate a skill-based judgment. - **Untraceable findings.** If a finding cannot name one of the four loaded standards, omit it. - -## Dispatch contract - -When dispatched by an orchestrator or critic round, you self-scope via the adaptive diff procedure -above — the caller does not pass you a file list — and the caller consumes your findings table -verbatim. diff --git a/evals/README.md b/evals/README.md index 626d811..e352a7b 100644 --- a/evals/README.md +++ b/evals/README.md @@ -377,9 +377,9 @@ Read the two arm scores, not the total. The without-arm is supposed to score bad - **The no-plugin arm swings between runs.** One two-arm run cannot disqualify a grader: `accessibility-declines-gesture-detector-tap-target` failed all three content graders without the plugin on one run and passed all three on the next. -- **`create-project` pins `model: haiku`.** Its with-arm answers on a weaker model than its - baseline, so its Δ reads low. Routing is decided before the switch, so the pin never - explains a routing miss. +- **A `model` pin weaker than the session model lowers that skill's Δ.** The with-arm + answers on the pinned model, the baseline on the session model. It never explains a + routing miss. Costs: **$13** for 102 cases in one arm, **$25** for both, roughly **$0.12 per run**. At `--runs 3` a two-arm sweep is six runs per case, so budget around **$75**. `-j` up to 8 diff --git a/skills/accessibility/SKILL.md b/skills/accessibility/SKILL.md index ab0cbe4..83c61e6 100644 --- a/skills/accessibility/SKILL.md +++ b/skills/accessibility/SKILL.md @@ -19,7 +19,7 @@ Flutter accessibility auditing and remediation across WCAG 2.2 conformance level AAA. This file is the workflow; the reference files listed under **Additional Resources** are loaded on demand, and each phase below names the one it needs. -Load only the file the current phase names. Do not duplicate its content here. +Load only the file the current phase names. --- @@ -51,7 +51,7 @@ Apply these standards to all accessibility work: **Text Containers** (WCAG 1.4.4) — Fixed-height containers must not wrap `Text`. Use `minHeight` constraints. Fixed heights clip text at 1.5x font scale on Android, sooner on iOS where Larger Accessibility Sizes go to ~3.1x. -**Contrast** (WCAG 1.4.3) — All text and UI components must meet the contrast ratio for the selected WCAG level. See the WCAG Level Criteria Reference section below. +**Contrast** (WCAG 1.4.3) — All text and UI components must meet the contrast ratio for the selected WCAG level. See [`references/wcag-criteria.md`](references/wcag-criteria.md). **Cupertino Semantics** (WCAG 4.1.2) — Cupertino widgets (`CupertinoSwitch`, `CupertinoSlider`, `CupertinoSegmentedControl`, `CupertinoButton`) ship with weaker semantic defaults than their Material equivalents. Always wrap them in `Semantics(label:, value:, button:)`. @@ -203,77 +203,7 @@ Write tests covering every fix so it cannot regress. See [`references/testing.md ## WCAG 2.2 Level Criteria Reference -Level AA includes all Level A criteria. Level AAA includes all Level A and AA criteria. The "Version" column flags whether the criterion is from WCAG 2.0, 2.1, or 2.2. WCAG 2.2 removed 4.1.1 Parsing. - -### Level A - -| WCAG ID | Version | Criterion | Flutter Check | -| --- | --- | --- | --- | -| 1.1.1 | 2.0 | Non-text Content | `semanticLabel` on images, `Semantics(label:)` on icons, `excludeFromSemantics: true` on decorative | -| 1.3.1 | 2.0 | Info and Relationships | Semantic roles. `MergeSemantics` for grouped label/value pairs only, never around interactive children | -| 1.3.2 | 2.0 | Meaningful Sequence | Reading order matches visual order. `FocusTraversalGroup` + `OrderedTraversalPolicy` | -| 1.3.3 | 2.0 | Sensory Characteristics | Instructions do not rely solely on shape, size, location, or sound | -| 1.4.1 | 2.0 | Use of Color | Color never sole differentiator | -| 2.1.1 | 2.0 | Keyboard | All functionality via keyboard or switch access. No bare `GestureDetector` | -| 2.1.2 | 2.0 | No Keyboard Trap | Focus can always be moved away | -| 2.3.1 | 2.0 | Three Flashes or Below Threshold | No content flashes > 3 times per second | -| 2.4.1 | 2.0 | Bypass Blocks | Skip-navigation mechanism. **Web only** | -| 2.4.2 | 2.0 | Page Titled | Each screen has a meaningful title. **Web: `` tag** | -| 2.4.3 | 2.0 | Focus Order | Tab/focus order preserves meaning | -| 2.5.3 | 2.1 | Label in Name | Visible label text contained in accessible name | -| 3.2.6 | 2.2 | Consistent Help | Help mechanism in same relative order across screens | -| 3.3.1 | 2.0 | Error Identification | Form errors identified in text, not color alone | -| 3.3.2 | 2.0 | Labels or Instructions | All form fields have visible labels | -| 3.3.7 | 2.2 | Redundant Entry | Multi-step forms must not re-collect already-provided info unless re-entry is essential | -| 4.1.2 | 2.0 | Name, Role, Value | `Semantics(label:, button: true)`, `Tooltip`, state via `checked`, `selected`, `enabled` | -| 4.1.3 | 2.1 | Status Messages | `Semantics(liveRegion: true)`, `SemanticsService.announce()` | - -### Level AA (adds these to Level A) - -| WCAG ID | Version | Criterion | Flutter Check | -| --- | --- | --- | --- | -| 1.3.4 | 2.1 | Orientation | App not locked to single orientation without essential reason | -| 1.3.5 | 2.1 | Identify Input Purpose | Correct `keyboardType` and `autofillHints` | -| 1.4.3 | 2.0 | Contrast (Minimum) | Normal text 4.5:1, large text 3:1 | -| 1.4.4 | 2.0 | Resize Text | Text scales to 200% (300% on iOS) without loss | -| 1.4.5 | 2.0 | Images of Text | Use `Text`, not images of text | -| 1.4.10 | 2.1 | Reflow | Content reflows at 320 CSS px equivalent | -| 1.4.11 | 2.1 | Non-text Contrast | UI components and focus indicators 3:1 | -| 1.4.12 | 2.1 | Text Spacing | Content not lost under increased spacing | -| 1.4.13 | 2.1 | Content on Hover or Focus | Dismissable, hoverable, persistent. **Web/desktop** | -| 2.4.5 | 2.0 | Multiple Ways | More than one way to locate a screen | -| 2.4.6 | 2.0 | Headings and Labels | Descriptive. `Semantics(header: true)` for sections | -| 2.4.7 | 2.0 | Focus Visible | Keyboard focus indicator always visible | -| 2.4.11 | 2.2 | Focus Not Obscured (Minimum) | Focused component not entirely hidden by author-created content | -| 2.5.7 | 2.2 | Dragging Movements | Every drag has a single-pointer alternative | -| 2.5.8 | 2.2 | Target Size (Minimum) | Targets >= 24x24 CSS px, with documented exceptions | -| 3.1.2 | 2.0 | Language of Parts | **Web: `lang` attribute** | -| 3.2.3 | 2.0 | Consistent Navigation | Consistent across screens | -| 3.2.4 | 2.0 | Consistent Identification | Same-function components identified consistently | -| 3.3.3 | 2.0 | Error Suggestion | Suggested correction when possible | -| 3.3.4 | 2.0 | Error Prevention (Legal/Financial) | Reversible or confirmable | -| 3.3.8 | 2.2 | Accessible Authentication (Minimum) | No required cognitive function test without alternative. Allow paste, support password managers, no puzzle CAPTCHAs without alternative | - -### Level AAA (adds these to A and AA) - -| WCAG ID | Version | Criterion | Flutter Check | -| --- | --- | --- | --- | -| 1.4.6 | 2.0 | Contrast (Enhanced) | Normal 7:1, large 4.5:1 | -| 2.1.3 | 2.0 | Keyboard (No Exception) | No `GestureDetector` anywhere | -| 2.2.3 | 2.0 | No Timing | No time limits except real-time events | -| 2.2.6 | 2.1 | Timeouts | Inactivity warning | -| 2.3.2 | 2.0 | Three Flashes | Zero flashing | -| 2.3.3 | 2.1 | Animation from Interactions | Every animation gated on `disableAnimations` | -| 2.4.8 | 2.0 | Location | Users always know where they are | -| 2.4.9 | 2.0 | Link Purpose (Link Only) | Understandable from link text alone | -| 2.4.12 | 2.2 | Focus Not Obscured (Enhanced) | No occlusion at all, not just total occlusion | -| 2.4.13 | 2.2 | Focus Appearance | At least 2 CSS px perimeter, encloses component, 3:1 against unfocused | -| 2.5.5 | 2.1 | Target Size (Enhanced) | Targets >= 44x44 CSS px | -| 2.5.6 | 2.1 | Concurrent Input Mechanisms | No single-modality restriction | -| 3.2.5 | 2.0 | Change on Request | Context changes only on user request | -| 3.3.5 | 2.0 | Help | Context-sensitive help available | -| 3.3.6 | 2.0 | Error Prevention (All) | All submissions reversible or confirmable | -| 3.3.9 | 2.2 | Accessible Authentication (Enhanced) | No cognitive function test even with alternative | +Per-level criterion tables with the Flutter check for each: [`references/wcag-criteria.md`](references/wcag-criteria.md). Load it in Phase 3. --- @@ -349,6 +279,7 @@ For corrected snippets, full classes (`AccessibleTapTarget`, `AccessibleSlider`, ## Additional Resources +- [`references/wcag-criteria.md`](references/wcag-criteria.md) — per-level WCAG 2.2 criterion tables with the Flutter check for each. - [`references/audit-templates.md`](references/audit-templates.md) — severity guide, report template, level-specific passed-check lists, cross-platform severity table. - [`references/examples.md`](references/examples.md) — full Flutter widget classes per category, including all WCAG 2.2 patterns. - [`references/widget-mapping.md`](references/widget-mapping.md) — widget-to-requirement quick reference for all commonly audited Flutter widgets. diff --git a/skills/accessibility/references/wcag-criteria.md b/skills/accessibility/references/wcag-criteria.md new file mode 100644 index 0000000..453241f --- /dev/null +++ b/skills/accessibility/references/wcag-criteria.md @@ -0,0 +1,75 @@ +# WCAG 2.2 Level Criteria Reference + +Every criterion by level, with the Flutter check that satisfies it. + +Level AA includes all Level A criteria. Level AAA includes all Level A and AA criteria. The "Version" column flags whether the criterion is from WCAG 2.0, 2.1, or 2.2. WCAG 2.2 removed 4.1.1 Parsing. + +## Level A + +| WCAG ID | Version | Criterion | Flutter Check | +| --- | --- | --- | --- | +| 1.1.1 | 2.0 | Non-text Content | `semanticLabel` on images, `Semantics(label:)` on icons, `excludeFromSemantics: true` on decorative | +| 1.3.1 | 2.0 | Info and Relationships | Semantic roles. `MergeSemantics` for grouped label/value pairs only, never around interactive children | +| 1.3.2 | 2.0 | Meaningful Sequence | Reading order matches visual order. `FocusTraversalGroup` + `OrderedTraversalPolicy` | +| 1.3.3 | 2.0 | Sensory Characteristics | Instructions do not rely solely on shape, size, location, or sound | +| 1.4.1 | 2.0 | Use of Color | Color never sole differentiator | +| 2.1.1 | 2.0 | Keyboard | All functionality via keyboard or switch access. No bare `GestureDetector` | +| 2.1.2 | 2.0 | No Keyboard Trap | Focus can always be moved away | +| 2.3.1 | 2.0 | Three Flashes or Below Threshold | No content flashes > 3 times per second | +| 2.4.1 | 2.0 | Bypass Blocks | Skip-navigation mechanism. **Web only** | +| 2.4.2 | 2.0 | Page Titled | Each screen has a meaningful title. **Web: `<title>` tag** | +| 2.4.3 | 2.0 | Focus Order | Tab/focus order preserves meaning | +| 2.5.3 | 2.1 | Label in Name | Visible label text contained in accessible name | +| 3.2.6 | 2.2 | Consistent Help | Help mechanism in same relative order across screens | +| 3.3.1 | 2.0 | Error Identification | Form errors identified in text, not color alone | +| 3.3.2 | 2.0 | Labels or Instructions | All form fields have visible labels | +| 3.3.7 | 2.2 | Redundant Entry | Multi-step forms must not re-collect already-provided info unless re-entry is essential | +| 4.1.2 | 2.0 | Name, Role, Value | `Semantics(label:, button: true)`, `Tooltip`, state via `checked`, `selected`, `enabled` | +| 4.1.3 | 2.1 | Status Messages | `Semantics(liveRegion: true)`, `SemanticsService.announce()` | + +## Level AA (adds these to Level A) + +| WCAG ID | Version | Criterion | Flutter Check | +| --- | --- | --- | --- | +| 1.3.4 | 2.1 | Orientation | App not locked to single orientation without essential reason | +| 1.3.5 | 2.1 | Identify Input Purpose | Correct `keyboardType` and `autofillHints` | +| 1.4.3 | 2.0 | Contrast (Minimum) | Normal text 4.5:1, large text 3:1 | +| 1.4.4 | 2.0 | Resize Text | Text scales to 200% (300% on iOS) without loss | +| 1.4.5 | 2.0 | Images of Text | Use `Text`, not images of text | +| 1.4.10 | 2.1 | Reflow | Content reflows at 320 CSS px equivalent | +| 1.4.11 | 2.1 | Non-text Contrast | UI components and focus indicators 3:1 | +| 1.4.12 | 2.1 | Text Spacing | Content not lost under increased spacing | +| 1.4.13 | 2.1 | Content on Hover or Focus | Dismissable, hoverable, persistent. **Web/desktop** | +| 2.4.5 | 2.0 | Multiple Ways | More than one way to locate a screen | +| 2.4.6 | 2.0 | Headings and Labels | Descriptive. `Semantics(header: true)` for sections | +| 2.4.7 | 2.0 | Focus Visible | Keyboard focus indicator always visible | +| 2.4.11 | 2.2 | Focus Not Obscured (Minimum) | Focused component not entirely hidden by author-created content | +| 2.5.7 | 2.2 | Dragging Movements | Every drag has a single-pointer alternative | +| 2.5.8 | 2.2 | Target Size (Minimum) | Targets >= 24x24 CSS px, with documented exceptions | +| 3.1.2 | 2.0 | Language of Parts | **Web: `lang` attribute** | +| 3.2.3 | 2.0 | Consistent Navigation | Consistent across screens | +| 3.2.4 | 2.0 | Consistent Identification | Same-function components identified consistently | +| 3.3.3 | 2.0 | Error Suggestion | Suggested correction when possible | +| 3.3.4 | 2.0 | Error Prevention (Legal/Financial) | Reversible or confirmable | +| 3.3.8 | 2.2 | Accessible Authentication (Minimum) | No required cognitive function test without alternative. Allow paste, support password managers, no puzzle CAPTCHAs without alternative | + +## Level AAA (adds these to A and AA) + +| WCAG ID | Version | Criterion | Flutter Check | +| --- | --- | --- | --- | +| 1.4.6 | 2.0 | Contrast (Enhanced) | Normal 7:1, large 4.5:1 | +| 2.1.3 | 2.0 | Keyboard (No Exception) | No `GestureDetector` anywhere | +| 2.2.3 | 2.0 | No Timing | No time limits except real-time events | +| 2.2.6 | 2.1 | Timeouts | Inactivity warning | +| 2.3.2 | 2.0 | Three Flashes | Zero flashing | +| 2.3.3 | 2.1 | Animation from Interactions | Every animation gated on `disableAnimations` | +| 2.4.8 | 2.0 | Location | Users always know where they are | +| 2.4.9 | 2.0 | Link Purpose (Link Only) | Understandable from link text alone | +| 2.4.12 | 2.2 | Focus Not Obscured (Enhanced) | No occlusion at all, not just total occlusion | +| 2.4.13 | 2.2 | Focus Appearance | At least 2 CSS px perimeter, encloses component, 3:1 against unfocused | +| 2.5.5 | 2.1 | Target Size (Enhanced) | Targets >= 44x44 CSS px | +| 2.5.6 | 2.1 | Concurrent Input Mechanisms | No single-modality restriction | +| 3.2.5 | 2.0 | Change on Request | Context changes only on user request | +| 3.3.5 | 2.0 | Help | Context-sensitive help available | +| 3.3.6 | 2.0 | Error Prevention (All) | All submissions reversible or confirmable | +| 3.3.9 | 2.2 | Accessible Authentication (Enhanced) | No cognitive function test even with alternative | diff --git a/skills/animations/SKILL.md b/skills/animations/SKILL.md index 4bfc5fe..5ee6153 100644 --- a/skills/animations/SKILL.md +++ b/skills/animations/SKILL.md @@ -19,7 +19,7 @@ motion guidelines. No third-party animation libraries (Lottie, Rive, etc.). ## Core Standards -Apply these standards to ALL animation work: +Apply these standards to all animation work: - **Clarify visual intent when the request is ambiguous** — when the developer says "add an animation" or "make it smoother" without specifying property, trigger, duration, or curve, ask before writing code. If the developer provides clear specs (e.g., "300ms ease-in fade on the card when it appears"), proceed directly - **Use the simplest animation approach that works** — follow the decision tree below; never reach for `AnimationController` when an implicit animation suffices, including when several properties animate at the same time @@ -30,7 +30,7 @@ Apply these standards to ALL animation work: - **Use `SingleTickerProviderStateMixin` for one controller** — use `TickerProviderStateMixin` only when the widget owns multiple controllers - **Keep animated subtrees small** — wrap only the widgets that change inside the animation builder, not entire widget trees - **Never animate a layout-triggering property** — `width`, `height`, `padding` and `SizedBox` dimensions force a fresh layout pass on every frame, in a one-child tree as much as in a deep one. Animate a `Transform` instead, `Transform.scale` for size and `Transform.translate` for position, or `Opacity` for fade, since those run on the compositing layer and skip layout -- **Dart 3.13 primary constructors** — on the Dart 3.13+ baseline (`very_good_core` 1.6 / `very_good_analysis` 11), declare widget fields as primary-constructor declaring parameters (`class const LabelChip({required final String label, super.key}) extends StatelessWidget`) rather than `this.field`; keep the classic form only below 3.13 +- **Dart 3.13 primary constructors** — on a Dart 3.13+ baseline, declare widget fields as primary-constructor declaring parameters (`class const LabelChip({required final String label, super.key}) extends StatelessWidget`) rather than `this.field`; keep the classic form only below 3.13 --- @@ -266,49 +266,13 @@ wiring, and `Hero` shared-element transitions. ### Do Not -- **Do not animate `width`, `height`, or `padding`** — each frame forces a new layout pass over the subtree, and the cost is the layout pass itself, not the depth of the tree, so a single `SizedBox` around one child is no exception. Replace a growing width with `Transform.scale` and a moving offset with `Transform.translate` - **Do not wrap entire screens in `AnimatedBuilder`** — only wrap the subtree that changes -- **Do not create multiple `AnimationController` instances for animations that share timing** — use `Interval` on a single controller. This applies once the animation already needs a controller; properties that animate to a target on the same rebuild are composed implicit widgets, not one controller with intervals +- **Do not create multiple `AnimationController` instances for animations that share timing** — use `Interval` on a single controller --- ## Anti-Patterns -### Hardcoded magic values - -```dart -// Bad — arbitrary values with no semantic meaning -AnimatedContainer( - duration: Duration(milliseconds: 375), - curve: Curves.easeInOutCubic, - // ... -) - -// Good — M3 tokens with clear intent -AnimatedContainer( - duration: Durations.medium2, - curve: Easing.standard, - // ... -) -``` - -### Missing controller disposal - -```dart -// Bad — memory leak -@override -void dispose() { - super.dispose(); -} - -// Good — dispose before super.dispose() -@override -void dispose() { - _controller.dispose(); - super.dispose(); -} -``` - ### Animating a width instead of a Transform ```dart @@ -353,6 +317,7 @@ AnimatedBuilder( ); }, ) +``` Explicit animation where implicit suffices. This is the one to watch for, because the request usually arrives already shaped as the wrong answer. diff --git a/skills/bloc/SKILL.md b/skills/bloc/SKILL.md index 8833249..5f16ef4 100644 --- a/skills/bloc/SKILL.md +++ b/skills/bloc/SKILL.md @@ -23,7 +23,7 @@ State management library for Dart and Flutter using the BLoC (Business Logic Com ## Core Standards -Apply these standards to ALL Bloc/Cubit work: +Apply these standards to all Bloc/Cubit work: - **Use `blocTest()` from `package:bloc_test`** for all Bloc and Cubit tests — never raw `test()` with manual stream assertions - **Use `package:mocktail` for mocking** — never `package:mockito` @@ -34,8 +34,8 @@ Apply these standards to ALL Bloc/Cubit work: - **Equatable for all states and events** — extend `Equatable` and override `props` for value equality - **Business logic in Bloc/Cubit only** — never in widgets, pages, or views - **Single responsibility** — one Bloc/Cubit per feature concern -- **Emit only after async checks** — use `emit` only inside the handler callback -- **Dart 3.13 primary constructors** — on the Dart 3.13+ baseline (`very_good_core` 1.6 / `very_good_analysis` 11), declare event, state, and widget fields as primary-constructor declaring parameters (`class const ProfileRequested(final String userId) extends ProfileEvent`) rather than `this.field`; keep the classic form only below 3.13 +- **`emit` only inside the handler** — call `emit` from the `on<Event>` callback or Cubit method that owns it, never from a stream subscription or callback that outlives the handler +- **Dart 3.13 primary constructors** — on a Dart 3.13+ baseline, declare event, state, and widget fields as primary-constructor declaring parameters (`class const ProfileRequested(final String userId) extends ProfileEvent`) rather than `this.field`; keep the classic form only below 3.13 --- diff --git a/skills/create-project/SKILL.md b/skills/create-project/SKILL.md index e8f4d97..7795689 100644 --- a/skills/create-project/SKILL.md +++ b/skills/create-project/SKILL.md @@ -14,7 +14,6 @@ description: > template. allowed-tools: mcp__very-good-cli__create mcp__very-good-cli__packages_get argument-hint: "[template] [project-name]" -model: haiku --- # Create Project @@ -38,13 +37,13 @@ Scaffold a new Dart or Flutter project using Very Good CLI templates. ### Step 1: Understand What the User Wants to Build -Infer the subcommand from the user's description — the available subcommands and their descriptions are defined by the Very Good CLI MCP server. Do NOT ask users to pick a subcommand name — figure it out from context. +Infer the subcommand from the user's description — the available subcommands and their descriptions are defined by the Very Good CLI MCP server. If the intent is ambiguous, use `AskUserQuestion` to clarify with a high-level question about what they're building — not which subcommand they want. ### Step 2: Gather Missing Parameters -Use `AskUserQuestion` to collect only what you cannot infer. Batch questions into a single call when possible. Do NOT ask for optional parameters (description, output directory, application ID, etc.) unless the user brings them up. +Use `AskUserQuestion` for what you cannot infer, in one call. Leave optional parameters (description, output directory, application ID) at their defaults unless the user raises them. ### Step 3: Create and Set Up @@ -62,34 +61,6 @@ Use `AskUserQuestion` to collect only what you cannot infer. Batch questions int --- -## Examples - -### User says "Create a new Flutter app" - -1. Infer: `flutter_app` -2. Ask for project name and organization -3. Create and install dependencies - -### User says "I need a package for my weather API client, put it in packages/" - -1. Infer: "API client" → pure Dart → `dart_package`, name `weather_api_client`, output `packages/` -2. Everything is clear — no questions needed -3. Create and install dependencies - -### User says "I want to build something for iOS and Android with bluetooth" - -1. Ambiguous: app or plugin? Ask to clarify -2. Gather remaining parameters based on answer -3. Create and install dependencies - -### User says "Create a new package" - -1. Ambiguous: Flutter or pure Dart? Ask to clarify -2. Ask for project name -3. Create and install dependencies - ---- - ## Anti-Patterns | Anti-Pattern | Problem | Correct Approach | diff --git a/skills/dart-flutter-sdk-upgrade/SKILL.md b/skills/dart-flutter-sdk-upgrade/SKILL.md index 56f7e2c..39208cc 100644 --- a/skills/dart-flutter-sdk-upgrade/SKILL.md +++ b/skills/dart-flutter-sdk-upgrade/SKILL.md @@ -13,7 +13,6 @@ description: > when the blocking package has a skill of its own. argument-hint: "[flutter-version]" allowed-tools: Read Glob Grep Edit Write Bash -model: sonnet effort: medium --- @@ -26,7 +25,7 @@ version bumps, no test changes. ## Core Standards -Apply these standards to ALL SDK upgrade work: +Apply these standards to all SDK upgrade work: - **Flutter and Dart versions differ** — a Flutter release ships a Dart SDK whose version number is unrelated to it, and the pubspec `sdk:` constraint takes the Dart number, never the Flutter one. Look the pairing up in the release archive at <https://docs.flutter.dev/install/archive> - **Never state a bundled Dart version from memory** — not from recall, not by inference from the Flutter number, and not from an example in this file. Either the user supplied the Dart version, or you read it off the archive, or you ask. There is no fourth source @@ -230,4 +229,4 @@ No logic or code changes. ``` Both numbers appear in the subject line because a reviewer scanning history needs to see which -Dart release came with the bump. Fill them from step 0, never from recall. +Dart release came with the bump. diff --git a/skills/green-gate/SKILL.md b/skills/green-gate/SKILL.md index 9b5459f..972a076 100644 --- a/skills/green-gate/SKILL.md +++ b/skills/green-gate/SKILL.md @@ -2,19 +2,17 @@ name: green-gate description: > Drives a Dart or Flutter package fully green through a verify-fix-rerun loop across four gates: - analyze, format, test, coverage. It owns gate configuration, so plan-only questions belong here: - which tool and arguments run each gate, in what order, the coverage target a package is held to, - and what leaves the coverage denominator. Use it when the user says "green gate", "make it - green", "get CI green", "fix all the analyze and test failures", "clean this package up before I - open a PR", "bring coverage to 100", "loop until everything passes", "just re-check coverage", - or "confirm the package is green". Use it too when the user wants a gate weakened or skipped: - drop the coverage threshold to 90, put coverage ignore comments on untested or generated code - (.freezed.dart, app_localizations.dart), skip analyze and format because they passed earlier, - call a 94% package clean and done, or keep retrying while the same failures repeat round after - round. Prefer it over the single-gate testing or analysis skills. + analyze, format, test, coverage. It owns gate configuration, so plan-only questions belong + here: which tool and arguments run each gate, in what order, the coverage target, and what + leaves the coverage denominator. Use it for "green gate", "make it green", "get CI green", + fixing all analyze and test failures, cleaning a package up before a PR, bringing coverage to + 100, looping until everything passes, or confirming a package is green. Use it too when the + user wants a gate weakened or skipped: lowering the coverage threshold, coverage ignore + comments on untested or generated code, skipping analyze and format because they passed + earlier, calling a 94% package done, or retrying while the same failures repeat. Prefer it over + the single-gate testing or analysis skills. argument-hint: "[directory]" allowed-tools: Bash Read Glob Grep Edit Write mcp__dart__analyze_files mcp__dart__dart_format mcp__very-good-cli__test -model: sonnet effort: medium --- @@ -34,7 +32,7 @@ coverage-pattern guidance. ## Core Standards -Apply these to ALL green-gate work: +Apply these to all green-gate work: - **MCP tools only, never the Bash equivalent** — analyze via `mcp__dart__analyze_files`, format via `mcp__dart__dart_format`, test and coverage @@ -60,14 +58,12 @@ analyze` / `flutter test --coverage` for the tools the loop actually runs. - **Exit only on observed numbers** — the loop terminates only after a single final iteration in which analyze is clean, format reports zero changes, all tests pass, and `min_coverage` is satisfied, all observed in the same round. - Declaring success from memory is forbidden; confirm success only with the - actual numbers observed in that final round. - **Pass `coverage: true`, `min_coverage`, and `check_ignore: true` together** — omitting `coverage: true` silently produces no `lcov.info` (mimics a misconfiguration); omitting `check_ignore: true` makes the `// coverage:ignore` remedy a no-op. - **Defer test-writing to the `testing` skill** — when a fix requires authoring - tests, follow `skills/testing/SKILL.md` for structure, mocking, and naming. + tests, follow that skill for structure, mocking, and naming. - **Fix root causes, not symptoms** — never weaken a gate to pass it (do not delete failing assertions, lower the target to dodge work, or `// coverage:ignore` reachable code). Escalate genuine product/API decisions instead of guessing. @@ -236,13 +232,11 @@ percentage and per-file fix targets. - **Fix only failing items** — address the diagnostics, tests, or under-covered files surfaced this round. Do not refactor unrelated code (YAGNI). - **Coverage fixes = author tests** — for each ranked under-covered `SF` file, - write tests following `skills/testing/SKILL.md`. Prioritize files by uncovered + write tests following the `testing` skill. Prioritize files by uncovered line count (`LF - LH`). - **Bound files per round** — fix a coherent batch, then re-verify. Re-running the gates after each batch is what makes "no progress" detectable and prevents fixing one gate while silently breaking another. -- **Never weaken a gate** — no deleted assertions, no lowered target to dodge - work, no `// coverage:ignore` on reachable code. --- @@ -295,9 +289,5 @@ report, and wait. - [`references/coverage.md`](references/coverage.md) — green-gate's coverage-gate detail (default target, exclude globs, lcov fields, decision tree, `check_ignore`, stale lcov). -- `skills/testing/SKILL.md` — how to write Dart unit, Flutter widget, and golden - tests (structure, `mocktail` mocking, naming). -- `skills/testing/references/coverage.md` — coverage-driven test patterns - (`copyWith`, branches, error paths) for closing per-file gaps. -- `hooks/scripts/block-cli-workarounds.sh` — why the Bash test path is blocked and - every gate runs through its MCP tool. +- The `testing` skill — test conventions, plus its coverage reference for closing + per-file gaps. diff --git a/skills/internationalization/SKILL.md b/skills/internationalization/SKILL.md index 0a61522..91923ef 100644 --- a/skills/internationalization/SKILL.md +++ b/skills/internationalization/SKILL.md @@ -14,7 +14,6 @@ description: > including mirroring padding, alignment, icons, or images, and replacing `EdgeInsets` left/right. allowed-tools: Read Glob Grep -model: sonnet --- # Internationalization @@ -23,7 +22,7 @@ Internationalization (i18n) and localization (l10n) best practices for Flutter a ## Core Standards -Apply these standards to ALL internationalization work: +Apply these standards to all internationalization work: - **Never hardcode user-facing strings** — all text must go through the l10n system - **Use Flutter's built-in localization system** — `flutter_localizations` + `intl`, never third-party i18n libraries @@ -33,7 +32,7 @@ Apply these standards to ALL internationalization work: - **Use `EdgeInsetsDirectional` (start/end) instead of `EdgeInsets` (left/right)** — ensures correct layout in RTL languages - **Handle RTL layout properly** — use directional widgets for padding, positioning, and alignment - **Implement i18n early** — even if only one language is planned initially, the overhead is small and the long-term benefit is significant -- **Dart 3.13 primary constructors** — on the Dart 3.13+ baseline (`very_good_core` 1.6 / `very_good_analysis` 11), declare a reusable widget's localized-string fields as primary-constructor declaring parameters (`class const ConfirmDialog({required final String title, super.key}) extends StatelessWidget`) rather than `this.field`; keep the classic form only below 3.13 +- **Dart 3.13 primary constructors** — on a Dart 3.13+ baseline, declare a reusable widget's localized-string fields as primary-constructor declaring parameters (`class const ConfirmDialog({required final String title, super.key}) extends StatelessWidget`) rather than `this.field`; keep the classic form only below 3.13 ## Setup Pipeline and ARB File Format diff --git a/skills/layered-architecture/SKILL.md b/skills/layered-architecture/SKILL.md index 5fd9432..0341c31 100644 --- a/skills/layered-architecture/SKILL.md +++ b/skills/layered-architecture/SKILL.md @@ -4,14 +4,13 @@ description: > VGV layered monorepo architecture in Flutter: four layers Data, Repository, Business Logic, and Presentation, unidirectional dependency rules, and model transformation across layers. Use when structuring a multi-package Flutter app, creating data or repository packages, defining layer - boundaries, or wiring packages in app bootstrap via path dependencies in pubspec.yaml, barrel - exports, and RepositoryProvider. Use too when asked to put a domain model, entity, or shared - class in an api_client or data package so the repository and app import it from one place, or to - add a cross-package dependency the layers forbid. Also use for a new app, from a one-line - description like "I'm starting a weather app that reads from a REST API" plus a request to lay - out the package structure, project, folder, or directory layout, or asking how to organize an - app that talks to a backend, where a file or feature should live, or which package code belongs - in. Applies even when the user never says monorepo, layer, or architecture. + boundaries, or wiring packages in app bootstrap via path dependencies, barrel exports, and + RepositoryProvider. Use too when asked to put a domain model or shared class in an api_client + or data package, or to add a cross-package dependency the layers forbid. Also use for a new app + described in one line ("I'm starting a weather app that reads from a REST API") plus a request + for the package, project, folder, or directory layout, or for where a file or feature should + live and which package code belongs in. Applies even when the user never says monorepo, layer, + or architecture. allowed-tools: Read Glob Grep mcp__very-good-cli__create mcp__very-good-cli__packages_get mcp__very-good-cli__test effort: high --- @@ -24,7 +23,7 @@ Layered monorepo architecture for Flutter apps — four layers organized as inde ## Core Standards -Apply these standards to ALL layered architecture work: +Apply these standards to all layered architecture work: - **Four layers** — Data, Repository, Business Logic, Presentation — a feature spans all four whenever its repository reads an external source - **Unidirectional dependencies** — Presentation → Business Logic → Repository → Data — never skip or invert a layer @@ -39,7 +38,7 @@ Apply these standards to ALL layered architecture work: - **Repositories take every external source through the constructor** — a data client or an SDK object such as `FirebaseAuth.instance`, built in bootstrap and passed in, never constructed or defaulted inside the repository - **App bootstrap wires all layers** — `main_<flavor>.dart` creates clients and repositories, provides them via `RepositoryProvider` - **Only the app's entrypoints and bootstrap import a data package** — blocs, widgets, and app tests reach data through a repository -- **Dart 3.13 primary constructors** — on the Dart 3.13+ baseline (`very_good_core` 1.6 / `very_good_analysis` 11), declare model and widget fields as primary-constructor declaring parameters (`class const User(final String id, final String name) extends Equatable`) rather than `this.field`; keep the classic form only below 3.13 +- **Dart 3.13 primary constructors** — on a Dart 3.13+ baseline, declare model and widget fields as primary-constructor declaring parameters (`class const User(final String id, final String name) extends Equatable`) rather than `this.field`; keep the classic form only below 3.13 > **Cross-harness fallback.** This skill scaffolds and tests packages via the Very Good CLI MCP server. On a host without this plugin's Bash hooks and without that MCP server connected, run the equivalent `very_good create dart_package …`, `very_good packages get`, and `very_good test` commands directly. @@ -260,31 +259,9 @@ checking the import boundary, including which files count as entrypoints and boo ## Data Flow -Step-by-step walkthrough: user taps "Load Profile" button. +Presentation dispatches an event → Bloc calls the repository → repository calls the data client and returns a domain model → `BlocBuilder` rebuilds on the new state. -1. **Presentation** dispatches event — `context.read<ProfileBloc>().add(ProfileLoadRequested(userId: '123'))` -2. **Business Logic** calls repository — Bloc handler invokes `_userRepository.getUser(event.userId)` and emits state based on the result -3. **Repository** calls data client — `UserRepository.getUser` delegates to `_userApiClient.getUser` and transforms the response into a domain `User` -4. **Data layer** communicates with external source — `UserApiClient.getUser` makes the HTTP request and returns a typed `UserResponse` -5. **Data flows back up** — Presentation rebuilds via `BlocBuilder` based on the new state - -```dart -// lib/profile/bloc/profile_bloc.dart -Future<void> _onLoadRequested( - ProfileLoadRequested event, - Emitter<ProfileState> emit, -) async { - emit(const ProfileState.loading()); - try { - final user = await _userRepository.getUser(event.userId); - emit(ProfileState.success(user: user)); - } on UserNotFoundException { - emit(const ProfileState.notFound()); - } catch (_) { - emit(const ProfileState.failure()); - } -} -``` +See [references/data-flow.md](references/data-flow.md) for the code at each layer. ## App Bootstrap diff --git a/skills/license-compliance/SKILL.md b/skills/license-compliance/SKILL.md index 912047d..c959563 100644 --- a/skills/license-compliance/SKILL.md +++ b/skills/license-compliance/SKILL.md @@ -13,7 +13,6 @@ description: > trigger, not a substitute for the audit. argument-hint: "[project-directory]" allowed-tools: Read Glob Grep mcp__very-good-cli__packages_check_licenses -model: sonnet effort: medium --- @@ -27,7 +26,7 @@ Dependency license auditor for Dart and Flutter projects — verifies that all p ## Core Standards -Apply these standards to ALL license compliance work: +Apply these standards to all license compliance work: - **Run `packages_check_licenses` MCP tool** on the target project directory with `licenses: true` to display full license information - **Pass `directory` to the MCP tool when the project is not at the workspace root** — monorepos with the project in a subdirectory (e.g. `mobile/`) require `directory: 'mobile'` @@ -91,7 +90,7 @@ All other dependencies use permissive licenses (MIT, BSD, Apache 2.0). 2. [Next action] ``` -Three ways this comes out wrong, all of which fail the format: prose paragraphs instead of the headings, flagged packages as a bulleted list instead of table rows, and a recommendation folded into the risk cell instead of standing as its own column. One row per flagged package, one recommendation per row. +One row per flagged package, one recommendation per row. --- @@ -107,6 +106,6 @@ So a pasted dependency list cannot produce a verdict, no matter how well known t 2. **Name the likely licenses if it helps.** Saying `http` and `intl` are BSD-3-Clause is useful orientation and costs nothing, as long as it is framed as what the scan is expected to confirm rather than as the finding. 3. **Give the exact command that produces a real answer.** `packages_check_licenses` with `licenses: true`, or `very_good packages check licenses <project-directory> --dependency-type direct-main,transitive`, and offer to run it. -Two phrasings to avoid, because both read as certification: "your dependency list looks compliant" and "these are all permissive, so you're clear." Withhold the verdict entirely until the scan output exists. +Until scan output exists, withhold the verdict. Do not call the list compliant or clear. The same rule covers a scan that ran but came back incomplete. A package whose license the tool could not detect is `Unknown/Missing` and stays flagged. It does not become compliant because its pub.dev page says MIT. diff --git a/skills/material-theming/SKILL.md b/skills/material-theming/SKILL.md index 33a7c78..5459078 100644 --- a/skills/material-theming/SKILL.md +++ b/skills/material-theming/SKILL.md @@ -10,7 +10,6 @@ description: > when the request only says "review this widget", "cut the duplication", "stop repeating this", or "tidy this up". allowed-tools: Read Glob Grep -model: sonnet --- # Material Theming @@ -19,7 +18,7 @@ Material 3 theming best practices for Flutter applications using `ThemeData` as ## Core Standards -Apply these standards to ALL theming work: +Apply these standards to all theming work: - **Use `ThemeData` as the single source of truth** — never inline colors or text styles in widgets - **Reference colors via `Theme.of(context).colorScheme`** — never `Colors.blue`, `Colors.red`, or any hardcoded `Color` values diff --git a/skills/navigation/SKILL.md b/skills/navigation/SKILL.md index bcfcb21..70df3a0 100644 --- a/skills/navigation/SKILL.md +++ b/skills/navigation/SKILL.md @@ -12,7 +12,6 @@ description: > a result to the caller. A one-line navigation call is still navigation work. Route motion belongs to the animations skill, even inside a `GoRouteData` subclass. allowed-tools: Read Glob Grep -model: sonnet --- # Navigation @@ -21,7 +20,7 @@ Routing and navigation best practices for Flutter applications using GoRouter, t ## Core Standards -Apply these standards to ALL navigation work: +Apply these standards to all navigation work: - **Use `package:go_router` for all navigation** — never raw Navigator 2.0 or Navigator 1.0 push/pop - **Use `@TypedGoRoute` annotations for type-safe routes** — never raw string paths in route definitions diff --git a/skills/static-security/SKILL.md b/skills/static-security/SKILL.md index 9806565..9a8e745 100644 --- a/skills/static-security/SKILL.md +++ b/skills/static-security/SKILL.md @@ -4,14 +4,13 @@ description: > Static security review for Flutter mobile apps and Dart code: hardcoded secrets, insecure storage, unsafe network calls, leaky logs, vulnerable dependencies. Use when reviewing or writing code handling secrets, user data, network communication, authentication, or - cryptography, or adding validation to user input in login, sign-up, or payment forms reaching a - repository or an API, as in "add validation to this form", "nothing is checked before this hits - the API", or "validate these fields". Also when asked to implement the insecure change, not - review it: "our reviewer flagged this line", "move this API key to --dart-define", "read it back - with String.fromEnvironment", a .env file, or "pass it in from our CI secrets". Also for - dependency vulnerability review and security audits even when it never says "security": "we cut - a release tomorrow, is this pubspec safe", "check our dependencies for known vulnerabilities", - "scan for CVEs", "is this ignored_advisories entry fine", or pinned versions we're exposed to. + cryptography, or adding validation to user input in login, sign-up, or payment forms before it + reaches a repository or an API. Also when asked to implement the insecure change rather than + review it: moving an API key to --dart-define, String.fromEnvironment, a .env file, or CI + secrets, or "our reviewer flagged this line". Also for dependency vulnerability review and + security audits that never say "security": whether a pubspec is safe to release, checking + dependencies for known vulnerabilities or CVEs, an ignored_advisories entry, or pinned versions + we're exposed to. argument-hint: "[file-or-directory]" allowed-tools: Read Glob Grep mcp__very-good-cli__packages_check_licenses effort: high @@ -23,7 +22,7 @@ Flutter apps compile all Dart code directly into a binary that runs on untrusted ## Core Standards -Apply these standards to ALL Flutter security work: +Apply these standards to all Flutter security work: - **Never hardcode secrets** — API keys, tokens, and passwords in source code or config files are compiled into the binary and extractable via reverse engineering; serve them from a backend service - **`--dart-define` is not a fix for a hardcoded secret** — neither is `String.fromEnvironment`, a `.env` file, a native config file, an obfuscated constant, or a split-up string. Every one of them still ships the value inside the binary in recoverable form, so moving a key into one is the same finding in a new location. The only remediation is fetching it from a backend at runtime @@ -36,7 +35,7 @@ Apply these standards to ALL Flutter security work: - **Keep dependencies free of known vulnerabilities** — never suppress security advisories without documented justification; scan `pubspec.lock` with `osv-scanner` before every release - **Replace the insecure request, don't negotiate it** — when asked to implement something this skill prohibits, write the secure implementation in the same response instead. Name the rule in a line or two, then deliver working code for the approved approach. Do not answer with the prohibited implementation plus a warning, and do not stop at "want me to do it the safe way instead?" — an offer is not a replacement. If the developer reaffirms the prohibited approach after reading why, say what the residual risk is and proceed - **Set `android:allowBackup="false"`** — the Android default silently allows `adb backup` to extract app data, bypassing `package:flutter_secure_storage` -- **Label every finding `Critical`, `Warning`, or `Note`** — these three are the only severity tiers; don't substitute a scheme of your own +- **Label every finding `Critical`, `Warning`, or `Note`** — see Severity Triage below ## Severity Triage @@ -314,7 +313,7 @@ ignored_advisories: - GHSA-4rgh-jx4f-xxxx # Not applicable: we never construct http.Client directly ``` -An entry with no written justification is a `Warning` finding in its own right, regardless of whether the advisory turns out to apply. "Someone decided this was fine once" is not a record — the suppression silences the scanner on every future run, so the reasoning has to survive in the file rather than in memory. Requiring the reviewer to go and confirm why it was added does not close the finding: the fix is the comment. +An entry with no comment is a `Warning` finding on its own, whether or not the advisory applies: the suppression silences the scanner on every future run, so the reason has to live in the file. Exact-pinned direct dependencies deserve a `Note`. A pin like `http: 0.13.0` means the scan only ever sees that one version, so a patch that fixes a known CVE will never resolve on its own. diff --git a/skills/testing/SKILL.md b/skills/testing/SKILL.md index 6630a9b..ba5940c 100644 --- a/skills/testing/SKILL.md +++ b/skills/testing/SKILL.md @@ -4,14 +4,13 @@ description: > Best practices for Dart unit tests, Flutter widget tests, and golden file tests, covering group and test naming, setUp lifecycle and isolation, mocking with package:mocktail, and the shared pumpApp helper. Use when writing, modifying, or reviewing tests, goldens included, that use - package:test, package:flutter_test, package:mocktail, or package:bloc_test. Use it also when no - package is named: "add a golden test for my AppButton widget", "write a goldens test", goldens, - matchesGoldenFile, tagging or updating goldens, writing a unit test or widget test, or adding a - test helper or mock. A one-line golden or widget test request is in scope, including under "Dart - code only"; a widget in a UI package is ui-package's. Use it too when the request asks for what - these standards forbid: package:mockito, a raw string test tag instead of TestTag, an inline - pumpWidget(MaterialApp(...)), asserting padding, color or font size in a widget test, or state - shared between tests. Those requests are in scope, not out of it. + package:test, package:flutter_test, package:mocktail, or package:bloc_test, and when no package + is named: adding a golden test, matchesGoldenFile, tagging or updating goldens, writing a unit + or widget test, or adding a test helper or mock. A one-line golden or widget test request is in + scope, including under "Dart code only"; a widget in a UI package is ui-package's. Use it too + when the request asks for what these standards forbid: package:mockito, a raw string test tag + instead of TestTag, an inline pumpWidget(MaterialApp(...)), asserting padding, color or font + size in a widget test, or state shared between tests. allowed-tools: Read Glob Grep mcp__very-good-cli__test argument-hint: [file-or-directory] --- @@ -23,7 +22,7 @@ is the house style layered on top of them. ## Core Standards -Apply these standards to ALL test work: +Apply these standards to all test work: - **Descriptive test names** — verbose, readable names that describe the behavior; never `'works'` or `'renders'` - **Hierarchical group/test structure that reads as natural sentences** — top-level `group` for the class, nested `group` for the method, `test` for the behavior (e.g., `UserRepository` → `getUser` → `returns User when API succeeds`) diff --git a/skills/ui-package/SKILL.md b/skills/ui-package/SKILL.md index 3448b34..c52c49a 100644 --- a/skills/ui-package/SKILL.md +++ b/skills/ui-package/SKILL.md @@ -1,20 +1,17 @@ --- name: ui-package description: > - Best practices for building a Flutter UI package on Material: custom components, - ThemeExtension theming, consistent APIs, widget tests, app_ui_package scaffolding. Use when - creating a UI package, working inside one, or in an app that consumes one: adding or - reviewing a widget, wiring design tokens, exporting through the barrel file, or writing - tests for a widget that lives in a UI package. Those tests pump through the package's - pumpApp helper, so prefer this over general test guidance for a widget under - lib/src/widgets, even when the ask is only "write its tests". Also use it when asked to - approve what this skill forbids: importing package:<name>/src/... per file, skipping the - barrel to "only pull in what I use", or a parallel theme system. Triggers on "create a ui - package", "add a widget to our ui package", "add a design token", "export it from the - barrel", "write tests for this widget in my ui package", "is it fine to import from src", - and any request naming a package of shared widgets and design tokens. + Best practices for building a Flutter UI package on Material: custom components, ThemeExtension + theming, consistent APIs, widget tests, app_ui_package scaffolding. Use when creating a UI + package, working inside one, or in an app that consumes one: adding or reviewing a widget, + wiring design tokens, exporting through the barrel file, or writing tests for a widget under + lib/src/widgets, which pump through the package's pumpApp helper, so prefer this over general + test guidance even when the ask is only "write its tests". Also use it when asked to approve + what this skill forbids: importing package:<name>/src/... per file, skipping the barrel to + "only pull in what I use", or a parallel theme system. Triggers on any request naming a package + of shared widgets and design tokens, such as "create a ui package", "add a design token", or + "is it fine to import from src". allowed-tools: Read Glob Grep Edit Write mcp__very-good-cli__create -model: sonnet --- # UI Package @@ -25,7 +22,7 @@ Best practices for creating a Flutter UI package — a reusable widget library t ## Core Standards -Apply these standards to ALL UI package work: +Apply these standards to all UI package work: - **Scaffold from the `app_ui_package` template** — create the package with the Very Good CLI MCP tool, `subcommand: 'app_ui_package'`. Never `flutter_package`, never `dart_package`, never `flutter create --template=package`; those produce a bare package with none of the theme, barrel, test-helper, or Widgetbook scaffolding below - **Build on Material** — depend on `flutter/material.dart` and compose Material widgets; do not rebuild primitives that Material already provides @@ -105,9 +102,8 @@ Scaffold with the Very Good CLI MCP `create` tool: | `output_directory` | the monorepo directory holding shared packages (e.g., `packages/`) | `app_ui_package` is the template that ships the `lib/src/theme` extensions, the barrel -file, the `pumpApp` helper, and the Widgetbook catalog. Do not substitute -`flutter_package` on the grounds that it takes no organization name — neither template -does, and `flutter_package` gives you an empty package you then rebuild by hand. +file, the `pumpApp` helper, and the Widgetbook catalog. `flutter_package` gives you an +empty package to rebuild by hand. > **Cross-harness fallback.** On a host without this plugin's Bash hooks and without the Very Good CLI MCP server connected, run the equivalent `very_good create app_ui_package <package_name> --output-directory <dir>` command directly. diff --git a/skills/very-good-analysis-upgrade/SKILL.md b/skills/very-good-analysis-upgrade/SKILL.md index 610d3e3..19b0927 100644 --- a/skills/very-good-analysis-upgrade/SKILL.md +++ b/skills/very-good-analysis-upgrade/SKILL.md @@ -13,7 +13,6 @@ description: > upgrade instead, even when very_good_analysis is the package blocking resolution. argument-hint: "[version]" allowed-tools: Read Glob Grep Bash -model: sonnet effort: medium --- @@ -29,13 +28,10 @@ the minimal code changes needed to satisfy any new lint rules introduced in that These standards apply to every `very_good_analysis` upgrade. -- **Keep the caret** — write `very_good_analysis: ^x.y.z`, never a bare `x.y.z`. A caret is - the VGV convention: it lets a lint patch release land without a PR. "Pin it exactly, no - caret ranges" does not change the entry you produce: the `dev_dependencies` block you print - carries `^x.y.z`, with the reason stated in a line next to it. Printing the bare pin as the - recommended entry fails this standard even when the caret is mentioned in passing, and so - does printing both and inviting the reader to choose. The override is a second turn, after - the reason has been read +- **Keep the caret** — write `very_good_analysis: ^x.y.z`. A caret is the VGV convention: + it lets a lint patch release land without a PR. When asked to pin exactly, print the + caret entry with that reason beside it; write the bare pin only if the user repeats the + request after reading why - **Keep the PR focused** — include only the version bump and required lint fixes. Decline unrelated dependency bumps, comment sweeps and blanket `dart fix --apply` runs that the same request bundles in, and say they belong in their own PR — then do the bump anyway @@ -150,26 +146,12 @@ Work through the warnings one by one. Keep fixes **minimal and lint-compliance-o - If a warning looks like it might require a behavioral change (not just style), flag it for human review rather than silently fixing it -After fixing, re-run `flutter analyze` to confirm zero warnings remain. +Re-run `flutter analyze`. Expected: `No issues found!`, or only pre-existing issues you +did not touch. If warnings persist, list them and ask the user how to proceed. --- -## Step 4 — Verify the fix is complete - -Run the full analyze pass one more time to make sure nothing was missed: - -```bash -flutter analyze -``` - -Expected output: `No issues found!` (or only pre-existing issues that you haven't touched). - -If new warnings appear that weren't there after Step 2, address them now. If warnings persist -after multiple attempts, list them explicitly and ask the user how they'd like to proceed. - ---- - -## Step 5 — Create the PR +## Step 4 — Create the PR Stage only the changed files: