Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion plugins/mobile-apps/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Do not add preparation rewrites for `scheme`, `package`, `bundleIdentifier`, `sr
## Guiding Principles

1. **Connector-first for data** — All Power Platform data access goes through connectors and generated services in `src/generated/`. No direct Graph / Azure REST calls.
2. **Native code is allowlist-bounded; pure JavaScript is app-scoped.** Expo modules and packages that ship native source, a podspec, codegen configuration, an Expo module/config plugin, or platform projects must already exist in `template/package.json`. The rewrap binary is built from a pre-built base, so adding those packages to an app cannot add their native code. Do not classify a package from its name alone: a `react-native-*` package can still be pure JavaScript. For an explicit library request or an approved use case that benefits from an established library, the planner may select a compatible pure-JavaScript package, pin it in the app's `package.json`, and install it before builders use it; no Android/iOS rebuild is required. Do not bundle optional libraries such as `react-native-calendars` in the base template. Follow [`shared/references/javascript-dependency-planning.md`](shared/references/javascript-dependency-planning.md). `expo-haptics` remains runtime-banned even if it appears in a future template (see [`agents/screen-builder.md`](agents/screen-builder.md) HARD RULE). The native boundary and reconciliation rule are in [`skills/add-native/SKILL.md`](skills/add-native/SKILL.md).
2. **Native code is allowlist-bounded; pure JavaScript is app-scoped.** Expo modules and packages that ship native source, a podspec, codegen configuration, an Expo module/config plugin, or platform projects must already exist in `template/package.json`. The rewrap binary is built from a pre-built base, so adding those packages to an app cannot add their native code. Do not classify a package from its name alone: a `react-native-*` package can still be pure JavaScript. For an explicit library request or an approved use case that benefits from an established pure-JavaScript package, the planner may select a compatible version, pin it in the app's `package.json`, and install it before builders use it; no Android/iOS rebuild is required. Do not bundle optional libraries such as `react-native-calendars` in the base template. Follow [`shared/references/javascript-dependency-planning.md`](shared/references/javascript-dependency-planning.md). `expo-haptics` is template-shipped: add haptic behavior through `/add-native haptics`, keep native calls in `src/native/haptics.ts`, and pair tactile feedback with visible UI. When a template/base maintainer adds or changes a native dependency version, the Android and iOS base binaries must be rebuilt before release; generated apps based on that rebuilt template do not install or link it again. The native boundary and reconciliation rule are in [`skills/add-native/SKILL.md`](skills/add-native/SKILL.md).
3. **Fresh-template mode** — `/create-mobile-app` validates and prepares an existing fresh Expo standalone template working directory. Do not silently copy the bundled `template/` snapshot over the user's folder.
4. **Safety guardrails** — Confirm before deploys, before global installs, before edits outside the project root.
5. **Memory bank** — Persist `memory-bank.md` in the project root.
Expand Down
5 changes: 3 additions & 2 deletions plugins/mobile-apps/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,10 @@ For other capabilities (only those actually shipped by the template):
> /add-native secure-store # expo-secure-store wrapper
> /add-native file-system # expo-file-system wrapper
> /add-native sharing # expo-sharing wrapper
> /add-native haptics # expo-haptics impact, selection, and notification wrapper
```

Native modules are allowlist-bound by the current template `package.json`. If the relevant package is present and not runtime-banned, `/add-native` can use it through the proper wrapper or host control. If the package is absent, the skill does not install it or fake support; it adds a transparency note and stops for that capability. For example, push notifications require `expo-notifications`; if the template does not ship it, notifications cannot be added until the upstream template includes it.
Native modules are allowlist-bound by the current template `package.json`. If the relevant package is present, `/add-native` can use it through the proper wrapper or host control. If the package is absent, the skill does not install it or fake support; it adds a transparency note and stops for that capability. For example, push notifications require `expo-notifications`; if the template does not ship it, notifications cannot be added until the upstream template includes it.

### 4. Add a connector

Expand Down Expand Up @@ -249,7 +250,7 @@ Example edit flows:
| `/add-dataverse` | ✅ v0 | Add Dataverse — connect to existing tables, or create / extend tables in Tier 0 → N order via the Dataverse Web API, then generate TS services. Accepts ER diagrams via image / Mermaid / text, or spawns the data-model-architect agent. |
| `/setup-datamodel` | ✅ v0 | Discoverable alias for `/add-dataverse` optimized for the design-first entry point ("how do I plan my Dataverse schema?"). Same workflow under a more searchable name. |
| `/add-connector` | ✅ v0 | Generic connector — runs `npx power-apps add-data-source` for any first-party or custom connector |
| `/add-native` | ✅ v0 | Add a supported native capability/control (camera, image-picker, barcode/QR scanner, document-picker, PDF viewer/report, pen/signature, secure-store, file-system, sharing, etc.) — verifies the module already ships in the template and writes typed wrappers under `src/native/` without installing native packages or editing `app.config.js` |
| `/add-native` | ✅ v0 | Add a supported native capability/control (camera, image-picker, barcode/QR scanner, document-picker, PDF viewer/report, pen/signature, secure-store, file-system, sharing, haptics, etc.) — verifies the module already ships in the template and writes typed wrappers under `src/native/` without installing native packages or editing `app.config.js` |
| `/list-connections` | ✅ v0 | Finds or creates a Power Platform connection ID, or resolves a solution connection reference, for `npx power-apps add-data-source`. Use when adding non-Dataverse connectors or re-binding after a 401. |
| `/edit-app` | ✅ v0 | Post-generation app editor — updates affected sections of `native-app-plan.md`, applies Dataverse/native/design/connector changes, rebuilds affected screens, runs verification, updates `memory-bank.md`, and regenerates `preview.html` when UI changed. `--plan-only` preserves the old docs-only behavior. |
| `/deploy` | ✅ v0 | Build + push — `npm run build` then `npx power-apps push` to the env in `power.config.json`. **Does not** drive `expo run:ios` or `expo run:android` (out of scope for v0). |
Expand Down
4 changes: 3 additions & 1 deletion plugins/mobile-apps/agents/native-app-planner.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ Map each shipped module to a user-facing capability slug. Use this known mapping
| `video` | `expo-video` | `/add-native video` |
| `sensors` | `expo-sensors` | `/add-native sensors` |
| `screen-orientation` | `expo-screen-orientation` | `/add-native screen-orientation` |
| `haptics` | `expo-haptics` | `/add-native haptics` |
| `date-time-picker` | `@react-native-community/datetimepicker` | screen-builder form component rule |

Do not propose `native-pdf-viewer` or `pen-input` unless the exact extension package is present in the template allowlist output (`@microsoft/power-apps-native-pdf-viewer` and `@microsoft/power-apps-native-pen-input`). Do not propose `geolocation` unless `@microsoft/power-apps-native-bglocation` is present, and only for continuous/background tracking or durable Dataverse upload — use one-shot `location` (`expo-location`) for a single foreground coordinate read. When proposing `geolocation`, record that its Dataverse target table must already exist and must be verified by `/add-native geolocation` (default entity set `msdyn_locationrecords`, or a custom `tableName` whose `fieldMap` columns exist). Do not propose `pdf-report` unless `expo-print` is present. Do not propose local sharing for generated PDFs unless `expo-sharing` is present. If neither package path is present, drop the PDF capability and add a transparency note.
Expand All @@ -167,9 +168,10 @@ PDF/pen inference rules:
- `native-pdf-viewer` means opening an HTTPS PDF URL or local `file://` URI with `@microsoft/power-apps-native-pdf-viewer` 0.2.9+. It does not support `content://`, `blob:`, or `http://`.
- `pen-input` means signature/ink capture with `@microsoft/power-apps-native-pen-input`. It returns PNG data URI and needs a Dataverse Image/File/child-row target when persisted.
- `geolocation` means continuous/background GPS tracking with durable storage and inline Dataverse sync via `@microsoft/power-apps-native-bglocation`. Auth is MSAL-only; native uploads each fix to an existing Dataverse table (default entity set `msdyn_locationrecords`). It is distinct from one-shot `location` (`expo-location`). Plan it only for continuous tracking or durable upload, require `/add-native geolocation` to verify the target table exists before use, and never propose the `GeolocationExtension`/HostingSDK path.
- `haptics` means supplemental tactile feedback through `expo-haptics`. Record the interaction and feedback kind in the screen spec: impact (`light`, `medium`, `heavy`, `soft`, or `rigid`) for deliberate actions, selection for changed selections, or notification (`success`, `warning`, or `error`) after a completed outcome. Every haptic must accompany visible UI feedback.
- The Power Apps extensions are use-case-specific, not generic replacements for Expo modules. For other native needs, choose the relevant Expo module or dependency already present in `template/package.json` and still enforce the allowlist.

**Capabilities not present or runtime-banned** — do not propose: anything with required native code/config whose exact package is absent, `expo-notifications` unless a future template ships it, Bluetooth/NFC/BLE/AR without a shipped package, and `expo-haptics` unless the screen-builder hard rule is explicitly removed.
**Capabilities not present** — do not propose anything with required native code/config whose exact package is absent, `expo-notifications` unless a future template ships it, or Bluetooth/NFC/BLE/AR without a shipped package.

### Pure-JavaScript dependency handoff

Expand Down
6 changes: 3 additions & 3 deletions plugins/mobile-apps/agents/screen-builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ You will be invoked by `/create-mobile-app` Step 11 or `/edit-app` screen-rebuil

