add-native
Skill1.1k starsChanged 10 days agoMITView on GitHub
Public entry point for native device capabilities and native controls — camera, image picker, barcode/QR scanner, document picker, file picker, secure storage, file system, sharing, haptic feedback, PDF generation/viewing, pen/signature capture, background GPS/geolocation tracking, or supported local file workflows — in a Power Apps mobile app. Also owns routing to internal camera/PDF/pen/geolocation implementation helpers and the guidance boundary between native wrappers and Dataverse File/Image host controls.
We split this skill into the rules it gives an agent
22 rules. About 0 are common, 6 are distinctive, and 16 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈6
Distinctive · estimate
This author's own choices. Worth a look.
16
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 13
Working directory: before any project read or command, execute native-artifact-compatibility.md Step 0.
- Distinctive: line 20
Entry routing: use the shared App feature entry points preflight before the workflow below.
- Very specific: line 130
Use host ImagePicker / FilePicker with generated upload(...) for persistence and downloadFile(...) / downloadImage(...) for read/view.
- Very specific: line 132
Do not use raw expo-document-picker, expo-image-picker, expo-file-system, or expo-sharing to build custom UI for Dataverse File/Image …
- Very specific: line 134
Use /add-native wrappers only when the workflow is not a Dataverse File/Image column, for example importing a local PDF for offline …
- Very specific: line 169
Do not treat every PDF request as document-picker.
- Very specific: line 170
Use document-picker when the user wants to pick, import, or upload a local PDF/document.
- Very specific: line 171
Use pdf-report when the app generates a PDF from records, evidence, inspection data, certificates, receipts, or reports, but only if …
- Very specific: line 172
Use native-pdf-viewer / pdf-control when the app opens/previews an HTTPS PDF URL or local file:// URI with …
- Very specific: line 174
Use pen-input only for signatures, drawn approvals, ink notes, sketches, and handwritten sign-off with …
- Distinctive: line 184
Never put File column bytes in the create/update JSON body.
- Distinctive: line 191
Verify project → 2. Resolve capability → 3.
- Very specific: line 210
When the user asks for "location" or "GPS", disambiguate by intent: continuous/background tracking or durable Dataverse upload → …
- Very specific: line 212
If the user names something not in the supported table, apply the Native capability gate: resolve the relevant package from package.json, …
- Very specific: line 232
INTERNAL_HELPER: read the printed helper file and execute its workflow with the same absolute working_dir, arguments, supplied answers, …
- Very specific: line 253
If the check fails, STOP. Do not run npx expo install.
- Distinctive: line 262
Read and execute native-artifact-compatibility.md Steps 1–3 for the inline capability row before writing or reusing output.
- Very specific: line 264
Create src/native/<wrapper-filename>.ts only when missing and in scope.
- Very specific: line 277
All wrappers return a discriminated-union result ({ ok: true, ...
- Distinctive: line 351
Keep visual feedback as the primary signal.
- Very specific: line 363
No permission request or app.config.js change is required for expo-haptics.
- Very specific: line 423
When a template/base maintainer first adds expo-haptics or changes its version, that release must rebuild the Android and iOS base binaries …
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Plugin check: Run
node "${PLUGIN_ROOT}/scripts/check-version.js"- if it outputs a message, show it to the user before proceeding.
📋 Shared instructions: shared-instructions.md — read first.
Working directory: before any project read or command, execute
native-artifact-compatibility.md Step 0.
Resolve --working-dir / owner context there; use the resolved absolute root for
every shell call and file tool, not a previous shell's cwd.
Add Native Capability
Entry routing: use the shared App feature entry points preflight before the workflow below.
The leaf generates typed wrappers under src/native/ for native capabilities that
the upstream template already ships. Screens import the wrapper instead of touching
Expo modules directly, so the discriminated-union result contract stays consistent.
Hard rules — do NOT cross these lines
- Never run
npx expo install,npm install, oryarn addfor a native module. The set of native modules inpackage.jsonis fixed by the upstream template. Adding a new one breaks the rewrap pipeline (the customer's binary is built from a pre-built base, not from theirpackage.json). - Never edit
app.config.js— plugins,ios.infoPlist,android.permissions, or anything else. All native config the template ships is intentional and signed off; arbitrary additions cannot be honored at rewrap time. - Never edit
package.jsondependenciesfor native modules. Native means the package ships platform source/projects, a podspec, codegen, an Expo module/config plugin, orreact-native.config.js; a package-name prefix alone is not proof. Pure-JavaScript dependencies are out of scope for/add-nativeand are installed from an approvedJavaScript Dependenciesplan by/create-mobile-appor/edit-app. - If the requested module isn't actually present in
package.json— STOP. That means the upstream template hasn't shipped it yet; do not work around by installing it.
Routing — /add-native is the public entry point
Some capabilities have a dedicated implementation helper that does more than a plain wrapper (camera writes scanner/upload helpers; PDF report/viewer helpers enforce local-vs-HTTPS boundaries; pen has native-control-specific validation). Users should still call /add-native <capability> for native controls. When a dedicated implementation exists, run it internally and do not ask the user to run that helper directly.
Lookup convention: after normalizing the capability, first check the internal-helper map below. For camera, image-picker, barcode-scanner, and qr-scanner, read and execute ${PLUGIN_ROOT}/skills/add-native/add-camera/SKILL.md inside this /add-native invocation. For pdf-report, read and execute ${PLUGIN_ROOT}/skills/add-native/add-pdf-report/SKILL.md. For pdf-viewer, read and execute ${PLUGIN_ROOT}/skills/add-native/add-pdf-viewer/SKILL.md. For pen-input, read and execute ${PLUGIN_ROOT}/skills/add-native/add-pen-input/SKILL.md. For geolocation, read and execute ${PLUGIN_ROOT}/skills/add-native/add-geolocation/SKILL.md. If no helper exists, fall through to this skill's inline wrapper flow.
Current dedicated implementations:
| Capability | Dedicated skill | Why dedicated |
camera, take-photo, photo, expo-camera, image-picker, gallery, expo-image-picker, barcode-scanner, qr-scanner, scanner | add-camera internal helper | Owns photo capture, gallery image picking, and barcode/QR scanner controls backed by expo-camera / expo-image-picker |
pdf-report, pdf-export, generate-pdf, print-report, evidence-packet | add-pdf-report internal helper | Generates app-owned local PDFs with expo-print and shares them only when expo-sharing is present |
pdf-viewer, native-pdf-viewer, pdf-control, open-pdf, @microsoft/power-apps-native-pdf-viewer | add-pdf-viewer internal helper | Enforces https:// / file:// viewer inputs and native viewer result handling |
pen-input, signature, ink, draw, @microsoft/power-apps-native-pen-input | add-pen-input internal helper | Captures PNG data URI and documents Dataverse Image/File persistence |
geolocation, location-tracking, background-location, gps-tracking, geo-tracking, @microsoft/power-apps-native-bglocation | add-geolocation internal helper | Native background GPS tracking with durable storage and inline Dataverse sync; distinct from one-shot expo-location |
For every other capability listed below, this skill writes the wrapper directly.
Native capability gate
Before adding any native control or wrapper, apply every gate: classify the intent, resolve the exact package/control from the live package.json, confirm it is not runtime-banned, confirm the input/output/storage constraints, then use the matching route below. If any gate fails, the native functionality is not supported for this app version — do not install packages, edit native config, or create fake wrappers.
| User intent | Add/use | Required package or control | Do not use / fallback |
| Form field bound to a Dataverse File column | Host <FilePicker> in screen JSX | @microsoft/power-apps-native-host host control | Do not generate document-picker/file-system/sharing wrappers for this field |
| Form field bound to a Dataverse Image column | Host <ImagePicker> in screen JSX | @microsoft/power-apps-native-host host control | Do not use camera/image-picker wrappers for normal form-bound image fields |
| Dedicated photo/gallery/scanner workflow | /add-native camera, image-picker, or barcode-scanner | expo-image-picker for photo/gallery; expo-camera for scanner; both for combined requests | If packages are absent, stop with missing-package guidance |
| Pick/import/upload a user-selected PDF/document | /add-native document-picker, or host <FilePicker> for Dataverse File fields | expo-document-picker present, or host File control | Do not treat this as pdf-report or native PDF viewer |
| Generate/export/print an app-owned report PDF | /add-native pdf-report | expo-print present | If expo-print is absent, do not add PDF report capability |
| Share a generated local PDF | pdfReport.ts share helper | expo-sharing present | If expo-sharing is absent, do not render sharing UI |
| Open/preview an HTTPS or local file PDF | /add-native pdf-viewer | @microsoft/power-apps-native-pdf-viewer 0.2.9+ present and input is https:// or file:// | Do not pass content://, blob:, or http:// URIs to the viewer |
| Capture signature, ink, drawing, or sign-off | /add-native pen-input | @microsoft/power-apps-native-pen-input present | If persisted, plan Dataverse Image/File/child Evidence target first |
| Continuous/background GPS tracking with durable Dataverse upload | /add-native geolocation | @microsoft/power-apps-native-bglocation present | Do not use one-shot expo-location for background tracking; do not use the GeolocationExtension/HostingSDK path |
| Tactile feedback for presses, selections, and operation results | /add-native haptics | expo-haptics present | Use the typed wrapper and pair every haptic with visible UI feedback |
| Store generated PDF/signature artifact | Generated Dataverse services after parent row exists | File/Image column or child Evidence/Attachment table exists | Never put File bytes in create/update JSON |
| Native capability not listed in this table | Resolve from package.json, then add an inline wrapper only when the matching package is present and not runtime-banned | Exact relevant package present in package.json | If no relevant package exists, or the package is runtime-banned, add a transparency note and stop |
Handle multi-part requests row-by-row. Example: "capture signature and attach signed report PDF" requires pen-input, pdf-report, Dataverse artifact storage, and possibly sharing; do not add only the native capability while leaving storage or screen states undefined. The map is not closed: for new shipped packages, resolve by capability semantics, use the directly matching package when safe, and ask once only if multiple installed packages plausibly match.
File/Image Picker Ownership
This skill owns native file capability mechanics. Screen builders own only the final JSX placement.
There are two different cases:
| Case | Correct implementation | Owner |
Dataverse File column | Use <FilePicker> from @microsoft/power-apps-native-host | screen-builder JSX rule, documented here |
Dataverse Image column | Use <ImagePicker> from @microsoft/power-apps-native-host | screen-builder JSX rule, documented here |
| Local device document/file workflow not bound to a Dataverse column | Generate/use src/native/documentPicker.ts, src/native/fileSystem.ts, and/or src/native/sharing.ts wrappers | /add-native |
| App-generated PDF report workflow | /add-native routes internally to add-pdf-report; uses expo-print and optionally expo-sharing only if present | /add-native |
| Camera capture, gallery image selection, barcode scanner, or QR scanner workflow | /add-native routes internally to add-camera | /add-native |
Dataverse File/Image columns use host controls, not raw Expo modules and not app-specific native wrappers. When a form field binds to a Dataverse File column (FileAttributeMetadata), render <FilePicker> from @microsoft/power-apps-native-host. When it binds to a Dataverse Image column (ImageAttributeMetadata), render <ImagePicker> from @microsoft/power-apps-native-host. These controls read accent, surface, and text colors from PowerAppsProvider / ThemeProvider and produce Dataverse-compatible payloads.
import { FilePicker, ImagePicker } from '@microsoft/power-apps-native-host';
import type { PickedFileInfo, PickedImageInfo } from '@microsoft/power-apps-native-host';
// Image column — seed preview from Dataverse bytes/base64 and capture upload-ready payload.
<ImagePicker
label="Site Photo"
initialBase64={imageBase64}
onImageChange={(image: PickedImageInfo | null) => { /* store image for upload */ }}
/>
// File column — edit-only pick/replace.
<FilePicker
label="Specification Sheet"
fileName={record.cr123_specsheet_name}
onChange={(file: PickedFileInfo) => { /* store file for save */ }}
/>
// File column — view + replace.
<FilePicker
label="Specification Sheet"
fileName={record.cr123_specsheet_name}
onDownload={() => Cr123_inspectionService.downloadFile(id, 'cr123_specsheet')}
onChange={(file: PickedFileInfo) => { /* store file for save */ }}
/>Save/read pattern for host picker-bound Dataverse columns:
// FILE/IMAGE columns: do not include picker bytes in the PATCH body.
// Save normal fields first, then upload through generated service helpers.
await Cr123_inspectionService.update(id, { cr123_name: name.trim() });
await Cr123_inspectionService.upload(id, 'cr123_specsheet', file.file, file.name);
await Cr123_inspectionService.upload(id, 'cr123_sitephoto', image.file, image.name);
// Download helpers are used for read/view scenarios.
const fileBytes = await Cr123_inspectionService.downloadFile(id, 'cr123_specsheet');
const imageBytes = await Cr123_inspectionService.downloadImage(id, 'cr123_sitephoto');Use host ImagePicker / FilePicker with generated upload(...) for persistence and downloadFile(...) / downloadImage(...) for read/view. Do not use Service.update(...{ <imageColumn>: base64 }) for picker-driven image/file persistence.
Do not use raw expo-document-picker, expo-image-picker, expo-file-system, or expo-sharing to build custom UI for Dataverse File/Image columns. Host FilePicker and ImagePicker already handle permissions, reading, sharing/download affordances, and Dataverse-compatible payload shape.
Use /add-native wrappers only when the workflow is not a Dataverse File/Image column, for example importing a local PDF for offline parsing, exporting a generated report, saving a local draft packet, or opening a share sheet for an app-generated summary.
Supported capabilities
Apply the Native capability gate above. This table is a known capability-to-package map, not a guarantee that every listed package exists in every template version.
| Capability | Module | Wrapper to generate | Notes |
camera, take-photo, photo, expo-camera | expo-image-picker | src/native/camera.ts | /add-native routes internally to add-camera for the photo artifact; expo-camera is a capability alias, not its required package |
image-picker, gallery, expo-image-picker | expo-image-picker | src/native/camera.ts (pickImage) | /add-native routes internally to add-camera; preserve compatible existing re-exports |
barcode-scanner, qr-scanner, scanner, barcode, qr | expo-camera | src/native/barcodeScanner.tsx | /add-native routes internally to add-camera |
document-picker | expo-document-picker | src/native/documentPicker.ts | Picks/imports user-selected files (PDF, docs, etc.) from the device |
pdf-viewer, native-pdf-viewer, pdf-control, open-pdf, @microsoft/power-apps-native-pdf-viewer | @microsoft/power-apps-native-pdf-viewer | src/native/pdfViewer.ts | /add-native routes internally to add-pdf-viewer; 0.2.9+ opens HTTPS URLs and file URIs |
pdf-report, pdf-export, generate-pdf, print-report, evidence-packet | expo-print (+ optional expo-sharing) | src/native/pdfReport.ts | /add-native routes internally to add-pdf-report; generated local files are shared only when expo-sharing is present, or uploaded to Dataverse |
pen-input, signature, ink, draw, @microsoft/power-apps-native-pen-input | @microsoft/power-apps-native-pen-input | src/native/penInput.ts | /add-native routes internally to add-pen-input; captures PNG data URI |
geolocation, location-tracking, background-location, gps-tracking, geo-tracking, @microsoft/power-apps-native-bglocation | @microsoft/power-apps-native-bglocation | src/native/geolocation.ts | /add-native routes internally to add-geolocation; native background tracking + durable Dataverse sync. Distinct from one-shot location below |
secure-store | expo-secure-store | src/native/secureStore.ts | |
file-system | expo-file-system | src/native/fileSystem.ts | |
sharing | expo-sharing | src/native/sharing.ts | |
location | expo-location | src/native/location.ts | One-shot/foreground fix only. For continuous background tracking with Dataverse sync, use geolocation (@microsoft/power-apps-native-bglocation). Use only when the current template package contains expo-location |
biometrics, local-authentication | expo-local-authentication | src/native/biometrics.ts | Use only when the current template package contains expo-local-authentication |
clipboard | expo-clipboard | src/native/clipboard.ts | Use only when the current template package contains expo-clipboard |
mail-composer, email-draft | expo-mail-composer | src/native/mailComposer.ts | Opens native mail compose when the package is present; connectors still own server-side email sends |
audio | expo-audio | src/native/audio.ts | Use for audio recording/playback only when package is present |
video | expo-video | src/native/video.ts | Use for video playback only when package is present |
sensors | expo-sensors | src/native/sensors.ts | Use only for sensor APIs exposed by the installed package |
screen-orientation | expo-screen-orientation | src/native/screenOrientation.ts | Use only when package is present; do not edit native config |
haptics, vibration-feedback, impact-feedback, selection-feedback, notification-feedback | expo-haptics | src/native/haptics.ts | Impact, selection, and success/warning/error notification feedback |
device-info | expo-device / expo-application / expo-cellular | src/native/deviceInfo.ts | Read-only device/app/cellular metadata wrappers |
date-time-picker | @react-native-community/datetimepicker | screen-level component usage | Use directly in form screens per screen-builder rules; no /add-native wrapper required |
For custom workflows outside Dataverse File/Image form fields, use the image-picker capability via /add-native image-picker for user-selected photos and videos, or the document-picker capability via /add-native document-picker for documents and other files. These wrappers use scoped system-picker flows. Dataverse File/Image form fields remain the exception: use host <FilePicker> / <ImagePicker> controls as described above, not /add-native wrappers or raw Expo module imports.
PDF / pen routing rules
- Do not treat every PDF request as
document-picker. - Use
document-pickerwhen the user wants to pick, import, or upload a local PDF/document. This remains supported and should still be used for that use case. - Use
pdf-reportwhen the app generates a PDF from records, evidence, inspection data, certificates, receipts, or reports, but only ifexpo-printis present inpackage.json. - Use
native-pdf-viewer/pdf-controlwhen the app opens/previews an HTTPS PDF URL or localfile://URI with@microsoft/power-apps-native-pdf-viewer0.2.9+.content://,blob:, andhttp://URIs are unsupported. - If a request says "view/open PDF" but the Power Apps viewer package is absent, fall back to
pdf-reportonly when the app is generating its own report andexpo-printis present. Do not claim generic PDF viewing support throughexpo-print; it generates local files, it does not view arbitrary PDFs. - Use
pen-inputonly for signatures, drawn approvals, ink notes, sketches, and handwritten sign-off with@microsoft/power-apps-native-pen-input. - For other use cases, use the relevant Expo module or other dependency already present in
package.json; do not force the Power Apps extensions into unrelated flows. - For generated local PDFs from
expo-print, use native PDF viewer 0.2.9+ for open/preview,expo-sharingfor sharing, and Dataverse File storage for retention. Do not requireexpo-sharingmerely to preview a local PDF. - Host
FilePickerandImagePickerare still correct for user-selected Dataverse File/Image form fields. Generated PDFs and pen captures use native wrappers first, then Dataverse persistence helpers.
Dataverse artifact persistence rules
- Pen input returns a PNG data URI like
data:image/png;base64,.... - For Dataverse Image columns, normalize the data URI to the generated service payload shape. If raw base64 is required, strip the
data:image/png;base64,prefix. - For Dataverse File columns, convert the generated PDF or signature PNG into upload bytes/File, save or update the parent row first, then upload through the generated service helper.
- Never put File column bytes in the create/update JSON body. File bytes are uploaded only after the parent row ID exists.
- Screens must handle unsupported, cancelled, upload failed, and viewer failed states explicitly. Pen cancellation is a non-error result that screens can ignore.
Missing or gated packages: package.json plus the runtime gate is authoritative. If the relevant package/control is absent, stop with a transparency note.
Workflow
- Verify project → 2. Resolve capability → 3. Auto-route to dedicated skill if one exists → 4. Verify module is template-shipped → 5. Write wrapper → 6. Type-check → 7. Summary
Step 1 — Verify project
cd -- '<working_dir>' || { echo "BLOCKED: cannot enter working_dir" >&2; exit 1; }
test -f app.config.js && test -f power.config.json && test -f package.json || { echo "BLOCKED: working_dir is not an initialized app" >&2; exit 1; }Step 2 — Resolve capability
Telemetry checkpoint: resolve_native_capability
If $ARGUMENTS includes a capability name, package name, or control name, use it. Otherwise look for a ## Native Capabilities section in native-app-plan.md and present the planned capabilities for confirmation. If neither exists, prompt the user with the supported-capabilities list above plus any relevant installed package from package.json that directly matches their request.
Normalize the capability name to lowercase, hyphenated form (e.g., Camera → camera, ImagePicker → image-picker, SecureStore → secure-store). Also normalize aliases: take-photo / photo / camera-control / expo-camera → camera; gallery / pick-image / expo-image-picker → image-picker; scanner / barcode / qr → barcode-scanner; open-pdf / view-pdf / pdf-control / pdf-viewer-control / @microsoft/power-apps-native-pdf-viewer → pdf-viewer; native-pdf-viewer → pdf-viewer; generate-pdf / pdf-export → pdf-report; signature / sign-off / ink / draw / pen-control / @microsoft/power-apps-native-pen-input → pen-input; location-tracking / background-location / gps-tracking / geo-tracking / track-location / power-apps-native-bglocation / @microsoft/power-apps-native-bglocation → geolocation; vibration / vibration-feedback / impact-feedback / selection-feedback / notification-feedback / expo-haptics → haptics.
When the user asks for "location" or "GPS", disambiguate by intent: continuous/background tracking or durable Dataverse upload → geolocation (@microsoft/power-apps-native-bglocation); a single foreground coordinate read → location (expo-location). If the intent is unclear, ask once before routing.
If the user names something not in the supported table, apply the Native capability gate: resolve the relevant package from package.json, continue only when present and not runtime-banned, otherwise stop with a transparency note.
Step 3 — Route to nested helpers or inline wrappers
Telemetry checkpoint: dispatch_native_capability
For normalized camera, image-picker, barcode-scanner, qr-scanner, pdf-report, pdf-viewer, pen-input, or geolocation, do not fall through to the generic wrapper flow and do not tell the user to run another slash command. Read the nested helper and follow its steps inside this /add-native invocation:
cd -- '<working_dir>' || { echo "BLOCKED: cannot enter working_dir" >&2; exit 1; }
case "<capability>" in
camera|image-picker|barcode-scanner|qr-scanner) test -f "${PLUGIN_ROOT}/skills/add-native/add-camera/SKILL.md" && echo "INTERNAL_HELPER:add-camera" ;;
pdf-report) test -f "${PLUGIN_ROOT}/skills/add-native/add-pdf-report/SKILL.md" && echo "INTERNAL_HELPER:add-pdf-report" ;;
pdf-viewer) test -f "${PLUGIN_ROOT}/skills/add-native/add-pdf-viewer/SKILL.md" && echo "INTERNAL_HELPER:add-pdf-viewer" ;;
pen-input) test -f "${PLUGIN_ROOT}/skills/add-native/add-pen-input/SKILL.md" && echo "INTERNAL_HELPER:add-pen-input" ;;
geolocation) test -f "${PLUGIN_ROOT}/skills/add-native/add-geolocation/SKILL.md" && echo "INTERNAL_HELPER:add-geolocation" ;;
*) echo "INLINE" ;;
esac- INTERNAL_HELPER: read the printed helper file and execute its workflow with
the same absolute
working_dir, arguments, supplied answers, mode flags, and current owner/phase/approved_scope. The helper must bind every operation to that root using Step 0, even if launched from another directory. Each helper must execute native-artifact-compatibility.md before its writes/reuse and before its success summary; outer Step 5 is not reached on this branch. PropagateNEEDS_CONTEXT/BLOCKEDand the artifact result to the owner, then STOP this leaf, not the owning workflow. Do not fall through to inline generation or report success for a partial helper result./add-nativeremains the only user-facing command for these controls. - INLINE: continue to Step 4.
Step 4 — Verify module is template-shipped
Confirm the underlying native-capability package is actually present in the project's package.json (catches the case where the user hand-removed it or the template version is older than expected):
cd -- '<working_dir>' || { echo "BLOCKED: cannot enter working_dir" >&2; exit 1; }
node -e "const p = require('./package.json'); const m = '<expo-module-name>'; if (!p.dependencies?.[m]) { console.error('MISSING: ' + m + ' is not in package.json. The template should ship it. Re-scaffold via /create-mobile-app, restore it from upstream, or wait for the template release that adds it — this skill will not install it.'); process.exit(1); }"If the check fails, STOP. Do not run npx expo install. Print the error verbatim.
Step 5 — Write wrapper
Telemetry checkpoint: generate_native_wrapper
Print before starting:
"→ Writing src/native/
<wrapper>.ts (typed wrapper with discriminated-union result + iOS/Android platform guards)…"
Read and execute native-artifact-compatibility.md
Steps 1–3 for the inline capability row before writing or reusing output.
Create src/native/<wrapper-filename>.ts only when missing and in scope. If the
file already exists, inspect its exports against the approved capability
contract, including behavior and storage. Reuse it unchanged when compatible;
make a scoped update without re-asking when already approved. Otherwise return
NEEDS_CONTEXT to the owner (or ask standalone) before updating; never silently
skip the feature or overwrite custom code.
Each wrapper exports:
request<Capability>Permission(): Promise<boolean>— only when the underlying API has a permission model (location, biometrics, notifications). Skip for capabilities without one (secure-store, file-system, sharing, clipboard).- One or two domain methods returning typed results.
The contract screen-builder agents rely on:
- All wrappers return a discriminated-union result (
{ ok: true, ... } | { ok: false, reason, message? }) — never throw - Unsupported runtime/platform states gracefully degrade or return
{ ok: false, reason: 'unsupported' }— never crash - Branch by supported native platform when a capability differs between iOS and Android
- Screens import these wrappers only for non-Dataverse native workflows. Dataverse File/Image fields use
@microsoft/power-apps-native-hostcontrols from the File/Image Picker Ownership section above.
Haptics wrapper
For normalized capability haptics, generate src/native/haptics.ts with this contract:
import * as Haptics from 'expo-haptics';
import { Platform } from 'react-native';
export type HapticImpactStyle = 'light' | 'medium' | 'heavy' | 'soft' | 'rigid';
export type HapticNotificationType = 'success' | 'warning' | 'error';
export type HapticResult =
| { ok: true }
| { ok: false; reason: 'unsupported' | 'error'; message: string };
const impactStyles: Record<HapticImpactStyle, Haptics.ImpactFeedbackStyle> = {
light: Haptics.ImpactFeedbackStyle.Light,
medium: Haptics.ImpactFeedbackStyle.Medium,
heavy: Haptics.ImpactFeedbackStyle.Heavy,
soft: Haptics.ImpactFeedbackStyle.Soft,
rigid: Haptics.ImpactFeedbackStyle.Rigid,
};
const notificationTypes: Record<HapticNotificationType, Haptics.NotificationFeedbackType> = {
success: Haptics.NotificationFeedbackType.Success,
warning: Haptics.NotificationFeedbackType.Warning,
error: Haptics.NotificationFeedbackType.Error,
};
async function runHaptic(effect: () => Promise<void>): Promise<HapticResult> {
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
return {
ok: false,
reason: 'unsupported',
message: 'Haptic feedback is available only on iOS and Android.',
};
}
try {
await effect();
return { ok: true };
} catch (error) {
return {
ok: false,
reason: 'error',
message: error instanceof Error ? error.message : 'The native haptic call failed.',
};
}
}
export function triggerImpact(style: HapticImpactStyle = 'medium'): Promise<HapticResult> {
return runHaptic(() => Haptics.impactAsync(impactStyles[style]));
}
export function triggerNotification(
type: HapticNotificationType = 'success'
): Promise<HapticResult> {
return runHaptic(() => Haptics.notificationAsync(notificationTypes[type]));
}
export function triggerSelection(): Promise<HapticResult> {
return runHaptic(() => Haptics.selectionAsync());
}Screens call the wrapper only when haptics appear in the approved native-capabilities and per-screen plan. Choose feedback by semantics:
triggerImpact('light' | 'medium' | 'heavy' | 'soft' | 'rigid')for deliberate presses or physical-feeling actions.triggerSelection()when a picker, segmented control, or other selection changes.triggerNotification('success' | 'warning' | 'error')after the corresponding outcome is known.- Keep visual feedback as the primary signal. Inspect non-OK results and log them, but do not turn an otherwise successful business operation into a failure because tactile feedback is unavailable.
Screen usage after a successful operation:
setSaveState({ kind: 'success', message: 'Saved' });
const hapticResult = await triggerNotification('success');
if (!hapticResult.ok) {
console.warn('[haptics] success feedback unavailable', hapticResult);
}No permission request or app.config.js change is required for expo-haptics.
Other inline wrappers
Consult the module's published API docs (linked from its npm page) for method signatures and permission patterns. Use the secure-store skeleton below as the canonical example of the discriminated-union shape, then translate it to the target module's API.
Secure-store canonical skeleton:
// src/native/secureStore.ts
import * as SecureStore from 'expo-secure-store';
import { Platform } from 'react-native';
export type SecureResult<T> =
| { ok: true; value: T }
| { ok: false; reason: 'unsupported' | 'not-found' | 'error'; message?: string };
export async function getSecret(key: string): Promise<SecureResult<string>> {
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
return { ok: false, reason: 'unsupported', message: 'SecureStore is not available on this platform.' };
}
try {
const value = await SecureStore.getItemAsync(key);
if (value === null) return { ok: false, reason: 'not-found' };
return { ok: true, value };
} catch (e: any) {
return { ok: false, reason: 'error', message: e?.message };
}
}
export async function setSecret(key: string, value: string): Promise<SecureResult<true>> {
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
return { ok: false, reason: 'unsupported', message: 'SecureStore is not available on this platform.' };
}
try {
await SecureStore.setItemAsync(key, value);
return { ok: true, value: true };
} catch (e: any) {
return { ok: false, reason: 'error', message: e?.message };
}
}Step 6 — Type-check
Telemetry checkpoint: validate_native_wrapper
Print before starting:
"→ Running tsc to verify wrapper compiles (~10–20 seconds)."
cd -- '<working_dir>' || { echo "BLOCKED: cannot enter working_dir" >&2; exit 1; }
npx --no-install tsc --noEmitFix only in-scope wrapper-side errors. Execute the shared compatibility contract's
Step 4 after type-checking: recheck all requested artifacts, API/behavior, and
storage obligations before returning success. Type-check success alone is
insufficient. Do not run platform-specific native build commands during normal
/add-native use because the module is already part of the current template binary.
When a template/base maintainer first adds expo-haptics or changes its version,
that release must rebuild the Android and iOS base binaries so Expo autolinking
includes the module.
Step 7 — Summary
Return the shared compatibility result before the summary; use the actual created/updated/reused paths and do not claim completion for unresolved artifacts.
✅ Native wrapper generated: <capability>
─────────────────────────────────────────────
Module (template-shipped) : <expo-module>@<version-from-package.json>
Wrapper created : src/native/<capability>.ts
package.json : unchanged ✓
app.config.js : unchanged ✓
Type-check: PASS
Sample usage:
import { takePhoto } from '../../src/native/camera';
const result = await takePhoto();
if (result.ok) {
setPhotoUri(result.uri);
} else if (result.reason === 'permission-denied') {
showToast('Camera permission required');
}
⚠️ No per-app native rebuild required when using a binary produced from the current
template. The underlying native module is already linked; wrapper edits are picked
up by Metro. Template/base dependency changes require a new native base build.
─────────────────────────────────────────────Notes
- This skill never modifies
package.json,app.config.js,src/playerConfig.ts,src/generated/, or any screen file. - For capabilities not in the supported table (
expo-notifications, Bluetooth, NFC, BLE, AR — until the template adds them), tell the user the template doesn't ship them yet — file a request at the upstream template repo. Do NOT attempt to install or configure anything yourself. - Pure-JavaScript libraries are out of scope for this skill.
/create-mobile-appor/edit-appselects and installs them throughshared/references/javascript-dependency-planning.md; no native wrapper or Android/iOS rebuild is needed. The prohibition above applies only to packages with native source/config.
Trending
Steady: 0 new projects took it in the last 4 weeks, none the 4 weeks before.thin data
Who's talking about microsoft/power-platform-skills
- geekfujiwara/CodeAppsDevelopmentStandard“[sdk-update] 自動検知: Power Platform SDK / 公式 skills 更新 (2026-09-06)”GitHub · Sep 6, 2026 · 1 reactions
- geekfujiwara/CodeAppsDevelopmentStandard“[sdk-update] 自動検知: Power Platform SDK / 公式 skills 更新 (2026-08-23)”GitHub · Aug 23, 2026 · 1 reactions
- sudosubin/agents.nix“agent-plugins.github.microsoft.power-platform-skills: 0-unstable-2026-10-28 → 0-unstable-2026-10-29”GitHub · Oct 30, 2026
- sudosubin/agents.nix“claude-code-plugins.github.microsoft.power-platform-skills: 0-unstable-2026-10-28 → 0-unstable-2026-10-29”GitHub · Oct 30, 2026
- sudosubin/agents.nix“agent-skills.github.microsoft.power-platform-skills: 0-unstable-2026-10-28 → 0-unstable-2026-10-29”GitHub · Oct 30, 2026
The biggest 5 of 50 mentions linking to the repository that our crawl found. Each line is their own headline.
See all 50
Talked about on GitHub (50 times).
On GitHub · 50
- Sep 6, 2026[sdk-update] 自動検知: Power Platform SDK / 公式 skills 更新 (2026-09-06) — geekfujiwara/CodeAppsDevelopmentStandard1 reaction
- Aug 23, 2026[sdk-update] 自動検知: Power Platform SDK / 公式 skills 更新 (2026-08-23) — geekfujiwara/CodeAppsDevelopmentStandard1 reaction · 2 comments
- Oct 30, 2026agent-plugins.github.microsoft.power-platform-skills: 0-unstable-2026-10-28 → 0-unstable-2026-10-29 — sudosubin/agents.nix0 reactions
- Oct 30, 2026claude-code-plugins.github.microsoft.power-platform-skills: 0-unstable-2026-10-28 → 0-unstable-2026-10-29 — sudosubin/agents.nix0 reactions
- Oct 30, 2026agent-skills.github.microsoft.power-platform-skills: 0-unstable-2026-10-28 → 0-unstable-2026-10-29 — sudosubin/agents.nix0 reactions
45 more on GitHub
- Oct 30, 2026claude-code-marketplaces.github.microsoft.power-platform-skills: 0-unstable-2026-10-28 → 0-unstable-2026-10-29 — sudosubin/agents.nix0 reactions
- Oct 29, 2026copilot-plugins.github.microsoft.power-platform-skills: 0-unstable-2026-10-28 → 0-unstable-2026-10-29 — sudosubin/agents.nix0 reactions
- Oct 29, 2026agent-skills.github.microsoft.power-platform-skills: 0a7bf47 → c159268 — sudosubin/agents.nix0 reactions
- Oct 29, 2026copilot-plugins.github.microsoft.power-platform-skills: 0a7bf47 → c159268 — sudosubin/agents.nix0 reactions
- Oct 28, 2026claude-code-plugins.github.microsoft.power-platform-skills: 0a7bf47 → c159268 — sudosubin/agents.nix0 reactions
- Oct 28, 2026agent-plugins.github.microsoft.power-platform-skills: 0a7bf47 → c159268 — sudosubin/agents.nix0 reactions
- Oct 28, 2026claude-code-marketplaces.github.microsoft.power-platform-skills: 0a7bf47 → c159268 — sudosubin/agents.nix0 reactions
- Oct 28, 2026agent-skills.github.microsoft.power-platform-skills: 0-unstable-2026-10-27 → 0-unstable-2026-10-28 — sudosubin/agents.nix0 reactions
- Oct 28, 2026copilot-plugins.github.microsoft.power-platform-skills: 0-unstable-2026-10-27 → 0-unstable-2026-10-28 — sudosubin/agents.nix0 reactions
- Oct 28, 2026claude-code-plugins.github.microsoft.power-platform-skills: 0-unstable-2026-10-27 → 0-unstable-2026-10-28 — sudosubin/agents.nix0 reactions
- Oct 28, 2026agent-plugins.github.microsoft.power-platform-skills: 0-unstable-2026-10-27 → 0-unstable-2026-10-28 — sudosubin/agents.nix0 reactions
- Oct 28, 2026claude-code-marketplaces.github.microsoft.power-platform-skills: init at 0-unstable-2026-10-28 — sudosubin/agents.nix0 reactions
- Oct 27, 2026agent-skills.github.microsoft.power-platform-skills: 696cac9 → f39b3bb — sudosubin/agents.nix0 reactions
- Oct 27, 2026agent-plugins.github.microsoft.power-platform-skills: 696cac9 → f39b3bb — sudosubin/agents.nix0 reactions
- Oct 27, 2026claude-code-plugins.github.microsoft.power-platform-skills: 696cac9 → f39b3bb — sudosubin/agents.nix0 reactions
- Oct 27, 2026copilot-plugins.github.microsoft.power-platform-skills: 696cac9 → f39b3bb — sudosubin/agents.nix0 reactions
- Oct 27, 2026agent-skills.github.microsoft.power-platform-skills: 0-unstable-2026-10-26 → 0-unstable-2026-10-27 — sudosubin/agents.nix0 reactions
- Oct 27, 2026claude-code-plugins.github.microsoft.power-platform-skills: 0-unstable-2026-10-26 → 0-unstable-2026-10-27 — sudosubin/agents.nix0 reactions
- Oct 27, 2026agent-plugins.github.microsoft.power-platform-skills: 0-unstable-2026-10-26 → 0-unstable-2026-10-27 — sudosubin/agents.nix0 reactions
- Oct 27, 2026copilot-plugins.github.microsoft.power-platform-skills: 0-unstable-2026-10-26 → 0-unstable-2026-10-27 — sudosubin/agents.nix0 reactions
- Oct 26, 2026agent-plugins.github.microsoft.power-platform-skills: ea1e2e7 → a887c53 — sudosubin/agents.nix0 reactions
- Oct 26, 2026agent-skills.github.microsoft.power-platform-skills: ea1e2e7 → a887c53 — sudosubin/agents.nix0 reactions
- Oct 26, 2026claude-code-plugins.github.microsoft.power-platform-skills: ea1e2e7 → a887c53 — sudosubin/agents.nix0 reactions
- Oct 26, 2026copilot-plugins.github.microsoft.power-platform-skills: ea1e2e7 → a887c53 — sudosubin/agents.nix0 reactions
- Oct 26, 2026agent-plugins.github.microsoft.power-platform-skills: 0-unstable-2026-10-24 → 0-unstable-2026-10-26 — sudosubin/agents.nix0 reactions
- Oct 26, 2026claude-code-plugins.github.microsoft.power-platform-skills: 0-unstable-2026-10-24 → 0-unstable-2026-10-26 — sudosubin/agents.nix0 reactions
- Oct 26, 2026agent-skills.github.microsoft.power-platform-skills: 0-unstable-2026-10-24 → 0-unstable-2026-10-26 — sudosubin/agents.nix0 reactions
- Oct 26, 2026copilot-plugins.github.microsoft.power-platform-skills: 0-unstable-2026-10-24 → 0-unstable-2026-10-26 — sudosubin/agents.nix0 reactions
- Oct 25, 2026[sdk-update] 自動検知: Power Platform SDK / 公式 skills 更新 (2026-10-25) — geekfujiwara/CodeAppsDevelopmentStandard0 reactions
- Oct 24, 2026agent-plugins.github.microsoft.power-platform-skills: 0-unstable-2026-10-23 → 0-unstable-2026-10-24 — sudosubin/agents.nix0 reactions
- Oct 24, 2026claude-code-plugins.github.microsoft.power-platform-skills: 0-unstable-2026-10-23 → 0-unstable-2026-10-24 — sudosubin/agents.nix0 reactions
- Oct 24, 2026copilot-plugins.github.microsoft.power-platform-skills: 0-unstable-2026-10-23 → 0-unstable-2026-10-24 — sudosubin/agents.nix0 reactions
- Oct 24, 2026agent-skills.github.microsoft.power-platform-skills: 0-unstable-2026-10-23 → 0-unstable-2026-10-24 — sudosubin/agents.nix0 reactions
- Oct 23, 2026claude-code-plugins.github.microsoft.power-platform-skills: b9786a1 → 9d2486d — sudosubin/agents.nix0 reactions
- Oct 23, 2026agent-plugins.github.microsoft.power-platform-skills: b9786a1 → 9d2486d — sudosubin/agents.nix0 reactions
- Oct 23, 2026agent-skills.github.microsoft.power-platform-skills: b9786a1 → 9d2486d — sudosubin/agents.nix0 reactions
- Oct 23, 2026copilot-plugins.github.microsoft.power-platform-skills: b9786a1 → 9d2486d — sudosubin/agents.nix0 reactions
- Oct 23, 2026claude-code-plugins.github.microsoft.power-platform-skills: 0-unstable-2026-10-22 → 0-unstable-2026-10-23 — sudosubin/agents.nix0 reactions
- Oct 23, 2026agent-plugins.github.microsoft.power-platform-skills: 0-unstable-2026-10-22 → 0-unstable-2026-10-23 — sudosubin/agents.nix0 reactions
- Oct 23, 2026agent-skills.github.microsoft.power-platform-skills: 0-unstable-2026-10-22 → 0-unstable-2026-10-23 — sudosubin/agents.nix0 reactions
- Oct 23, 2026copilot-plugins.github.microsoft.power-platform-skills: 0-unstable-2026-10-22 → 0-unstable-2026-10-23 — sudosubin/agents.nix0 reactions
- Oct 22, 2026agent-skills.github.microsoft.power-platform-skills: 633974a → 6ff5c6f — sudosubin/agents.nix0 reactions
- Oct 22, 2026agent-skills.github.microsoft.power-platform-skills: 0-unstable-2026-10-21 → 0-unstable-2026-10-22 — sudosubin/agents.nix0 reactions
- Oct 21, 2026agent-skills.github.microsoft.power-platform-skills: 0-unstable-2026-10-20 → 0-unstable-2026-10-21 — sudosubin/agents.nix0 reactions
- Oct 20, 2026agent-skills.github.microsoft.power-platform-skills: 0-unstable-2026-10-19 → 0-unstable-2026-10-20 — sudosubin/agents.nix0 reactions
In this repository145 other files it gives its agents.
- AGENTS.mdAGENTS.md
- plugins/canvas-apps/AGENTS.mdAGENTS.md
- plugins/code-apps/AGENTS.mdAGENTS.md
- plugins/mobile-apps/AGENTS.mdAGENTS.md
- plugins/mobile-apps/template/AGENTS.mdAGENTS.md
- plugins/model-apps/AGENTS.mdAGENTS.md
- plugins/pcf/AGENTS.mdAGENTS.md
- plugins/power-apps-mobile-extension/AGENTS.mdAGENTS.md
- plugins/power-automate/AGENTS.mdAGENTS.md
- plugins/power-pages/AGENTS.mdAGENTS.md
- plugins/process-intelligence/AGENTS.mdAGENTS.md
- plugins/mobile-apps/template/CLAUDE.mdCLAUDE.md
- plugins/mobile-apps/template/.github/copilot-instructions.mdCopilot instructions
- add-data-sourceSkill
- canvas-appSkill
- configure-canvas-mcpSkill
- report-issueSkill
- add-azuredevopsSkill
- add-connectorSkill
- add-datasourceSkill
- add-dataverseSkill
- add-excelSkill
- add-mcscopilotSkill
- add-office365Skill
- add-onedriveSkill
- add-sharepointSkill
- add-teamsSkill
- add-workiqSkill
- create-code-appSkill
- deploySkill
- list-connectionsSkill
- report-issueSkill
- generate-codeful-mcp-toolSkill
- generate-mcp-app-uiSkill
- report-issueSkill
- add-connectorSkill
- add-datasourceSkill
- add-dataverseSkill
- add-cameraSkill
- add-geolocationSkill
- add-pdf-reportSkill
- add-pdf-viewerSkill
- add-pen-inputSkill
- add-sample-dataSkill
- add-sharepointSkill
- add-table-to-offline-profileSkill
- assign-offline-profileSkill
- check-updatesSkill
- create-mobile-appSkill
- debug-appSkill
- deploySkill
- design-systemSkill
- edit-appSkill
- edit-offline-profileSkill
- enable-tables-offlineSkill
- list-connectionsSkill
- open-wrap-urlSkill
- preview-offline-scopeSkill
- preview-screensSkill
- report-issueSkill
- The first 60 shown.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

