From 69e2c3352f29dce9de146d926913eac6ffb4b85d Mon Sep 17 00:00:00 2001 From: Priyanshu Agrawal Date: Fri, 24 Apr 2026 17:50:52 +0530 Subject: [PATCH 1/5] Refresh power-pages README and plan logos - Expand README Skills section to cover the 6 new skills (/test-site, /integrate-backend, /add-server-logic, /add-cloud-flow, /audit-permissions, /report-issue), grouped into Scaffolding, Data, Backend, Security, Polish, and Support. - Add a "Bundled MCP servers" section describing the playwright and microsoft-learn servers shipped in .mcp.json. - Update the typical workflow to route backend work through /integrate-backend and end with /test-site. - Replace the text-based "PP" gradient tile in all 6 plan HTML templates (data-model, permissions, audit-report, backend, cloud-flow, serverlogic) with the real power-pages-icon.png embedded as a base64 data URI so rendered plans stay self-contained. Co-Authored-By: Claude Opus 4.7 (1M context) --- plugins/power-pages/README.md | 140 ++++++++++++++---- .../agents/assets/data-model-plan.html | 4 +- .../agents/assets/permissions-plan.html | 4 +- .../assets/cloud-flow-plan.html | 4 +- .../assets/serverlogic-plan.html | 4 +- .../assets/audit-report.html | 4 +- .../assets/backend-plan.html | 4 +- 7 files changed, 125 insertions(+), 39 deletions(-) diff --git a/plugins/power-pages/README.md b/plugins/power-pages/README.md index 0e7856339..1dd9f01b0 100644 --- a/plugins/power-pages/README.md +++ b/plugins/power-pages/README.md @@ -38,9 +38,11 @@ This keeps hook behavior in one place and avoids relying on skill-frontmatter ho ## Skills -The plugin provides 9 skills that cover the full lifecycle of a Power Pages code site. Each skill is invoked conversationally — just describe what you want to do. +The plugin provides 15 skills that cover the full lifecycle of a Power Pages code site — scaffolding, deployment, data modeling, backend integration, authentication, testing, and auditing. Each skill is invoked conversationally — just describe what you want to do. -### `/create-site` +### Site scaffolding and deployment + +#### `/create-site` > "Create a Power Pages site with React for a job board" @@ -51,7 +53,7 @@ Scaffolds a complete code site from a framework template, applies your design di - Live browser preview during development - Git commits at each milestone -### `/deploy-site` +#### `/deploy-site` > "Deploy my site to Power Pages" @@ -61,7 +63,7 @@ Builds your project and uploads it to your Power Pages environment using `pac pa - Confirms target environment before deploying - Creates `.powerpages-site` folder with deployment artifacts -### `/activate-site` +#### `/activate-site` > "Activate my site" @@ -71,7 +73,20 @@ Provisions a website record in your Power Platform environment so your site is a - Polls provisioning status until the site is live - Provides the final site URL -### `/setup-datamodel` +#### `/test-site` + +> "Test my deployed site at https://contoso.powerappsportals.com" + +Runtime-tests a deployed, activated site using a real browser (via the bundled Playwright MCP). Crawls discoverable links, verifies pages render, captures network traffic for API calls, and produces a test report. + +- Browser-based navigation and page crawling +- Network request verification for Web API / Server Logic / Cloud Flow endpoints +- Console and network error capture +- Screenshots on failure + +### Data modeling + +#### `/setup-datamodel` > "Create Dataverse tables for my site" @@ -82,7 +97,7 @@ Analyzes your site's requirements and creates Dataverse tables, columns, and rel - Generates a `.datamodel-manifest.json` used by downstream skills - Visualizes the data model as a Mermaid ER diagram -### `/add-sample-data` +#### `/add-sample-data` > "Add sample data to my tables" @@ -92,7 +107,19 @@ Populates your Dataverse tables with realistic, contextually appropriate records - Generates values that match column types and names (emails, dates, currencies, etc.) - Inserts records in dependency order (parent tables first) -### `/integrate-webapi` +### Backend integration + +#### `/integrate-backend` + +> "I need to send a confirmation email when someone submits the contact form" + +Router skill that analyzes your business problem and recommends the right backend approach — Web API, Server Logic, Cloud Flow, or a combination — then hands off to the specialized skill(s). Use this as the entry point when you're not sure which integration path fits your scenario. + +- Generates a visual backend plan (HTML) with the recommended approach and trade-offs +- Routes to `/integrate-webapi`, `/add-server-logic`, or `/add-cloud-flow` +- Supports multi-approach plans (e.g., Web API for CRUD + Cloud Flow for notifications) + +#### `/integrate-webapi` > "Connect my site to the Dataverse tables" @@ -113,7 +140,41 @@ The skill first scans your codebase to find components using mock data, placehol - Existing components are refactored to use real API calls (mock data and placeholder fetches are replaced) - `.powerpages-site/table-permissions/` and `.powerpages-site/site-settings/` directories are populated for deployment -### `/setup-auth` +#### `/add-server-logic` + +> "Move my pricing calculation out of the browser and onto the server" + +Creates and manages Power Pages Server Logic — server-side JavaScript that runs securely on the Power Pages runtime. Covers the full lifecycle: gathering requirements, fetching Microsoft Learn docs for reference, implementing the handler, configuring site settings, and deploying. + +- Grounded in live Microsoft Learn docs (via the bundled MCP) +- Generates the server-side handler and its deployment metadata +- Wires up the client-side call site in your frontend code +- Handles site-setting flags required to enable Server Logic + +#### `/add-cloud-flow` + +> "When a user submits an application, kick off my existing approval flow" + +Integrates Power Automate cloud flows into a Power Pages site. Discovers flows available in the environment, suggests relevant ones for your intent, identifies scenarios and web roles, creates flow metadata, and generates the client-side code to invoke the flow. + +- Lists available cloud flows in the environment +- Handles both new flow registration and adding already-registered flows to additional pages +- Generates metadata files and client-side call-site code +- Configures web role access for the flow + +### Security and access + +#### `/create-webroles` + +> "Create web roles for my site" + +Generates web role YAML files in your `.powerpages-site` directory for managing user access. + +- Discovers existing roles before creating new ones +- Generates proper UUIDs for each role +- Enforces uniqueness constraints (one anonymous role, one authenticated role) + +#### `/setup-auth` > "Set up authentication for my site" @@ -124,17 +185,19 @@ Adds login/logout functionality and role-based authorization to your site. - Role-based UI patterns (show/hide elements by role) - Framework-specific implementation (hooks, composables, services) -### `/create-webroles` +#### `/audit-permissions` -> "Create web roles for my site" +> "Check my table permissions for security issues" -Generates web role YAML files in your `.powerpages-site` directory for managing user access. +Audits existing table permissions on a deployed or in-progress site by analyzing them against the site code and live Dataverse metadata. Produces a visual HTML audit report grouped by severity with suggested fixes. -- Discovers existing roles before creating new ones -- Generates proper UUIDs for each role -- Enforces uniqueness constraints (one anonymous role, one authenticated role) +- Findings grouped as critical / warning / info / pass +- Cross-references code usage, web roles, and Dataverse schema +- Suggests concrete fixes for each issue + +### Polish -### `/add-seo` +#### `/add-seo` > "Add SEO to my site" @@ -144,6 +207,18 @@ Adds search engine optimization artifacts: `robots.txt`, `sitemap.xml`, and meta - Generates sitemap with production URLs - Adds viewport, charset, description, and social sharing meta tags +### Support + +#### `/report-issue` + +> "Report a bug with the create-site skill" + +Collects context about the current session and opens a pre-filled GitHub issue against [microsoft/power-platform-skills](https://github.com/microsoft/power-platform-skills/issues). + +- Captures the skill(s) involved and recent error messages +- Attaches relevant file paths and environment info +- Opens the issue in your browser for final review + ## Agents The plugin includes 4 specialized agents that are spawned automatically by skills when needed: @@ -152,29 +227,40 @@ The plugin includes 4 specialized agents that are spawned automatically by skill |---|---|---| | **Data Model Architect** | Analyzes your site and proposes a Dataverse data model with an ER diagram | `/setup-datamodel` | | **Web API Integration** | Creates typed API client, services, and hooks for a Dataverse table | `/integrate-webapi` | -| **Table Permissions** | Proposes table permissions (web roles, CRUD flags, scopes) with a visual Mermaid diagram | `/integrate-webapi` | +| **Table Permissions** | Proposes table permissions (web roles, CRUD flags, scopes) with a visual Mermaid diagram | `/integrate-webapi`, `/audit-permissions` | | **Web API Settings** | Proposes Web API site settings with case-sensitive validated column names from Dataverse | `/integrate-webapi` | The Data Model Architect, Table Permissions, and Web API Settings agents are **read-only** — they analyze and propose but never create or modify resources directly. You review and approve their proposals before any changes are made. +## Bundled MCP servers + +The plugin ships with two MCP servers configured in `.mcp.json` — they start automatically when the plugin loads: + +| Server | Purpose | Used by | +|---|---|---| +| **playwright** | Headless browser automation for live previews and runtime tests | `/create-site`, `/test-site` | +| **microsoft-learn** | Grounded search/fetch over official Microsoft Learn docs | `/add-server-logic` | + ## Typical Workflow A common end-to-end workflow looks like this: ``` -1. /create-site → Scaffold + design + build pages -2. /deploy-site → Upload to Power Pages environment -3. /activate-site → Provision a public URL -4. /setup-datamodel → Create Dataverse tables -5. /add-sample-data → Populate tables with test records -6. /integrate-webapi → Configure table permissions, web API site settings and generate API client code -7. /create-webroles → Define access roles -8. /setup-auth → Add login/logout + role-based UI -9. /add-seo → Search engine optimization -10. /deploy-site → Push final changes live +1. /create-site → Scaffold + design + build pages +2. /deploy-site → Upload to Power Pages environment +3. /activate-site → Provision a public URL +4. /setup-datamodel → Create Dataverse tables +5. /add-sample-data → Populate tables with test records +6. /integrate-backend → Pick the right backend approach (Web API / Server Logic / Cloud Flow) +7. /create-webroles → Define access roles +8. /setup-auth → Add login/logout + role-based UI +9. /audit-permissions → Verify table permissions are safe +10. /add-seo → Search engine optimization +11. /deploy-site → Push final changes live +12. /test-site → Runtime smoke test on the live URL ``` -Steps can be run independently — you don't need to follow this exact order. Each skill checks its own prerequisites and will tell you if something is missing. +Steps can be run independently — you don't need to follow this exact order. Each skill checks its own prerequisites and will tell you if something is missing. If something goes wrong, `/report-issue` opens a pre-filled GitHub issue. ## Running Without Interruption diff --git a/plugins/power-pages/agents/assets/data-model-plan.html b/plugins/power-pages/agents/assets/data-model-plan.html index 278d07c7a..a13139c25 100644 --- a/plugins/power-pages/agents/assets/data-model-plan.html +++ b/plugins/power-pages/agents/assets/data-model-plan.html @@ -30,7 +30,7 @@ /* Top Bar */ .topbar{z-index:100;background:var(--surface);box-shadow:var(--shadow-4);padding:14px 28px;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:10px;} .topbar-left{display:flex;align-items:center;gap:14px;} -.logo{width:36px;height:36px;border-radius:var(--radius);background:linear-gradient(135deg,#0078d4,#5c2d91);display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:800;color:#fff;font-family:var(--mono);letter-spacing:-0.5px;} +.logo{width:36px;height:36px;object-fit:contain;display:block;flex-shrink:0;} .topbar-title{font-size:16px;font-weight:700;color:var(--text-bright);} .topbar-sub{font-size:11px;color:var(--text-dim);margin-top:1px;} @@ -158,7 +158,7 @@
- +
Data Model Plan
__SITE_NAME__
diff --git a/plugins/power-pages/agents/assets/permissions-plan.html b/plugins/power-pages/agents/assets/permissions-plan.html index 33d3ff655..967887c86 100644 --- a/plugins/power-pages/agents/assets/permissions-plan.html +++ b/plugins/power-pages/agents/assets/permissions-plan.html @@ -29,7 +29,7 @@ /* Top Bar */ .topbar{z-index:100;background:var(--surface);box-shadow:var(--shadow-4);padding:14px 28px;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:10px;} .topbar-left{display:flex;align-items:center;gap:14px;} -.logo{width:36px;height:36px;border-radius:var(--radius);background:linear-gradient(135deg,#0078d4,#5c2d91);display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:800;color:#fff;font-family:var(--mono);letter-spacing:-0.5px;} +.logo{width:36px;height:36px;object-fit:contain;display:block;flex-shrink:0;} .topbar-title{font-size:16px;font-weight:700;color:var(--text-bright);} .topbar-sub{font-size:11px;color:var(--text-dim);margin-top:1px;} @@ -130,7 +130,7 @@
- +
Table Permissions Plan
__SITE_NAME__
diff --git a/plugins/power-pages/skills/add-cloud-flow/assets/cloud-flow-plan.html b/plugins/power-pages/skills/add-cloud-flow/assets/cloud-flow-plan.html index 76c73bdc6..046cb22f7 100644 --- a/plugins/power-pages/skills/add-cloud-flow/assets/cloud-flow-plan.html +++ b/plugins/power-pages/skills/add-cloud-flow/assets/cloud-flow-plan.html @@ -18,7 +18,7 @@ body{font-family:var(--sans);background:var(--bg);color:var(--text);font-size:14px;line-height:1.6;} .topbar{z-index:100;background:var(--surface);box-shadow:var(--shadow-4);padding:14px 28px;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:10px;} .topbar-left{display:flex;align-items:center;gap:14px;} -.logo{width:36px;height:36px;border-radius:var(--radius);background:linear-gradient(135deg,#0078d4,#5c2d91);display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:800;color:#fff;font-family:var(--mono);letter-spacing:-0.5px;} +.logo{width:36px;height:36px;object-fit:contain;display:block;flex-shrink:0;} .topbar-title{font-size:16px;font-weight:700;color:var(--text-bright);} .topbar-sub{font-size:11px;color:var(--text-dim);margin-top:1px;} .layout{display:flex;min-height:calc(100vh - 65px);} @@ -82,7 +82,7 @@
- +
__PLAN_TITLE__
__SITE_NAME__
diff --git a/plugins/power-pages/skills/add-server-logic/assets/serverlogic-plan.html b/plugins/power-pages/skills/add-server-logic/assets/serverlogic-plan.html index 9c5d6a639..deba39190 100644 --- a/plugins/power-pages/skills/add-server-logic/assets/serverlogic-plan.html +++ b/plugins/power-pages/skills/add-server-logic/assets/serverlogic-plan.html @@ -18,7 +18,7 @@ body{font-family:var(--sans);background:var(--bg);color:var(--text);font-size:14px;line-height:1.6;} .topbar{z-index:100;background:var(--surface);box-shadow:var(--shadow-4);padding:14px 28px;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:10px;} .topbar-left{display:flex;align-items:center;gap:14px;} -.logo{width:36px;height:36px;border-radius:var(--radius);background:linear-gradient(135deg,#0078d4,#5c2d91);display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:800;color:#fff;font-family:var(--mono);letter-spacing:-0.5px;} +.logo{width:36px;height:36px;object-fit:contain;display:block;flex-shrink:0;} .topbar-title{font-size:16px;font-weight:700;color:var(--text-bright);} .topbar-sub{font-size:11px;color:var(--text-dim);margin-top:1px;} .layout{display:flex;min-height:calc(100vh - 65px);} @@ -110,7 +110,7 @@
- +
__PLAN_TITLE__
__SITE_NAME__
diff --git a/plugins/power-pages/skills/audit-permissions/assets/audit-report.html b/plugins/power-pages/skills/audit-permissions/assets/audit-report.html index 953e5cad2..43f0f7002 100644 --- a/plugins/power-pages/skills/audit-permissions/assets/audit-report.html +++ b/plugins/power-pages/skills/audit-permissions/assets/audit-report.html @@ -29,7 +29,7 @@ /* Top Bar */ .topbar{z-index:100;background:var(--surface);box-shadow:var(--shadow-4);padding:14px 28px;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:10px;} .topbar-left{display:flex;align-items:center;gap:14px;} -.logo{width:36px;height:36px;border-radius:var(--radius);background:linear-gradient(135deg,#0078d4,#5c2d91);display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:800;color:#fff;font-family:var(--mono);letter-spacing:-0.5px;} +.logo{width:36px;height:36px;object-fit:contain;display:block;flex-shrink:0;} .topbar-title{font-size:16px;font-weight:700;color:var(--text-bright);} .topbar-sub{font-size:11px;color:var(--text-dim);margin-top:1px;} @@ -133,7 +133,7 @@
- +
Permissions Audit Report
__SITE_NAME__
diff --git a/plugins/power-pages/skills/integrate-backend/assets/backend-plan.html b/plugins/power-pages/skills/integrate-backend/assets/backend-plan.html index 35306403d..2bb035bbd 100644 --- a/plugins/power-pages/skills/integrate-backend/assets/backend-plan.html +++ b/plugins/power-pages/skills/integrate-backend/assets/backend-plan.html @@ -19,7 +19,7 @@ body{font-family:var(--sans);background:var(--bg);color:var(--text);font-size:14px;line-height:1.6;} .topbar{z-index:100;background:var(--surface);box-shadow:var(--shadow-4);padding:14px 28px;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:10px;} .topbar-left{display:flex;align-items:center;gap:14px;} -.logo{width:36px;height:36px;border-radius:var(--radius);background:linear-gradient(135deg,#0078d4,#5c2d91);display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:800;color:#fff;font-family:var(--mono);letter-spacing:-0.5px;} +.logo{width:36px;height:36px;object-fit:contain;display:block;flex-shrink:0;} .topbar-title{font-size:16px;font-weight:700;color:var(--text-bright);} .topbar-sub{font-size:11px;color:var(--text-dim);margin-top:1px;} .layout{display:flex;min-height:calc(100vh - 65px);} @@ -168,7 +168,7 @@
- +
__PLAN_TITLE__
__SITE_NAME__
From 1d6af2eb46d8d6dd05ba4d5581c5d7bc331dfa19 Mon Sep 17 00:00:00 2001 From: Priyanshu Agrawal Date: Fri, 24 Apr 2026 20:14:47 +0530 Subject: [PATCH 2/5] address PR review feedback Switch plan/report templates from an inline ~30KB base64 data URI to a direct ./power-pages-icon.png reference. scripts/lib/render-template.js now silently copies the shared icon from skills/create-site/assets/shared/power-pages-icon.png next to every rendered HTML so the output stays self-contained without duplicating the large literal across six templates. Addresses feedback from copilot-pull-request-reviewer on PR #123. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../agents/assets/data-model-plan.html | 2 +- .../agents/assets/permissions-plan.html | 2 +- .../power-pages/scripts/lib/render-template.js | 15 +++++++++++++++ .../add-cloud-flow/assets/cloud-flow-plan.html | 2 +- .../add-server-logic/assets/serverlogic-plan.html | 2 +- .../audit-permissions/assets/audit-report.html | 2 +- .../integrate-backend/assets/backend-plan.html | 2 +- 7 files changed, 21 insertions(+), 6 deletions(-) diff --git a/plugins/power-pages/agents/assets/data-model-plan.html b/plugins/power-pages/agents/assets/data-model-plan.html index a13139c25..a5612dc89 100644 --- a/plugins/power-pages/agents/assets/data-model-plan.html +++ b/plugins/power-pages/agents/assets/data-model-plan.html @@ -158,7 +158,7 @@
- +
Data Model Plan
__SITE_NAME__
diff --git a/plugins/power-pages/agents/assets/permissions-plan.html b/plugins/power-pages/agents/assets/permissions-plan.html index 967887c86..822db8b9d 100644 --- a/plugins/power-pages/agents/assets/permissions-plan.html +++ b/plugins/power-pages/agents/assets/permissions-plan.html @@ -130,7 +130,7 @@
- +
Table Permissions Plan
__SITE_NAME__
diff --git a/plugins/power-pages/scripts/lib/render-template.js b/plugins/power-pages/scripts/lib/render-template.js index f0fabb587..918cd751b 100644 --- a/plugins/power-pages/scripts/lib/render-template.js +++ b/plugins/power-pages/scripts/lib/render-template.js @@ -75,6 +75,21 @@ function renderTemplate({ templatePath, outputPath, dataPath, dataObject, requir } fs.writeFileSync(outputPath, result, 'utf8'); + + // Silently copy the shared Power Pages icon next to the rendered HTML so the + // template's reference resolves when the + // file is opened directly (or served from docs/). Copy is best-effort — + // rendering still succeeds if the icon is missing. + const iconSrc = path.join(__dirname, '..', '..', 'skills', 'create-site', 'assets', 'shared', 'power-pages-icon.png'); + const iconDest = path.join(outputDir, 'power-pages-icon.png'); + try { + if (fs.existsSync(iconSrc) && !fs.existsSync(iconDest)) { + fs.copyFileSync(iconSrc, iconDest); + } + } catch { + // non-fatal + } + console.log(JSON.stringify({ status: 'ok', output: outputPath })); } diff --git a/plugins/power-pages/skills/add-cloud-flow/assets/cloud-flow-plan.html b/plugins/power-pages/skills/add-cloud-flow/assets/cloud-flow-plan.html index 046cb22f7..c09f7d0c9 100644 --- a/plugins/power-pages/skills/add-cloud-flow/assets/cloud-flow-plan.html +++ b/plugins/power-pages/skills/add-cloud-flow/assets/cloud-flow-plan.html @@ -82,7 +82,7 @@
- +
__PLAN_TITLE__
__SITE_NAME__
diff --git a/plugins/power-pages/skills/add-server-logic/assets/serverlogic-plan.html b/plugins/power-pages/skills/add-server-logic/assets/serverlogic-plan.html index deba39190..916bd7f4c 100644 --- a/plugins/power-pages/skills/add-server-logic/assets/serverlogic-plan.html +++ b/plugins/power-pages/skills/add-server-logic/assets/serverlogic-plan.html @@ -110,7 +110,7 @@
- +
__PLAN_TITLE__
__SITE_NAME__
diff --git a/plugins/power-pages/skills/audit-permissions/assets/audit-report.html b/plugins/power-pages/skills/audit-permissions/assets/audit-report.html index 43f0f7002..68645b8eb 100644 --- a/plugins/power-pages/skills/audit-permissions/assets/audit-report.html +++ b/plugins/power-pages/skills/audit-permissions/assets/audit-report.html @@ -133,7 +133,7 @@
- +
Permissions Audit Report
__SITE_NAME__
diff --git a/plugins/power-pages/skills/integrate-backend/assets/backend-plan.html b/plugins/power-pages/skills/integrate-backend/assets/backend-plan.html index 2bb035bbd..16f190843 100644 --- a/plugins/power-pages/skills/integrate-backend/assets/backend-plan.html +++ b/plugins/power-pages/skills/integrate-backend/assets/backend-plan.html @@ -168,7 +168,7 @@
- +
__PLAN_TITLE__
__SITE_NAME__
From 8bc8f5fe954c85daa828b07ddbdd751c4a8e7aec Mon Sep 17 00:00:00 2001 From: Priyanshu Agrawal Date: Fri, 24 Apr 2026 20:15:00 +0530 Subject: [PATCH 3/5] fix(data-model-plan): sanitize non-identifier chars in ER diagram types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mermaid's erDiagram parser requires attribute types to match ATTRIBUTE_WORD. Dataverse exposes a boolean column type literally named "Yes/No" — the slash tripped the parser with "Expecting 'ATTRIBUTE_WORD', got '/'", and the rendered plan showed "Syntax error in text" instead of the ER diagram. The previous sanitizer only stripped dots and whitespace. Widen it to strip anything outside [A-Za-z0-9_] so future Dataverse type names with special chars also render cleanly. Co-Authored-By: Claude Opus 4.7 (1M context) --- plugins/power-pages/agents/assets/data-model-plan.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/power-pages/agents/assets/data-model-plan.html b/plugins/power-pages/agents/assets/data-model-plan.html index a5612dc89..657fdcd8a 100644 --- a/plugins/power-pages/agents/assets/data-model-plan.html +++ b/plugins/power-pages/agents/assets/data-model-plan.html @@ -389,7 +389,9 @@

ER Diagram

: (t.columns || []); cols.forEach(col => { - const type = col.type.replace(/[.\s]/g, '_'); + // Strip any char that isn't valid in a mermaid ATTRIBUTE_WORD — e.g. the + // slash in Dataverse "Yes/No" breaks the erDiagram parser. + const type = col.type.replace(/[^A-Za-z0-9_]/g, '_'); const key = col.key || ''; lines.push(' ' + type + ' ' + col.logicalName + (key ? ' ' + key : '')); }); From 503d9aaf45f97bd1bf4dff884821482fb3d74491 Mon Sep 17 00:00:00 2001 From: Priyanshu Agrawal Date: Fri, 24 Apr 2026 20:38:20 +0530 Subject: [PATCH 4/5] address PR review feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - render-template.js: drop the !fs.existsSync(iconDest) guard so the shared power-pages-icon.png is copied unconditionally on every render. Stale icons at the destination now get overwritten, keeping rendered plans in sync with the shared asset. - render-cloudflow-plan.test.js: add two tests covering the icon-copy path — one verifies the icon is produced alongside the HTML with bytes matching the shared asset, one pre-seeds a stale icon and asserts the re-render overwrites it. Addresses copilot-pull-request-reviewer feedback on PR #123 (L86 unconditional overwrite, L88 missing test coverage). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../scripts/lib/render-template.js | 2 +- .../tests/render-cloudflow-plan.test.js | 44 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/plugins/power-pages/scripts/lib/render-template.js b/plugins/power-pages/scripts/lib/render-template.js index 918cd751b..520d908e6 100644 --- a/plugins/power-pages/scripts/lib/render-template.js +++ b/plugins/power-pages/scripts/lib/render-template.js @@ -83,7 +83,7 @@ function renderTemplate({ templatePath, outputPath, dataPath, dataObject, requir const iconSrc = path.join(__dirname, '..', '..', 'skills', 'create-site', 'assets', 'shared', 'power-pages-icon.png'); const iconDest = path.join(outputDir, 'power-pages-icon.png'); try { - if (fs.existsSync(iconSrc) && !fs.existsSync(iconDest)) { + if (fs.existsSync(iconSrc)) { fs.copyFileSync(iconSrc, iconDest); } } catch { diff --git a/plugins/power-pages/scripts/tests/render-cloudflow-plan.test.js b/plugins/power-pages/scripts/tests/render-cloudflow-plan.test.js index 1fb545449..aee3832e1 100644 --- a/plugins/power-pages/scripts/tests/render-cloudflow-plan.test.js +++ b/plugins/power-pages/scripts/tests/render-cloudflow-plan.test.js @@ -110,6 +110,50 @@ test('render-cloudflow-plan fails with invalid --data-inline JSON', () => { assert.match(result.stderr, /not valid JSON/); }); +test('render-cloudflow-plan copies power-pages-icon.png next to the HTML', () => { + const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cloudflow-plan-')); + const outputPath = path.join(tempDir, 'plan.html'); + const iconPath = path.join(tempDir, 'power-pages-icon.png'); + + const result = spawnSync( + process.execPath, + [scriptPath, '--output', outputPath, '--data-inline', JSON.stringify(SAMPLE_DATA)], + { encoding: 'utf8' } + ); + assert.equal(result.status, 0, result.stderr || result.stdout); + + assert.ok(fs.existsSync(iconPath), 'icon should be copied alongside the rendered HTML'); + const sourceIcon = path.join( + __dirname, '..', '..', 'skills', 'create-site', 'assets', 'shared', 'power-pages-icon.png' + ); + assert.deepEqual(fs.readFileSync(iconPath), fs.readFileSync(sourceIcon), 'icon bytes should match shared asset'); +}); + +test('render-cloudflow-plan overwrites a stale power-pages-icon.png on re-render', () => { + const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cloudflow-plan-')); + const outputPath = path.join(tempDir, 'plan.html'); + const iconPath = path.join(tempDir, 'power-pages-icon.png'); + + // Pre-seed a stale/dummy icon that render should overwrite. + fs.writeFileSync(iconPath, Buffer.from([0x00, 0x01, 0x02])); + + const result = spawnSync( + process.execPath, + [scriptPath, '--output', outputPath, '--data-inline', JSON.stringify(SAMPLE_DATA)], + { encoding: 'utf8' } + ); + assert.equal(result.status, 0, result.stderr || result.stdout); + + const sourceIcon = path.join( + __dirname, '..', '..', 'skills', 'create-site', 'assets', 'shared', 'power-pages-icon.png' + ); + assert.deepEqual( + fs.readFileSync(iconPath), + fs.readFileSync(sourceIcon), + 'stale icon should have been overwritten with the shared asset' + ); +}); + test('render-cloudflow-plan refuses to overwrite existing file', () => { const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cloudflow-plan-')); const dataPath = path.join(tempDir, 'data.json'); From 0db05b847f83f4148ed4c91a6239b1b91042cd11 Mon Sep 17 00:00:00 2001 From: Priyanshu Agrawal Date: Tue, 28 Apr 2026 19:39:47 +0530 Subject: [PATCH 5/5] Fix formatting in README for MCP servers section --- plugins/power-pages/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/power-pages/README.md b/plugins/power-pages/README.md index 1dd9f01b0..a132d0aff 100644 --- a/plugins/power-pages/README.md +++ b/plugins/power-pages/README.md @@ -236,10 +236,10 @@ The Data Model Architect, Table Permissions, and Web API Settings agents are **r The plugin ships with two MCP servers configured in `.mcp.json` — they start automatically when the plugin loads: -| Server | Purpose | Used by | -|---|---|---| -| **playwright** | Headless browser automation for live previews and runtime tests | `/create-site`, `/test-site` | -| **microsoft-learn** | Grounded search/fetch over official Microsoft Learn docs | `/add-server-logic` | +| Server | Purpose +|---|---| +| **playwright** | Headless browser automation for live previews and runtime tests | +| **microsoft-learn** | Grounded search/fetch over official Microsoft Learn docs | ## Typical Workflow