- **Dataverse image rendering rule (detail/list screens).** If a screen displays a Dataverse Image column, include the real image/base64 field in `select` and render a `data:image/<mime>;base64,...` URI when base64 is present. Do not rely on guessed URL/display pseudo-fields alone.

- **Native capabilities: use `src/native/` wrappers, NOT raw Expo modules.** `/add-native` creates typed wrappers under `src/native/` (e.g., `camera.ts`, `cameraUpload.ts`, `secureStore.ts`, `documentPicker.ts`, `pdfReport.ts`, `pdfViewer.ts`, `penInput.ts`, `geolocation.ts`). For non-Dataverse native workflows, always import from these wrappers — never import `expo-camera`, `expo-image-picker`, `expo-document-picker`, `expo-print`, `expo-secure-store`, `expo-file-system`, `expo-sharing`, `@microsoft/power-apps-native-pdf-viewer`, `@microsoft/power-apps-native-pen-input`, or `@microsoft/power-apps-native-bglocation` directly in screen files. The wrappers handle permissions, iOS/Android platform differences, URL validation, and return discriminated-union results (`{ ok: true, ... } | { ok: false, reason }`). If a wrapper doesn't exist yet, write the screen with the expected import path and a `// TODO(native-not-yet-added): run /add-native <capability> to create src/native/<wrapper>.ts` comment. For `camera.ts`, use `/add-native camera`; for `barcodeScanner.tsx`, use `/add-native barcode-scanner`; for `pdfReport.ts`, use `/add-native pdf-report`; for `pdfViewer.ts`, use `/add-native pdf-viewer` or `/add-native @microsoft/power-apps-native-pdf-viewer`; for `penInput.ts`, use `/add-native pen-input` or `/add-native @microsoft/power-apps-native-pen-input`; for `geolocation.ts`, use `/add-native geolocation` or `/add-native @microsoft/power-apps-native-bglocation`. **`expo-notifications` and `expo-haptics` are NOT available** — per AGENTS.md §2 and the HARD RULE below for haptics. If the plan tells you to use an unavailable capability, return `NEEDS_CONTEXT` — do not import it.
- **Native capabilities use `src/native/` wrappers.** `/add-native` creates typed wrappers under `src/native/` (e.g., `camera.ts`, `cameraUpload.ts`, `secureStore.ts`, `documentPicker.ts`, `pdfReport.ts`, `pdfViewer.ts`, `penInput.ts`, `geolocation.ts`, `haptics.ts`). For non-Dataverse native workflows, screen files import these wrappers; the wrapper implementation owns imports from `expo-camera`, `expo-image-picker`, `expo-document-picker`, `expo-print`, `expo-secure-store`, `expo-file-system`, `expo-sharing`, `expo-haptics`, `@microsoft/power-apps-native-pdf-viewer`, `@microsoft/power-apps-native-pen-input`, or `@microsoft/power-apps-native-bglocation`. The wrappers handle permissions, iOS/Android platform differences, URL validation, and return discriminated-union results (`{ ok: true, ... } | { ok: false, reason }`). If a wrapper doesn't exist yet, write the screen with the expected import path and a `// TODO(native-not-yet-added): run /add-native <capability> to create src/native/<wrapper>.ts` comment. For `camera.ts`, use `/add-native camera`; for `barcodeScanner.tsx`, use `/add-native barcode-scanner`; for `pdfReport.ts`, use `/add-native pdf-report`; for `pdfViewer.ts`, use `/add-native pdf-viewer` or `/add-native @microsoft/power-apps-native-pdf-viewer`; for `penInput.ts`, use `/add-native pen-input` or `/add-native @microsoft/power-apps-native-pen-input`; for `geolocation.ts`, use `/add-native geolocation` or `/add-native @microsoft/power-apps-native-bglocation`; for `haptics.ts`, use `/add-native haptics`. `expo-notifications` remains unavailable. If the plan tells you to use an unavailable capability, return `NEEDS_CONTEXT`.

- **Planned pure-JavaScript dependencies may be imported directly.** Follow `${PLUGIN_ROOT}/shared/references/javascript-dependency-planning.md`'s Builder Contract. Before importing any non-template package, verify it appears with an exact version in `## Screens → ### JavaScript Dependencies` and in the project's `package.json` `dependencies`, and that `require.resolve('<package>', { paths: [working_dir] })` succeeds. If the plan lists it but installation is missing, return `BLOCKED [<screen_name>]: approved JavaScript dependency <package>@<version> is not installed — orchestrator must install dependencies before builders run.` If it is not in the approved table, return `NEEDS_CONTEXT` instead of adding it yourself. Builders never select packages, edit `package.json`, or run installs. Import and use only the package APIs named by the approved per-screen spec.

