Skip to content

feat(ui): add ios-widgets skill for MetaMask Mobile - #110

Open
tommasini wants to merge 2 commits into
mainfrom
tommasini/add-ios-widgets-skill
Open

feat(ui): add ios-widgets skill for MetaMask Mobile#110
tommasini wants to merge 2 commits into
mainfrom
tommasini/add-ios-widgets-skill

Conversation

@tommasini

Copy link
Copy Markdown

Description

Adds ui/ios-widgets, a skill for building and reviewing iOS home screen widgets and Live Activities in metamask-mobile, on the expo-widgets + @expo/ui foundation in app/core/Widgets/ and ios/ExpoWidgetsTarget/.

It promotes metamask-mobile's repo-local .cursor/rules/widget-development.mdc into the shared set, so the guidance installs across Claude, Cursor, and Codex instead of Cursor only, and is versioned alongside the rest of the mobile skills. A companion PR removes the local rule from the mobile repo.

Why this is generation-time work a linter cannot do. A widget layout does not run in the app. babel-preset-expo replaces any function whose body starts with 'widget'; with a string literal of its own source, ships that string to a separate iOS app extension, and evals it in an embedded JavaScriptCore VM that shares no memory, imports, or module state with the React Native runtime. The only channel between the two processes is a serialized props object in a shared App Group container.

Every mistake that follows from misunderstanding this type-checks, lints, and passes Jest:

  • Referencing an import or module constant inside the layout is a ReferenceError on device only.
  • Importing expo-widgets from a file without an .ios. extension crashes the Android app at startup.
  • Pairing an .ios.tsx implementation with a .ts fallback makes the no-op silently shadow the real widget on iOS — and Jest's resolver does not reproduce it, so the suite stays green while the app is broken.

There is no lint rule for any of this today. Some of it (the import and extension-parity rules) is mechanically checkable and would pair well with one later; the closure rule is the part that most wants generation-time guidance.

Type of Change

  • New skill

Skill Details

Provider Name: MetaMask Mobile (platform / widgets foundation)
Skill Name: ios-widgets (domains/ui/skills/ios-widgets, installs as mms-ios-widgets)
Brief Description: Build and review iOS home screen widgets and Live Activities in MetaMask Mobile — the 'widget'-directive sandbox, .ios.tsx/.tsx platform split, WidgetTheme, WidgetUpdaterService data flow, Live Activity lifecycle, testing, and troubleshooting.

Layout

domains/ui/skills/ios-widgets/
  skill.md                        two-process model, hard rules, platform split,
                                  props design, theming, router, review checklist
  references/adding-a-widget.md   10-step JS + Swift + Xcode checklist
  references/live-activities.md   the delta vs a widget, layout regions, lifecycle,
                                  orphaned-activity trap
  references/testing.md           what is assertable at which layer, Jest mocks
  references/troubleshooting.md   symptom to cause
  repos/metamask-mobile.md        file map, MM_WIDGETS_ENABLED, adoption analytics,
                                  Flask + provisioning limitations

skill.md holds the invariants and routes to one reference per task; the references are only opened when the task calls for them. repos/metamask-mobile.md gates installation — expo-widgets is iOS-only, so the skill is skipped for metamask-extension and core — and holds the repo state most likely to drift (paths, flag defaults, current limitations).

Improvements over the source rule

  • Trigger cues in the description. The Cursor rule was glob-scoped to app/core/Widgets/**; a skill is selected on description, so the cues now include the symptoms (stale data, device-only ReferenceError, Android import crash, widget missing from the gallery) as well as the paths.
  • Leads with the two-process model. The rule listed prohibitions; the skill states the one fact they all derive from, so an agent can reason about a case the checklist does not name.
  • Troubleshooting section, which the rule omitted entirely. Symptom-first, which is how an agent arrives at this problem.
  • A "designing props" section. Pre-format, pre-translate, pre-mask, and pass semantic flags rather than resolved colors — previously scattered across the long-form doc and only stated for Live Activities.
  • Corrects an over-broad prohibition. The rule rejected all explicit .ios-suffixed value imports; that pattern is in fact correct and in use from a file that is itself .ios-only (see BalanceWidget.ios.tsx). The skill qualifies it.
  • Widget-vs-Live-Activity split into separate references, so neither task pays for the other's checklist.

Testing

  • node .github/scripts/lint-skill-entry.mjs domains/ui/skills/ios-widgets/... — 0 errors, 0 warnings, including both recommended sections.
  • yarn audit:skills — 49 skills, 0 errors; no new warnings.
  • yarn test — 61/61 pass.
  • yarn lint (changelog) — clean.
  • tools/install --repo metamask-mobile --target <scratch> — writes mms-ios-widgets to .claude/skills/, .cursor/rules/, and .agents/skills/, with all four references mirrored and the metamask-mobile overlay appended to the body.
  • tools/install --repo metamask-extension|core --domain ui --dry-runskipped (no overlay for …) for both, as intended.
  • Description is 651 / 1536 characters.

Checklist

  • I have read the CONTRIBUTING.md guidelines
  • My skill follows the SKILL_TEMPLATE.md format
  • I have tested this skill with an AI agent
  • My skill does not contain any secrets, private keys, or sensitive data
  • I have added appropriate documentation
  • My changes don't break existing skills

Additional Context

The long-form human guide stays in the mobile repo at docs/widgets/README.md (architecture, rationale, possibilities, limitations); this skill is the condensed, actionable version and points at it.

Domain choice: ui, next to ui-development and component-scaffold — a widget is a UI surface, just one whose defining constraint is that the design-system runtime is unavailable to it. Happy to move it if reviewers would rather it sit under coding.

Made with Cursor

tommasini and others added 2 commits August 4, 2026 23:02
Widget layout code runs in a separate iOS extension process inside a
JavaScriptCore VM, reached only by a serialized props object. Every rule
in this skill follows from that, and every one of the resulting mistakes
type-checks, lints, and passes Jest — they only surface at runtime on
device, or as an Android startup crash.

Promotes metamask-mobile's repo-local .cursor/rules/widget-development.mdc
into a shared skill so it installs across Claude, Cursor, and Codex, and
so the guidance is versioned alongside the rest of the mobile skill set.
Gated to metamask-mobile via a repos/ overlay; expo-widgets is iOS-only.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
tommasini added a commit to MetaMask/metamask-mobile that referenced this pull request Aug 4, 2026
The Cursor rule only reached Cursor, and lived a repo apart from the rest
of the mobile agent guidance. It is now `mms-ios-widgets` in
MetaMask/skills, which installs for Claude, Cursor, and Codex via
`yarn skills`, splits the widget and Live Activity checklists into
separate references, and adds the troubleshooting section the rule never
carried.

Source: MetaMask/skills#110
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant