genpage
Creates, updates, and deploys Power Apps generative pages for model-driven apps using React v17, TypeScript, and Fluent UI V9. Orchestrates specialist agents for planning, entity creation, and code generation. Use it when user asks to build, retrieve, or update a page in an existing Microsoft Power Apps model-driven app. Use it when user mentions "generative page", "page in a model-driven", or "genux".
Skill body
Power Apps Generative Pages Builder
Triggers: genpage, generative page, create genpage, genux page, build genux, power apps page, model page Keywords: power apps, generative pages, genux, model-driven, dataverse, react, fluent ui, pac cli Aliases: /genpage, /gen-page, /genux
Overview
This skill orchestrates four specialist agents across the create and edit flows:
Create flow:
genpage-planner— validates prerequisites, gathers requirements, detects what entities and apps exist, presents a plan for approval, writesgenpage-plan.mdgenpage-entity-builder— creates Dataverse entities (tables, columns, relationships, choices, sample data) via the plugin’s Node.js Web API scriptsgenpage-page-builder— generates one complete.tsxfile per page; multiple builders run in parallel for multi-page requests
Edit flow:
genpage-edit-planner— reads the downloaded page artifacts, gathers change requirements, presents an edit plan, writesgenpage-edit-plan.md
You (the skill) coordinate the agents and own app creation, RuntimeTypes generation, deployment, browser verification, and the inline application of planned edits.
References
- Code generation rules: rules.md
- Troubleshooting: troubleshooting.md
- Sample pages: samples/
Development Standards
- React 17 + TypeScript — all generated code
- Fluent UI V9 —
@fluentui/react-componentsexclusively (DatePicker from@fluentui/react-datepicker-compat, TimePicker from@fluentui/react-timepicker-compat) - Single file architecture — all components, utilities, styles in one
.tsxfile - No external libraries — only React, Fluent UI V9, approved Fluent icons, D3.js for charts
- Type-safe DataAPI — use RuntimeTypes when Dataverse entities are involved
- Responsive design — flexbox, relative units, never
100vh/100vw - Accessibility — WCAG AA, ARIA labels, keyboard navigation, semantic HTML
- Complete code — no placeholders, TODOs, or ellipses in final output
Instructions
Follow these phases in order for every /genpage invocation.
Phase 0: Create Working Directory
Derive a short folder name from the user’s requirements:
- Extract the page name or a 2-4 word summary from
$ARGUMENTS - Convert to kebab-case (e.g., “Candidate Tracker” →
candidate-tracker) - Create the folder:
mkdir -p <folder-name> - Resolve its absolute path — this is the working directory for all subsequent phases
Phase 1: Plan
⚠️ CRITICAL — you MUST invoke
genpage-plannervia theTasktool. You MUST NOT inline the planner’s questions yourself withAskUserQuestion.The planner is not optional or skippable. It runs:
- Prerequisite validation (
node --version,pac helpversion >= 2.7.0)- Auth verification (
pac auth list, environment selection)- The structured “Create new / Edit existing” question (via
AskUserQuestioninside the planner subagent, not here)- Language detection (
pac model list-languages) — only on new-page path- Entity existence detection (
pac model list-tables --search)- App detection (
pac model list) with proper selection prompts- Plan-mode presentation and approval
- Writes
genpage-plan.mdto the working directoryReasons to NEVER ask “new or edit?” yourself before invoking the planner:
- You would skip prereq + auth (the planner is the only thing that runs them)
- The structured question gives the user labeled options; an inline free-text prompt forces them to guess
- The planner returns
{ "action": "edit" }as a contract — your inline question can’t produce that signal cleanlyEven if
$ARGUMENTSlooks like it tells you the intent, still invoke the planner. Pass the intent in the prompt — the planner uses it to skip its own Question 1 if appropriate, but the prereq/auth/env steps still run.
Steps
- Invoke
genpage-plannerviaTaskwith the prompt below. - Wait for it to finish (it returns a summary).
- If the return includes
{ "action": "edit" }, jump to the Edit Flow section. - Otherwise the planner has written
genpage-plan.md. Proceed to Phase 2.
Invocation prompt
Pass a prompt that includes:
- The user’s requirements:
$ARGUMENTS - The working directory (absolute path from Phase 0)
- The plugin root path:
${CLAUDE_PLUGIN_ROOT}
Example:
You are the genpage-planner agent. Plan generative page(s) for the following requirements:
[paste $ARGUMENTS here verbatim, or “no arguments provided — gather from user”]
Working directory: [absolute path from Phase 0] Plugin root: ${CLAUDE_PLUGIN_ROOT}
Follow the instructions in your agent file. Validate prereqs, confirm auth, ask the new/edit question via AskUserQuestion, then proceed accordingly. Write genpage-plan.md to the working directory if creating. Return the page list, entity status, app selection, and any
{ "action": "edit" }signal when complete.
Phase 2: Create Entities (Conditional)
Read genpage-plan.md from the working directory. Check the Entity Creation Required
section.
If the section literally says “No entity creation required — all entities already exist”: Skip to Phase 3.
If entities need creating:
2a. Pre-flight: az + pac + Dataverse
Entity creation runs through the plugin’s Node.js Web API scripts using az for
auth, and the az and pac identities should normally match. Run the
consolidated pre-flight:
node "${CLAUDE_PLUGIN_ROOT}/scripts/check-auth.js"
It returns a single JSON object:
{
"ok": true | false,
"blocker": null | "az_missing" | "az_not_logged_in" | "pac_not_logged_in"
| "no_env_url" | "whoami_403" | "whoami_401" | "whoami_error",
"message": "human-readable next step",
"azUser": "...", "pacUser": "...", "envUrl": "...",
"identitiesMatch": true | false,
"whoAmI": { "ok": true, "userId": "...", "organizationId": "..." }
}
ok: trueandidentitiesMatch: true→ proceed to 2b.ok: trueandidentitiesMatch: false→ proceed to 2b but surface themessageto the user as an inline warning (“az is X, pac is Y — WhoAmI works for now, but if entity creation later returns 403, run the suggestedaz login --usernameto align them”).ok: false→ show themessagefield to the user verbatim and stop the workflow. The script already includes a fix-it command for every blocker (runaz login, etc.).
Capture envUrl from the result — Phase 2b passes it to the entity-builder.
2b. Invoke entity-builder
Invoke the genpage-entity-builder agent via the Task tool. Pass in the prompt:
- Path to
genpage-plan.md - Working directory (absolute path)
- Plugin root:
${CLAUDE_PLUGIN_ROOT} - Dataverse env URL (from
pac org who)
The entity-builder reads Solution and Publisher Prefix directly from the
plan’s ## Environment — no need to re-thread them here.
Wait for completion. The builder writes a transactional log at
<working-dir>/entity-creation-log.md for recovery on failure.
Phase 3: App Creation/Selection
Read genpage-plan.md for the app decision and the Solution line in
## Environment.
If “create new”:
pac model create --name "App Name" --solution "<Solution unique name>" --publish
--solution is mandatory. pac model create errors out with
"The given solution name is not valid: ()" if you omit it — its claimed
“active solution” fallback does not work in practice.
--publish is mandatory. Without it the new appmodule stays in draft and
the genux runtime URL errors with “app not published”.
- Use the plan’s
Solutionvalue verbatim. The planner always writes one (default fallback is literallyDefault). - If the plan is somehow missing
Solution, pass--solution Default— every Dataverse env has a built-in “Default Solution” by that unique name.
Store the new app-id for Phase 6.
If existing app-id: Use it directly. pac model create is not called, so
the Solution line is informational only for this phase.
Phase 4: Generate RuntimeTypes (Conditional)
If any page uses Dataverse entities, generate the TypeScript schema:
pac model genpage generate-types --data-sources "entity1,entity2,..." --output-file <working-dir>/RuntimeTypes.ts
Windows + Bash: Always use forward slashes in file paths (e.g.,
D:/temp/RuntimeTypes.ts).
After generating, read the RuntimeTypes.ts file to verify it generated correctly.
For mock data pages only: Skip this phase.
Phase 5: Build Pages (Parallel)
Read genpage-plan.md and extract the pages table.
5a. Validate the plan before dispatch
Before invoking any builders, verify:
- At least one page exists in the
## Pagestable - Every page has a
### [Page Name]subsection in## Per-Page Specifications - All filenames in the
## Pagestable are unique. If any are duplicated, rewrite the plan appending-1,-2, etc. before dispatch. Duplicate filenames cause silent last-writer-wins data loss under parallel execution.
See ${CLAUDE_PLUGIN_ROOT}/references/plan-schema.md for the full contract.
5b. Single-page fast path (skip Task dispatch when N=1)
If the plan’s Pages table contains exactly one row, do NOT dispatch a Task subagent. Inline the page-builder workflow directly in the orchestrator:
- Read
${CLAUDE_PLUGIN_ROOT}/references/rules.md - Read the sample listed in the plan’s
## Relevant Samples - If the plan’s Per-Page Specification has
Needs caching: true, also read${CLAUDE_PLUGIN_ROOT}/references/data-caching.md - If the plan’s
## Environmentindicates non-English languages, also read${CLAUDE_PLUGIN_ROOT}/references/localization.md - Read
genpage-plan.md(already in working directory) andRuntimeTypes.tsif Data mode is dataverse - Write the
.tsxfile to<working-dir>/<filename>.tsxfollowing all rules - After writing, Grep every named import from
@fluentui/react-iconsagainst${CLAUDE_PLUGIN_ROOT}/references/verified-icons.txt(one Grep per name). Rewrite any unverified names with the closest verified alternative; do not load the full icon list into context - Proceed to Phase 6
This saves ~5-15s of Task overhead and ~3K tokens that would otherwise be duplicated in a subagent context.
5c. Multi-page: invoke page-builders in parallel
If the plan’s Pages table contains 2+ rows, invoke a genpage-page-builder
agent via the Task tool per page. Fire all invocations in a single message
for parallel execution.
For each page, pass a prompt that includes:
- Page name (e.g., “Candidate Tracker”)
- Target file name (e.g., “candidate-tracker.tsx”)
- Absolute path to
genpage-plan.md - Data mode (see below) — either a RuntimeTypes path or an explicit mock flag
- Working directory
- Plugin root:
${CLAUDE_PLUGIN_ROOT}
For Dataverse pages, include the RuntimeTypes line:
You are the genpage-page-builder agent. Generate the [Page Name] page.
- Target file: [filename].tsx
- Plan document: [absolute path to genpage-plan.md]
- Data mode: dataverse
- RuntimeTypes: [absolute path to RuntimeTypes.ts]
- Working directory: [absolute path from Phase 0]
- Plugin root: ${CLAUDE_PLUGIN_ROOT}
Follow the instructions in your agent file. Write [filename].tsx and return your result when done.
For mock data pages, omit the RuntimeTypes line and set Data mode: mock:
You are the genpage-page-builder agent. Generate the [Page Name] page.
- Target file: [filename].tsx
- Plan document: [absolute path to genpage-plan.md]
- Data mode: mock
- Working directory: [absolute path from Phase 0]
- Plugin root: ${CLAUDE_PLUGIN_ROOT}
Follow the instructions in your agent file. Write [filename].tsx and return your result when done.
Wait for all page-builder tasks to complete before proceeding.
Phase 6: Deploy
For each .tsx file produced, deploy to Power Apps.
Copy the upload commands below exactly — --app-id, --code-file, --prompt, --agent-message are all required and must use these exact flag names.
--prompt semantics
- First upload (
--add-to-sitemap, no--page-id): full page description from plan’s## User Requirements. - Any subsequent upload (
--page-id, no--add-to-sitemap): delta only — the changes in this upload, written like a commit message, never a re-statement of the original.
Applies in Phase 6 updates, Phase 6.5 PAGEREF re-uploads, Phase 7.5 fix re-deploys, and the entire edit flow.
For Dataverse entity pages (first upload — create):
pac model genpage upload `
--app-id <app-id> `
--code-file <working-dir>/<file>.tsx `
--name "Page Display Name" `
--data-sources "entity1,entity2" `
--prompt "<Full page description from plan's ## User Requirements>" `
--model "<current-model-id>" `
--agent-message "Description of what was built and any relevant details" `
--add-to-sitemap
For mock data pages: Same but omit --data-sources.
For updating existing pages (subsequent upload):
Use --page-id, omit --add-to-sitemap, and scope --prompt to the delta only:
pac model genpage upload `
--app-id <app-id> `
--page-id <page-id> `
--code-file <working-dir>/<file>.tsx `
--data-sources "entity1,entity2" `
--prompt "<Only the changes in this upload, e.g. 'Add a search box and sort by company name'>" `
--model "<current-model-id>" `
--agent-message "Description of what was changed in this upload"
Phase 6.5: Navigation Fix-Up (Multi-Page Only)
Runs only when the plan has 2+ pages AND any built .tsx contains a PAGEREF_
token. Page-builders emit pageId: "PAGEREF_<filename-without-tsx>" as a
placeholder because GUIDs don’t exist until after Phase 6 (see Rule 13). This
phase substitutes the real GUIDs.
Steps
- Build
filename-without-tsx → page-idmap from Phase 6 upload output. - Sort keys by length descending so
PAGEREF_petcan’t match insidePAGEREF_pet-gallery. - For each
.tsxin<working-dir>/*.tsx(top level only, no recursion), replace every quoted"PAGEREF_<name>"(must be in double quotes — that’s the format page-builders emit) with"<page-id-guid>". - If a placeholder doesn’t match any map key (typo, missing sibling), stop and report — never silently ship the literal string.
-
Re-upload only the files that had at least one replacement. Use the update form of
pac model genpage upload(--page-id, no--add-to-sitemap). Per the “--promptsemantics” rule in Phase 6, this is an update, so--promptdescribes the delta only — not the original page description:pac model genpage upload ` --app-id <app-id> ` --page-id <page-id-from-Phase-6> ` --code-file <working-dir>/<file>.tsx ` --data-sources "entity1,entity2" ` --prompt "Resolve cross-page navigation placeholders to real page GUIDs (post-deploy fix-up)" ` --model "<current-model-id>" ` --agent-message "Replaced PAGEREF_<name> tokens with actual page IDs returned by Phase 6"
Pages with no PAGEREF_ strings need no second upload.
Phase 7: Verify in Browser (Optional)
After successful deployment, ask the user via AskUserQuestion:
“Would you like to verify the page(s) in the browser using Playwright?”
Options: Yes, verify in browser / Skip verification
- If the user picks Skip verification → jump to Phase 8.
- If the user picks Yes → read
${CLAUDE_PLUGIN_ROOT}/skills/genpage/verify-flow.mdfor the full Playwright verification workflow (navigate, structural verification including below-the-fold, interactive testing, screenshots, fix-and-redeploy). The orchestrator only loads that file on demand to keep context lean when verification is skipped.
Phase 8: Summary
Write a workflow-log.md file to the working directory summarizing the run:
agents invoked, commands executed, decisions made, files produced. This log is
useful for debugging and required by the eval harness.
Then present a final summary to the user:
## Genpage Complete
| Page | File | Entities | Status |
|------|------|----------|--------|
| [Name] | [file].tsx | [entities or "mock data"] | Deployed |
App: [app name] ([app-id])
Screenshots: [if verification was done]
Next steps: Share with team, iterate on design, create additional pages
Edit Flow
For the edit flow (triggered when the genpage-planner returns
{ "action": "edit" }), see edit-flow.md in this folder.
The edit flow has its own 8 phases (Edit Phase 1-8): discover and select target
app + page via pac model list + pac model genpage list, download, generate
RuntimeTypes if needed, invoke genpage-edit-planner, apply the edit inline,
deploy, verify, summarize.