Expand Down Expand Up @@ -245,7 +245,7 @@ You will be invoked by `/create-mobile-app` Step 11 or `/edit-app` screen-rebuil

- **Camera evidence flows MUST expose a visible `Take picture` action.** If the spec, route name, or table purpose includes camera evidence, photo evidence, proof photos, scan evidence, inspection photos, or any custom camera capture/upload flow, the screen must show a first-class button labeled `Take picture` (or a domain-specific equivalent beginning with `Take`, such as `Take evidence photo`) that calls `takePhoto()` from `src/native/camera`. Do not hide camera capture behind gallery-only upload, a file picker, an overflow menu, or a detail-only affordance. Gallery/upload can be a secondary sibling action, but camera capture must be visible in the main content or bottom action bar. After capture, if the photo is written to Dataverse, follow the create-then-navigate/upload rule: pre-generate any needed parent/evidence ID with `newId()`, check every write/upload result, and keep the user on the screen with inline error UI if capture/upload fails.

- **HARD RULE — NEVER use `expo-haptics` in any generated screen.** Even though the package may appear in `package.json`, it crashes at runtime in the current rewrap binary (the native module isn't bundled into the binary the customer's app loads — `Haptics.notificationAsync()` / `Haptics.impactAsync()` throw on first call). Visual-only feedback for all interactions: button press uses `pressStyle={{ scale: 0.98 }}`, success uses a green pill / banner / snackbar, error uses inline error text + retry button, toggle uses Switch's visible state change. NEVER write `import * as Haptics from 'expo-haptics'` — it WILL crash the running app, and the screen-builder hooks will block the write anyway.
- **Haptics usage.** When the screen spec includes haptics, import `triggerImpact`, `triggerNotification`, or `triggerSelection` from `src/native/haptics`. Use impact feedback for deliberate presses, selection feedback when a selected value changes, and notification feedback only after a success, warning, or error outcome is known. Inspect the wrapper result and log a non-OK result without turning an otherwise successful operation into a failure. Haptics supplement visible state; buttons, banners, inline errors, and selected states must remain understandable when vibration is unavailable or disabled. Do not trigger haptics during render, loading loops, or repeated list events.
- **React Query (`@tanstack/react-query`) is the default for server state.** The template ships it (`package.json`), and current `@microsoft/power-apps-native-host` wraps the app with `QueryClientProvider` inside `PowerAppsProvider`. **Never re-wire the provider from a screen file** and never construct a new `QueryClient` inside a component. Use `useQuery` for reads, `useMutation` for writes (create / update / delete), `useInfiniteQuery` for cursor-paginated lists, and `useQueryClient()` for invalidation. The raw `useEffect` + `useState` skeleton in Step 3 is only a fallback for screens where the spec explicitly excludes React Query (rare). Required usage rules:
- **Query keys are arrays starting with the entity, then params.** Examples: `['inspections']`, `['inspections', { status: 'open' }]`, `['inspection', id]`. Consistent shape lets `queryClient.invalidateQueries({ queryKey: ['inspections'] })` from a sibling mutation invalidate every variant in one line.
- **After every successful mutation, invalidate the affected query keys.** Pattern:
Expand All @@ -259,7 +259,7 @@ You will be invoked by `/create-mobile-app` Step 11 or `/edit-app` screen-rebuil
});
```
Without invalidation, the parent list shows stale data after the user pops back. This is the most common React Query bug in this plugin's generated screens.
- **Loading button rule (#38) consumes `isPending`.** A submit button on a `useMutation` flow MUST disable while `isPending`, label-swap, and trigger any visible success state inside `onSuccess` (green pill / banner / snackbar). Never haptic — see hard rule above.
- **Loading button rule (#38) consumes `isPending`.** A submit button on a `useMutation` flow MUST disable while `isPending`, label-swap, and trigger visible success state inside `onSuccess` (green pill / banner / snackbar). When the approved screen spec includes haptics, call `triggerNotification('success')` after setting that visible success state and handle its result as described above.
- **Focus refetch is handled by React Query** — see the `useFocusEffect` rule below for the exception. Do NOT pair `useQuery` with `useFocusEffect`; pick one.
- **Refresh on focus, never on mount.** Data loading on a screen MUST use `useFocusEffect` from `expo-router` wrapped in `React.useCallback` — NEVER `useEffect` with `[]`/`[loadX]` dependencies for the initial fetch on a list, detail, or any screen the user can navigate back to. `useEffect` only runs on mount; React Navigation keeps screens mounted when a child is pushed, so popping back to the list never re-fetches and the user sees stale data after a create/update/delete in a child screen. `useFocusEffect` re-runs every time the screen regains focus (mount AND re-focus), which is what users expect.
- **Required import:** `import { useFocusEffect } from 'expo-router';` — not `@react-navigation/native`.
Expand Down
Loading
Loading