Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
6e7e827
Add integrate-serverlogic skill for Power Pages Server Logic
Mar 19, 2026
8534ae7
Fix server logic file location and add YAML metadata support
Mar 20, 2026
710354d
Add table permissions phase for Dataverse connector access
Mar 20, 2026
b5b2045
Add web role creation and permissions source choice to server logic s…
Mar 25, 2026
76bc1d5
Merge branch 'main' of https://github.com/microsoft/power-platform-sk…
priyanshu92 Mar 26, 2026
19443f9
Update command references in SKILL.md
priyanshu92 Mar 26, 2026
fa73210
Align server logic skill workflow
priyanshu92 Mar 26, 2026
adc1db3
Update server logic skill workflow and bump plugin to v1.2.0
priyanshu92 Mar 26, 2026
24e209f
Address Copilot review feedback on server logic skill
priyanshu92 Mar 26, 2026
23395fe
Enhance server logic validation and documentation
priyanshu92 Mar 30, 2026
aa1d028
Merge remote-tracking branch 'origin/main' into users/suyashpatel/ser…
Apr 1, 2026
800bdb5
Rename integrate-serverlogic skill to add-server-logic
Apr 1, 2026
176dda0
Fix server-logic validator regex and YAML string quoting
Apr 2, 2026
09e3ae7
Add cloud flow skill with intent-based suggestion and re-integration …
Apr 2, 2026
cbf4369
Add document.title requirement for create-site page components
Apr 2, 2026
ae2d377
Fix serverlogic metadata test to expect single-quoted YAML values
Apr 2, 2026
8c7baa1
Prevent render-template from overwriting existing HTML plan files
Apr 2, 2026
186ef9b
Fix server-logic skill accuracy and add secrets management workflow
Apr 2, 2026
8dfe89a
Add integrate-backend meta skill, fix review findings, add missing tests
Apr 2, 2026
2fb0ae3
Fix CI timeout in create-environment-variable test
Apr 2, 2026
c6114ca
Fix CI timeout in store-keyvault-secret test and update Playwright MC…
Apr 6, 2026
4b1e64c
Add Key Concepts and Implementation Order tabs to backend plan, enabl…
Apr 6, 2026
3d97f8a
Fix PR review findings across server-logic, cloud-flow, and integrate…
Apr 6, 2026
5a3e680
Fix PR review findings: fileSlug validation and list-cloud-flows test…
Apr 6, 2026
d4cae01
Update docs links and improve integrate-backend decision framework
Apr 8, 2026
70bc94b
Add plugin version check script and tests
Apr 8, 2026
c509d1b
Add plugin version check to all SKILL.md files and update conventions
Apr 8, 2026
d167f47
Add CI workflow to enforce version check in SKILL.md files
Apr 8, 2026
f90e7cd
Add Azure Key Vault visualization to server logic HTML plan
Apr 8, 2026
28fb565
Address PR review comments on validator, plan template, and SKILL.md
Apr 8, 2026
e5c7afc
Avoid secret exposure in process listings and fix CI fork safety
Apr 8, 2026
9ae9ec2
Fix innerHTML XSS, unused SITE_NAME in script blocks, and cloudflow U…
Apr 8, 2026
908459f
Fix cloudflow validator: flowtriggerurl is always empty in Power Pages
Apr 8, 2026
261ef57
Add Dataverse custom action discovery and wrapping to server logic skill
Apr 8, 2026
541ca31
Address PR review comments: security, robustness, and correctness fixes
Apr 8, 2026
074bf92
Fix ER diagram coloring for Mermaid v11 SVG ID prefix change
Apr 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion plugins/power-pages/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "power-pages",
"version": "1.1.2",
"version": "1.2.0",
"description": "Create and deploy Power Pages sites using modern development approaches. Supports code sites (SPAs) with React, Angular, Vue, or Astro, with more site types coming soon.",
Comment thread
priyanshu92 marked this conversation as resolved.
"author": {
"name": "Microsoft",
Expand Down
2 changes: 1 addition & 1 deletion plugins/power-pages/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ model: opus
---
```

Note: `allowed-tools` must be a comma-separated list, not JSON array or YAML list syntax.
Note: `allowed-tools` must be a comma-separated list, not JSON array or YAML list syntax. Do not add `hooks` to skill frontmatter; Power Pages skills register lifecycle hooks centrally.

### Key Patterns

Expand Down
3 changes: 3 additions & 0 deletions plugins/power-pages/references/skill-tracking-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ node "${CLAUDE_PLUGIN_ROOT}/scripts/update-skill-tracking.js" --projectRoot "<PR

The script exits silently if `.powerpages-site/site-settings/` does not exist, so it is safe to call unconditionally.

If the tracking script creates or updates site setting YAML files, include those tracking changes in the next git commit for the current phase or final summary commit.

## Skill Name Mapping

| Skill | PascalCase (`--skillName`) | Setting Name |
Expand All @@ -35,6 +37,7 @@ The script exits silently if `.powerpages-site/site-settings/` does not exist, s
| setup-auth | SetupAuth | Site/AI/Skills/SetupAuth |
| test-site | TestSite | Site/AI/Skills/TestSite |
| audit-permissions | AuditPermissions | Site/AI/Skills/AuditPermissions |
| integrate-serverlogic | IntegrateServerlogic | Site/AI/Skills/IntegrateServerlogic |

Comment thread
priyanshu92 marked this conversation as resolved.
## YAML Format

Expand Down
3 changes: 3 additions & 0 deletions plugins/power-pages/scripts/lib/powerpages-hook-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ const TRACKED_SKILLS = {
'create-webroles': {
validatorScript: 'skills/create-webroles/scripts/validate-webroles.js',
},
'integrate-serverlogic': {
validatorScript: 'skills/integrate-serverlogic/scripts/validate-serverlogic.js',
},
'integrate-webapi': {
validatorScript: 'skills/integrate-webapi/scripts/validate-webapi-integration.js',
},
Expand Down
43 changes: 43 additions & 0 deletions plugins/power-pages/scripts/render-serverlogic-plan.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env node
/**
* render-serverlogic-plan.js — Renders the server logic plan HTML from a JSON data file.
*
* Usage:
* node render-serverlogic-plan.js --output <path> --data <json-file>
*
* Required keys in the JSON data file:
* SITE_NAME, PLAN_TITLE, SUMMARY, WEB_ROLES_DATA, SERVER_LOGICS_DATA, RATIONALE_DATA
*/

const path = require('path');
const { renderTemplate, parseArgs } = require('./lib/render-template');

const args = parseArgs(process.argv);

if (!args.output || !args.data) {
console.error(
'Usage: node render-serverlogic-plan.js --output <path> --data <json-file>'
);
process.exit(1);
}

renderTemplate({
templatePath: path.join(
__dirname,
'..',
'skills',
'integrate-serverlogic',
'assets',
'serverlogic-plan.html'
),
outputPath: path.resolve(args.output),
dataPath: path.resolve(args.data),
requiredKeys: [
'SITE_NAME',
'PLAN_TITLE',
'SUMMARY',
'WEB_ROLES_DATA',
'SERVER_LOGICS_DATA',
'RATIONALE_DATA',
],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const { spawnSync } = require('child_process');

const { createTempProject } = require('./test-utils');

function runCreateServerlogicMetadata(args) {
const cliPath = path.join(
__dirname,
'..',
'..',
'skills',
'integrate-serverlogic',
'scripts',
'create-serverlogic-metadata.js'
);

return spawnSync(process.execPath, [cliPath, ...args], {
encoding: 'utf8',
});
}

test('create-serverlogic-metadata writes sorted metadata YAML', (t) => {
const projectRoot = createTempProject(t);
const endpointDir = path.join(projectRoot, '.powerpages-site', 'server-logic', 'ticket-dashboard');
fs.mkdirSync(endpointDir, { recursive: true });
fs.writeFileSync(path.join(endpointDir, 'ticket-dashboard.js'), 'function get() { return "{}"; }\n', 'utf8');

const roleIds = [
'11111111-1111-1111-1111-111111111111',
'22222222-2222-2222-2222-222222222222',
];

const result = runCreateServerlogicMetadata([
'--projectRoot', projectRoot,
'--name', 'ticket-dashboard',
'--displayName', 'Ticket Dashboard',
'--description', 'Server-side dashboard aggregation',
'--webRoleIds', roleIds.join(','),
]);

assert.equal(result.status, 0, result.stderr);

const parsed = JSON.parse(result.stdout);
const yaml = fs.readFileSync(parsed.filePath, 'utf8');

assert.match(parsed.filePath, /ticket-dashboard\.serverlogic\.yml$/);
assert.match(yaml, /^adx_serverlogic_adx_webrole:\n - 11111111-1111-1111-1111-111111111111\n - 22222222-2222-2222-2222-222222222222$/m);
assert.match(yaml, /^description: Server-side dashboard aggregation$/m);
assert.match(yaml, /^display_name: Ticket Dashboard$/m);
assert.match(yaml, /^id: [0-9a-f-]{36}$/m);
assert.match(yaml, /^name: ticket-dashboard$/m);
});

test('create-serverlogic-metadata rejects invalid web role ids', (t) => {
const projectRoot = createTempProject(t);
const endpointDir = path.join(projectRoot, '.powerpages-site', 'server-logic', 'ticket-dashboard');
fs.mkdirSync(endpointDir, { recursive: true });

const result = runCreateServerlogicMetadata([
'--projectRoot', projectRoot,
'--name', 'ticket-dashboard',
'--displayName', 'Ticket Dashboard',
'--description', 'Server-side dashboard aggregation',
'--webRoleIds', 'not-a-uuid',
]);

assert.equal(result.status, 1);
assert.match(result.stderr, /Invalid UUID in --webRoleIds/);
});
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ test('detectTrackedSkill recognizes tracked skill references', () => {
test('detectTrackedSkill recognizes slash command aliases without plugin prefix', () => {
assert.equal(detectTrackedSkill('/create-site'), 'create-site');
assert.equal(detectTrackedSkill('/setup-auth'), 'setup-auth');
assert.equal(detectTrackedSkill('/integrate-serverlogic'), 'integrate-serverlogic');
assert.equal(detectTrackedSkill('/integrate-webapi'), 'integrate-webapi');
assert.equal(detectTrackedSkill('/audit-permissions'), 'audit-permissions');
assert.equal(detectTrackedSkill('/deploy-site'), null);
Expand All @@ -25,6 +26,10 @@ test('detectTrackedSkill recognizes slash command aliases without plugin prefix'
test('getTrackedSkillFromToolInput finds a tracked skill in common fields', () => {
assert.equal(getTrackedSkillFromToolInput({ skill_name: 'create-site' }), 'create-site');
assert.equal(getTrackedSkillFromToolInput({ name: '/power-pages:setup-auth' }), 'setup-auth');
assert.equal(
getTrackedSkillFromToolInput({ command: 'run /power-pages:integrate-serverlogic for this repo' }),
'integrate-serverlogic'
);
assert.equal(
getTrackedSkillFromToolInput({ command: 'run /power-pages:integrate-webapi for this repo' }),
'integrate-webapi'
Expand All @@ -34,6 +39,7 @@ test('getTrackedSkillFromToolInput finds a tracked skill in common fields', () =

test('getValidatorScript returns validator paths only for command-backed skills', () => {
assert.match(getValidatorScript('create-site'), /validate-site\.js$/);
assert.match(getValidatorScript('integrate-serverlogic'), /validate-serverlogic\.js$/);
assert.equal(getValidatorScript('test-site'), null);
assert.equal(getValidatorScript('missing-skill'), null);
});
120 changes: 120 additions & 0 deletions plugins/power-pages/scripts/tests/render-serverlogic-plan.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const { spawnSync } = require('node:child_process');

test('render-serverlogic-plan renders HTML from JSON data', () => {
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'serverlogic-plan-'));
const dataPath = path.join(tempDir, 'data.json');
const outputPath = path.join(tempDir, 'serverlogic-plan.html');
const scriptPath = path.join(
__dirname,
'..',
'render-serverlogic-plan.js'
);

fs.writeFileSync(
dataPath,
JSON.stringify(
{
SITE_NAME: 'Contoso Portal',
PLAN_TITLE: 'Server Logic Plan',
SUMMARY: 'Create multiple server logic items for the support portal while keeping role assignment and function design visible in one reviewable plan.',
WEB_ROLES_DATA: [
{
id: 'authenticated-users',
name: 'Authenticated Users',
desc: 'Built-in role for signed-in users.',
builtin: true,
isNew: false,
color: '#8890a4',
},
{
id: 'support-managers',
name: 'Support Managers',
desc: 'Custom role for elevated support dashboards.',
builtin: false,
isNew: true,
color: '#4a7ce8',
},
],
SERVER_LOGICS_DATA: [
{
id: 'dashboard-summary',
name: 'dashboard-summary',
displayName: 'Dashboard Summary',
status: 'create',
apiUrl: 'https://contoso.powerappsportals.com/_api/serverlogics/dashboard-summary',
webRoles: [
{
id: 'authenticated-users',
reasoning: 'Authenticated users need the dashboard because it surfaces their assigned work and queue metrics.',
},
{
id: 'support-managers',
reasoning: 'Support managers need the same endpoint to review queue-wide workload and escalations.',
},
],
rationale: 'This endpoint centralizes support dashboard aggregation on the server.',
functions: [
{
name: 'get',
purpose: 'Return dashboard summary data',
reasoning: 'The dashboard only reads data, so GET is the right fit.',
},
],
},
{
id: 'case-export',
name: 'case-export',
displayName: 'Case Export',
status: 'reuse',
apiUrl: 'https://contoso.powerappsportals.com/_api/serverlogics/case-export',
webRoles: [
{
id: 'support-managers',
reasoning: 'Only support managers should initiate exports because the payload includes sensitive case data.',
},
],
rationale: 'This existing endpoint is being reused as-is for the export workflow.',
functions: [
{
name: 'post',
purpose: 'Start export job',
reasoning: 'Export initiation changes state, so POST is required.',
},
],
},
],
RATIONALE_DATA: [
{
icon: '🛡️',
title: 'Why this structure',
desc: 'The plan separates reusable endpoints from newly proposed endpoints so review can focus on real changes.',
},
],
},
null,
2
),
'utf8'
);

const result = spawnSync(process.execPath, [scriptPath, '--output', outputPath, '--data', dataPath], {
encoding: 'utf8',
});

assert.equal(result.status, 0, result.stderr || result.stdout);
assert.ok(fs.existsSync(outputPath));

const html = fs.readFileSync(outputPath, 'utf8');
assert.match(html, /Contoso Portal/);
assert.match(html, /dashboard-summary/);
assert.match(html, /Authenticated Users/);
assert.match(html, /Support Managers/);
assert.match(html, /Case Export/);
assert.match(html, /assigned work and queue metrics/);
assert.match(html, /Export initiation changes state/);
});
Loading
Loading