@pega/sdk 0.0.10 → 0.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -30
- package/dist/index.js +116 -125
- package/package.json +1 -1
- package/templates/react-sdk-reference/.ai-common/agents/embedded-flow-expert.md +13 -13
- package/templates/react-sdk-reference/.ai-common/global/ai-agent-instructions-embedded.md +2 -2
- package/templates/react-sdk-reference/.ai-common/global/ai-agent-instructions.md +4 -0
- package/templates/react-sdk-reference/.ai-common/instructions/dse-component.md +1 -1
- package/templates/react-sdk-reference/.ai-common/instructions/embedded-component.md +1 -1
- package/templates/react-sdk-reference/.ai-common/instructions/field-component.md +1 -1
- package/templates/react-sdk-reference/.ai-common/instructions/infra-component.md +1 -1
- package/templates/react-sdk-reference/.ai-common/instructions/template-component.md +1 -1
- package/templates/react-sdk-reference/.ai-common/instructions/widget-component.md +1 -1
- package/templates/react-sdk-reference/.ai-common/prompts/embedded-component-manifest.md +2 -2
- package/templates/react-sdk-reference/.ai-common/prompts/generate-all-components.md +50 -5
- package/templates/react-sdk-reference/.ai-common/prompts/generate-embedded-components.md +10 -11
- package/templates/react-sdk-reference/.ai-common/skills/connect-pega-server.md +84 -31
- package/templates/react-sdk-reference/.ai-common/skills/embedded-flow/analyze-customer-repo.md +11 -2
- package/templates/react-sdk-reference/.ai-common/skills/embedded-flow/wire-integration.md +6 -6
package/README.md
CHANGED
|
@@ -15,10 +15,12 @@ has a project in it:
|
|
|
15
15
|
said while working inside an existing host application) → run in that
|
|
16
16
|
**existing project's folder**.
|
|
17
17
|
|
|
18
|
-
In both cases, run `npx @pega/sdk init` non-interactively — see
|
|
18
|
+
In both cases, run `npx --yes @pega/sdk init` non-interactively — see
|
|
19
19
|
[Non-Interactive / Scripted Usage](#non-interactive--scripted-usage) below for the
|
|
20
20
|
full flag reference. Gather answers conversationally, then invoke one command; do
|
|
21
|
-
not try to drive the interactive prompts through a terminal.
|
|
21
|
+
not try to drive the interactive prompts through a terminal. The leading `npx
|
|
22
|
+
--yes` matters: without it, npx's own install confirmation blocks before this CLI
|
|
23
|
+
even starts.
|
|
22
24
|
|
|
23
25
|
Mode is decided by the folder you run the command in, not something you need to ask
|
|
24
26
|
about or guess:
|
|
@@ -32,13 +34,15 @@ about or guess:
|
|
|
32
34
|
| Decide yourself (don't ask the user) | Ask the user (can't be inferred) |
|
|
33
35
|
| ------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- |
|
|
34
36
|
| `--mode` (auto-detected from whether `package.json` exists in the cwd) | none — these are all optional and not needed to scaffold |
|
|
35
|
-
| `--ai-tool` (
|
|
37
|
+
| `--ai-tool` (set it to yourself: GitHub Copilot → `copilot`, Cursor → `cursor`, Claude Code → `claude-code`) | |
|
|
36
38
|
| `--framework`, `--name`, `--templates`, `--categories` (infer from the request, or ask only if genuinely ambiguous) | |
|
|
37
39
|
|
|
38
|
-
|
|
39
|
-
are
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
Server connection details (Pega server URL, app alias, OAuth client ID, MCP rule
|
|
41
|
+
name/instance key) are never asked at scaffold time, in either mode — the scaffold
|
|
42
|
+
always leaves `sdk-config.json` blank and the generated MCP config file with its
|
|
43
|
+
placeholder markers. An AI coding assistant gathers these from the user in chat
|
|
44
|
+
and fills both files in automatically the first time a workflow needs them (see
|
|
45
|
+
[Configure Your Pega Server](#configure-your-pega-server)).
|
|
42
46
|
|
|
43
47
|
## Requirements
|
|
44
48
|
|
|
@@ -46,6 +50,22 @@ config file directly (see [Configure Your Pega Server](#configure-your-pega-serv
|
|
|
46
50
|
- **npm** 9 or later
|
|
47
51
|
- Access to a Pega Infinity server (for running the generated app)
|
|
48
52
|
|
|
53
|
+
## Non-Interactive / Scripted Usage
|
|
54
|
+
|
|
55
|
+
Pass any flag to `init` to scaffold in one shot, with no prompts:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# New project
|
|
59
|
+
npx --yes @pega/sdk init --name my-app --framework react --ai-tool copilot --templates false
|
|
60
|
+
|
|
61
|
+
# Embedded flow, inside an existing project
|
|
62
|
+
npx --yes @pega/sdk init --mode embedded --ai-tool cursor --conflict-policy merge
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Run `npx --yes @pega/sdk init --help` for the full flag reference (`--help` also
|
|
66
|
+
works without the `init` subcommand). On invalid or missing required input, the
|
|
67
|
+
CLI prints an error to stderr and exits with a non-zero code — no hanging prompt.
|
|
68
|
+
|
|
49
69
|
## Quick Start
|
|
50
70
|
|
|
51
71
|
```bash
|
|
@@ -94,27 +114,10 @@ If you skip starter components in a React project, the scaffold uses a minimal
|
|
|
94
114
|
dependency manifest for a lighter baseline app.
|
|
95
115
|
|
|
96
116
|
Server connection details (Pega server URL, app alias, OAuth client ID) and MCP
|
|
97
|
-
rule details are
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
Pass any flag to `init` (or `--json`) to scaffold in one shot, with no prompts:
|
|
103
|
-
|
|
104
|
-
```bash
|
|
105
|
-
# New project
|
|
106
|
-
npx @pega/sdk init --name my-app --framework react --ai-tool copilot --templates false --yes
|
|
107
|
-
|
|
108
|
-
# Embedded flow, inside an existing project
|
|
109
|
-
npx @pega/sdk init --mode embedded --ai-tool copilot --conflict-policy merge
|
|
110
|
-
|
|
111
|
-
# Or supply everything via a JSON answers file (values here win over individual flags)
|
|
112
|
-
npx @pega/sdk init --json ./sdk-answers.json
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
Run `npx @pega/sdk init --help` for the full flag reference. On invalid or
|
|
116
|
-
missing required input, the CLI prints an error to stderr and exits with a
|
|
117
|
-
non-zero code — no hanging prompt.
|
|
117
|
+
rule details are never asked — neither interactively nor as CLI flags. Configure
|
|
118
|
+
them after scaffolding, either by asking your AI coding assistant (it gathers them
|
|
119
|
+
in chat and fills in both files automatically) or by editing `sdk-config.json` and
|
|
120
|
+
the generated MCP config file yourself (see below).
|
|
118
121
|
|
|
119
122
|
## What You Get
|
|
120
123
|
|
|
@@ -129,8 +132,31 @@ The generated project is ready to run and includes:
|
|
|
129
132
|
|
|
130
133
|
## Configure Your Pega Server
|
|
131
134
|
|
|
132
|
-
|
|
133
|
-
|
|
135
|
+
The scaffolded project ships with `sdk-config.json` and a generated MCP config file
|
|
136
|
+
both intentionally blank/placeholder — connection details are never collected at
|
|
137
|
+
scaffold time. The same setup applies to full-portal and embedded projects.
|
|
138
|
+
|
|
139
|
+
**AI coding assistant (recommended):** the first workflow that needs a live Pega
|
|
140
|
+
server checks the active `sdk-config.json`, the generated MCP config, and project-root
|
|
141
|
+
`.env*` files. It asks once for every missing non-secret value. You can provide those
|
|
142
|
+
values in chat or update an allowed file yourself; the assistant then synchronizes
|
|
143
|
+
all applicable destinations and asks again only for values that remain missing.
|
|
144
|
+
|
|
145
|
+
**Manual or environment setup:** configure the following non-secret values in any
|
|
146
|
+
applicable source:
|
|
147
|
+
|
|
148
|
+
| Value | Environment key example | `sdk-config.json` | MCP config |
|
|
149
|
+
| --------------------- | ----------------------- | ----------------------------------------------------------- | -------------------------------------- |
|
|
150
|
+
| Pega server URL | `PEGA_INFINITY_URL` | `serverConfig.infinityRestServerUrl` | `{InfinityURL}` in the URL |
|
|
151
|
+
| Application alias | `PEGA_APP_ALIAS` | `serverConfig.appAlias` | `{AppAlias}` in the URL |
|
|
152
|
+
| MCP rule name | `PEGA_MCP_RULE_NAME` | — | `{MCPRuleName}` in the server key |
|
|
153
|
+
| MCP rule instance key | `PEGA_MCP_RULE_INS_KEY` | — | `{MCPRuleInsKey}` in the URL |
|
|
154
|
+
| OAuth client ID | `PEGA_CLIENT_ID` | `authConfig.mashupClientId` and `authConfig.portalClientId` | Cursor's `auth.clientId`, when present |
|
|
155
|
+
|
|
156
|
+
For full-portal projects, use the scaffolded application `sdk-config.json`. For
|
|
157
|
+
embedded projects, use the copy in the host framework's browser-served static
|
|
158
|
+
directory (for example `public/sdk-config.json`), not a stale same-named root copy.
|
|
159
|
+
Its relevant structure is:
|
|
134
160
|
|
|
135
161
|
```jsonc
|
|
136
162
|
{
|
|
@@ -153,6 +179,16 @@ environment details:
|
|
|
153
179
|
- `appAlias` — the application alias to load
|
|
154
180
|
- `mashupClientId` / `portalClientId` — OAuth 2.0 client IDs registered in Pega
|
|
155
181
|
|
|
182
|
+
The generated MCP file is `.vscode/mcp.json` for Copilot, `.cursor/mcp.json` for
|
|
183
|
+
Cursor, or `.mcp.json` for Claude Code. Copilot and Claude Code use URL-only MCP
|
|
184
|
+
configuration and do not require a client secret. Cursor's authenticated MCP entry
|
|
185
|
+
also requires `auth.clientId` and `auth.clientSecret`. The Pega MCP client secret
|
|
186
|
+
must come from a private project-root environment variable such as
|
|
187
|
+
`PEGA_CLIENT_SECRET`; never provide it in chat, use a browser-exposed environment
|
|
188
|
+
name (`VITE_*`, `NEXT_PUBLIC_*`, `REACT_APP_*`, or `EXPO_PUBLIC_*`), or put it in
|
|
189
|
+
browser-served `sdk-config.json`. The assistant reads the private environment value
|
|
190
|
+
and writes it only to Cursor's `auth.clientSecret`.
|
|
191
|
+
|
|
156
192
|
## Available Scripts
|
|
157
193
|
|
|
158
194
|
Inside the generated project:
|
package/dist/index.js
CHANGED
|
@@ -1020,16 +1020,9 @@ function generateAiConfigs(targetDir, choices) {
|
|
|
1020
1020
|
if (choices.aiTool) {
|
|
1021
1021
|
applyPlannedFiles(planAiConfigs(aiCommonDir, targetDir, choices.aiTool), "override", { markers: false });
|
|
1022
1022
|
}
|
|
1023
|
-
if (choices.aiTool
|
|
1024
|
-
const { config: mcpConfig, destRelPath: mcpDestRelPath } = resolveMcpConfig(aiCommonDir,
|
|
1025
|
-
|
|
1026
|
-
serverUrl: choices.serverUrl,
|
|
1027
|
-
appAlias: choices.appAlias,
|
|
1028
|
-
clientId: choices.clientId,
|
|
1029
|
-
mcpRuleName: choices.mcpRuleName,
|
|
1030
|
-
mcpRuleInsKey: choices.mcpRuleInsKey
|
|
1031
|
-
});
|
|
1032
|
-
writeManagedJson(path2.join(targetDir, mcpDestRelPath), mcpConfig, "override", MCP_CONFIG_USER_KEYS);
|
|
1023
|
+
if (choices.aiTool) {
|
|
1024
|
+
const { config: mcpConfig, destRelPath: mcpDestRelPath } = resolveMcpConfig(aiCommonDir, choices.aiTool);
|
|
1025
|
+
writeManagedJson(path2.join(targetDir, mcpDestRelPath), mcpConfig, "override");
|
|
1033
1026
|
if (choices.aiTool === "cursor") {
|
|
1034
1027
|
ensureGitignoreEntry(targetDir, mcpDestRelPath);
|
|
1035
1028
|
}
|
|
@@ -1046,27 +1039,18 @@ function resolveEmbeddedSource() {
|
|
|
1046
1039
|
}
|
|
1047
1040
|
return { sampleDir, aiCommonSrc };
|
|
1048
1041
|
}
|
|
1049
|
-
var SDK_CONFIG_USER_KEYS = [];
|
|
1050
|
-
var MCP_CONFIG_USER_KEYS = [];
|
|
1051
1042
|
var MCP_CONFIG_FILES = {
|
|
1052
1043
|
copilot: { source: "copilot.mcp.json", dest: path2.join(".vscode", "mcp.json") },
|
|
1053
1044
|
cursor: { source: "cursor.mcp.json", dest: path2.join(".cursor", "mcp.json") },
|
|
1054
1045
|
"claude-code": { source: "claude-code.mcp.json", dest: ".mcp.json" }
|
|
1055
1046
|
};
|
|
1056
|
-
function resolveMcpConfig(aiCommonSrc,
|
|
1057
|
-
const { source, dest } = MCP_CONFIG_FILES[
|
|
1047
|
+
function resolveMcpConfig(aiCommonSrc, aiTool) {
|
|
1048
|
+
const { source, dest } = MCP_CONFIG_FILES[aiTool];
|
|
1058
1049
|
const srcPath = path2.join(aiCommonSrc, "mcp", source);
|
|
1059
1050
|
if (!fs2.existsSync(srcPath)) {
|
|
1060
1051
|
throw new Error(`MCP config template not found at ${srcPath}. Ensure the template source is available.`);
|
|
1061
1052
|
}
|
|
1062
|
-
|
|
1063
|
-
if (choices.serverUrl) tokens["{InfinityURL}"] = choices.serverUrl;
|
|
1064
|
-
if (choices.appAlias) tokens["{AppAlias}"] = choices.appAlias;
|
|
1065
|
-
if (choices.clientId) tokens["{ClientId}"] = choices.clientId;
|
|
1066
|
-
if (choices.mcpRuleName) tokens["{MCPRuleName}"] = choices.mcpRuleName;
|
|
1067
|
-
if (choices.mcpRuleInsKey) tokens["{MCPRuleInsKey}"] = choices.mcpRuleInsKey;
|
|
1068
|
-
const content = applyTokens(fs2.readFileSync(srcPath, "utf-8"), tokens);
|
|
1069
|
-
return { config: JSON.parse(content), destRelPath: dest };
|
|
1053
|
+
return { config: JSON.parse(fs2.readFileSync(srcPath, "utf-8")), destRelPath: dest };
|
|
1070
1054
|
}
|
|
1071
1055
|
function ensureGitignoreEntry(targetDir, entry) {
|
|
1072
1056
|
const gitignorePath = path2.join(targetDir, ".gitignore");
|
|
@@ -1075,11 +1059,11 @@ function ensureGitignoreEntry(targetDir, entry) {
|
|
|
1075
1059
|
const separator = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
1076
1060
|
fs2.writeFileSync(gitignorePath, existing + separator + entry + "\n");
|
|
1077
1061
|
}
|
|
1078
|
-
function buildSdkConfig(
|
|
1062
|
+
function buildSdkConfig() {
|
|
1079
1063
|
return {
|
|
1080
1064
|
serverConfig: {
|
|
1081
|
-
infinityRestServerUrl:
|
|
1082
|
-
appAlias:
|
|
1065
|
+
infinityRestServerUrl: "",
|
|
1066
|
+
appAlias: "",
|
|
1083
1067
|
sdkContentServerUrl: "",
|
|
1084
1068
|
appPortal: "",
|
|
1085
1069
|
appMashupCaseType: "",
|
|
@@ -1094,10 +1078,10 @@ function buildSdkConfig(choices) {
|
|
|
1094
1078
|
},
|
|
1095
1079
|
authConfig: {
|
|
1096
1080
|
authService: "pega",
|
|
1097
|
-
mashupClientId:
|
|
1081
|
+
mashupClientId: "",
|
|
1098
1082
|
mashupUserIdentifier: "",
|
|
1099
1083
|
mashupPassword: "",
|
|
1100
|
-
portalClientId:
|
|
1084
|
+
portalClientId: "",
|
|
1101
1085
|
portalClientSecret: "",
|
|
1102
1086
|
portalGrantType: ""
|
|
1103
1087
|
}
|
|
@@ -1115,12 +1099,12 @@ function collectEmbeddedConflicts(targetDir, choices) {
|
|
|
1115
1099
|
}
|
|
1116
1100
|
}).map((file) => file.destPath);
|
|
1117
1101
|
const sdkConfigPath = path2.join(resolveStaticDir(targetDir), "sdk-config.json");
|
|
1118
|
-
if (isConflict(sdkConfigPath, buildSdkConfig(
|
|
1102
|
+
if (isConflict(sdkConfigPath, buildSdkConfig())) {
|
|
1119
1103
|
conflicts.push(sdkConfigPath);
|
|
1120
1104
|
}
|
|
1121
|
-
const { config: mcpConfig, destRelPath: mcpDestRelPath } = resolveMcpConfig(aiCommonSrc, choices);
|
|
1105
|
+
const { config: mcpConfig, destRelPath: mcpDestRelPath } = resolveMcpConfig(aiCommonSrc, choices.aiTool);
|
|
1122
1106
|
const mcpConfigPath = path2.join(targetDir, mcpDestRelPath);
|
|
1123
|
-
if (isConflict(mcpConfigPath, mcpConfig
|
|
1107
|
+
if (isConflict(mcpConfigPath, mcpConfig)) {
|
|
1124
1108
|
conflicts.push(mcpConfigPath);
|
|
1125
1109
|
}
|
|
1126
1110
|
return conflicts.map((filePath) => path2.relative(targetDir, filePath));
|
|
@@ -1169,13 +1153,13 @@ async function scaffoldEmbedded(choices, targetDir, onProgress) {
|
|
|
1169
1153
|
report("Creating SDK configuration\u2026");
|
|
1170
1154
|
const staticDir = ensureStaticDir(targetDir);
|
|
1171
1155
|
const sdkConfigPath = path2.join(staticDir, "sdk-config.json");
|
|
1172
|
-
writeManagedJson(sdkConfigPath, buildSdkConfig(
|
|
1156
|
+
writeManagedJson(sdkConfigPath, buildSdkConfig(), policy);
|
|
1173
1157
|
if (staticDir !== targetDir && fs2.existsSync(path2.join(targetDir, "sdk-config.json"))) {
|
|
1174
1158
|
report(`\u26A0\uFE0F A sdk-config.json also exists at the project root. The active one is ${sdkConfigPath}.`);
|
|
1175
1159
|
}
|
|
1176
1160
|
report("Creating MCP server configuration\u2026");
|
|
1177
|
-
const { config: mcpConfig, destRelPath: mcpDestRelPath } = resolveMcpConfig(aiCommonSrc,
|
|
1178
|
-
writeManagedJson(path2.join(targetDir, mcpDestRelPath), mcpConfig, policy
|
|
1161
|
+
const { config: mcpConfig, destRelPath: mcpDestRelPath } = resolveMcpConfig(aiCommonSrc, aiTool);
|
|
1162
|
+
writeManagedJson(path2.join(targetDir, mcpDestRelPath), mcpConfig, policy);
|
|
1179
1163
|
if (aiTool === "cursor") {
|
|
1180
1164
|
ensureGitignoreEntry(targetDir, mcpDestRelPath);
|
|
1181
1165
|
}
|
|
@@ -1250,23 +1234,46 @@ var AI_TOOLS = ["copilot", "cursor", "claude-code"];
|
|
|
1250
1234
|
var PROJECT_NAME_RE = /^[a-z0-9@][a-z0-9._/-]*$/;
|
|
1251
1235
|
var CliValidationError = class extends Error {
|
|
1252
1236
|
};
|
|
1237
|
+
var AI_TOOL_CHOICES = AI_TOOLS.join("|");
|
|
1238
|
+
var FRAMEWORK_CHOICES = FRAMEWORKS.join("|");
|
|
1239
|
+
var AGENT_QUICKSTART = `
|
|
1240
|
+
AI coding agents (GitHub Copilot, Cursor, Claude Code, or any other): do not answer
|
|
1241
|
+
this CLI's interactive prompts. Run it non-interactively instead \u2014 one command, no
|
|
1242
|
+
prompts, no keystrokes to drive:
|
|
1243
|
+
|
|
1244
|
+
# Inside an existing project (package.json present) \u2014 embedded flow
|
|
1245
|
+
npx --yes @pega/sdk init --ai-tool <${AI_TOOL_CHOICES}>
|
|
1246
|
+
|
|
1247
|
+
# In an empty folder \u2014 new project
|
|
1248
|
+
npx --yes @pega/sdk init --name <project-name> --framework <${FRAMEWORK_CHOICES}> --ai-tool <${AI_TOOL_CHOICES}>
|
|
1249
|
+
|
|
1250
|
+
Mode is auto-detected from whether package.json exists in the current folder \u2014 you
|
|
1251
|
+
do not need to pass --mode.
|
|
1252
|
+
|
|
1253
|
+
--ai-tool is you, the agent running this command \u2014 not something to detect or ask
|
|
1254
|
+
about. GitHub Copilot -> copilot, Cursor -> cursor, Claude Code -> claude-code.
|
|
1255
|
+
Always pass it: it decides which agent config files get written, and a repo can
|
|
1256
|
+
contain another assistant's config left behind by someone else.
|
|
1257
|
+
`.trim();
|
|
1253
1258
|
var HELP_TEXT = `
|
|
1254
|
-
Usage: npx @pega/sdk init [flags]
|
|
1259
|
+
Usage: npx --yes @pega/sdk init [flags]
|
|
1255
1260
|
|
|
1256
|
-
Run with no flags for the interactive setup
|
|
1257
|
-
to scaffold non-interactively in one shot
|
|
1261
|
+
Scaffolds a Pega SDK application. Run with no flags for the interactive setup,
|
|
1262
|
+
or pass any flag below to scaffold non-interactively in one shot.
|
|
1263
|
+
|
|
1264
|
+
Mode is auto-detected from the folder you run in: an empty folder creates a new
|
|
1265
|
+
project, a folder that already has a package.json integrates the embedded flow.
|
|
1258
1266
|
|
|
1259
1267
|
Common:
|
|
1260
|
-
--
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
--
|
|
1264
|
-
--
|
|
1265
|
-
--help Show this help.
|
|
1268
|
+
--mode <new|embedded> Override the auto-detection described above.
|
|
1269
|
+
--ai-tool <tool> ${AI_TOOLS.join(" | ")} (new: also accepts "none").
|
|
1270
|
+
Adds that assistant's config and skill files.
|
|
1271
|
+
--yes Accept all defaults; enough on its own to skip every prompt.
|
|
1272
|
+
--help, -h Show this help.
|
|
1266
1273
|
|
|
1267
1274
|
New project:
|
|
1268
1275
|
--name <name> Project directory name (required).
|
|
1269
|
-
--framework <fw>
|
|
1276
|
+
--framework <fw> ${FRAMEWORKS.join(" | ")}. Default: react.
|
|
1270
1277
|
--templates <bool> Include starter components. Default: true.
|
|
1271
1278
|
--categories <list> Comma-separated: fields,templates,widgets,infra,designSystemExtension.
|
|
1272
1279
|
Default: all categories when --templates is true.
|
|
@@ -1274,20 +1281,17 @@ New project:
|
|
|
1274
1281
|
Embedded:
|
|
1275
1282
|
--conflict-policy <p> merge | override. Default: merge.
|
|
1276
1283
|
|
|
1277
|
-
Optional, either mode (CI/scripted use only \u2014 a chat agent should not ask a
|
|
1278
|
-
user for these; they're configured post-scaffold by editing sdk-config.json /
|
|
1279
|
-
the generated MCP config file):
|
|
1280
|
-
--server-url <url>
|
|
1281
|
-
--app-alias <alias>
|
|
1282
|
-
--client-id <id>
|
|
1283
|
-
--mcp-rule-name <name>
|
|
1284
|
-
--mcp-rule-ins-key <key>
|
|
1285
|
-
|
|
1286
1284
|
Examples:
|
|
1287
|
-
npx @pega/sdk init --name my-app --framework react --ai-tool copilot --templates false
|
|
1288
|
-
npx @pega/sdk init --mode embedded --ai-tool
|
|
1289
|
-
|
|
1285
|
+
npx --yes @pega/sdk init --name my-app --framework react --ai-tool copilot --templates false
|
|
1286
|
+
npx --yes @pega/sdk init --mode embedded --ai-tool cursor --conflict-policy merge
|
|
1287
|
+
|
|
1288
|
+
------------------------------------------------------------------------------
|
|
1289
|
+
${AGENT_QUICKSTART}
|
|
1290
1290
|
`.trim();
|
|
1291
|
+
var HELP_FLAGS = /* @__PURE__ */ new Set(["--help", "-h", "-help", "--h", "-?"]);
|
|
1292
|
+
function isHelpRequest(argv) {
|
|
1293
|
+
return argv[0] === "help" || argv.some((arg) => HELP_FLAGS.has(arg));
|
|
1294
|
+
}
|
|
1291
1295
|
function parseCliArgs(argv) {
|
|
1292
1296
|
let values;
|
|
1293
1297
|
try {
|
|
@@ -1295,17 +1299,11 @@ function parseCliArgs(argv) {
|
|
|
1295
1299
|
args: argv,
|
|
1296
1300
|
options: {
|
|
1297
1301
|
help: { type: "boolean" },
|
|
1298
|
-
json: { type: "string" },
|
|
1299
1302
|
yes: { type: "boolean" },
|
|
1300
1303
|
mode: { type: "string" },
|
|
1301
1304
|
name: { type: "string" },
|
|
1302
1305
|
framework: { type: "string" },
|
|
1303
1306
|
"ai-tool": { type: "string" },
|
|
1304
|
-
"server-url": { type: "string" },
|
|
1305
|
-
"app-alias": { type: "string" },
|
|
1306
|
-
"client-id": { type: "string" },
|
|
1307
|
-
"mcp-rule-name": { type: "string" },
|
|
1308
|
-
"mcp-rule-ins-key": { type: "string" },
|
|
1309
1307
|
templates: { type: "string" },
|
|
1310
1308
|
categories: { type: "string" },
|
|
1311
1309
|
"conflict-policy": { type: "string" }
|
|
@@ -1318,17 +1316,11 @@ function parseCliArgs(argv) {
|
|
|
1318
1316
|
}
|
|
1319
1317
|
return {
|
|
1320
1318
|
help: Boolean(values.help),
|
|
1321
|
-
json: values.json,
|
|
1322
1319
|
yes: Boolean(values.yes),
|
|
1323
1320
|
mode: values.mode,
|
|
1324
1321
|
name: values.name,
|
|
1325
1322
|
framework: values.framework,
|
|
1326
1323
|
aiTool: values["ai-tool"],
|
|
1327
|
-
serverUrl: values["server-url"],
|
|
1328
|
-
appAlias: values["app-alias"],
|
|
1329
|
-
clientId: values["client-id"],
|
|
1330
|
-
mcpRuleName: values["mcp-rule-name"],
|
|
1331
|
-
mcpRuleInsKey: values["mcp-rule-ins-key"],
|
|
1332
1324
|
templates: values.templates,
|
|
1333
1325
|
categories: values.categories,
|
|
1334
1326
|
conflictPolicy: values["conflict-policy"]
|
|
@@ -1336,23 +1328,13 @@ function parseCliArgs(argv) {
|
|
|
1336
1328
|
}
|
|
1337
1329
|
function isNonInteractive(args) {
|
|
1338
1330
|
return Boolean(
|
|
1339
|
-
args.help || args.
|
|
1331
|
+
args.help || args.mode || args.name || args.framework || args.aiTool || args.templates || args.categories || args.conflictPolicy || args.yes
|
|
1340
1332
|
);
|
|
1341
1333
|
}
|
|
1342
|
-
function readJsonAnswers(source) {
|
|
1343
|
-
const raw = source === "-" ? fs3.readFileSync(0, "utf-8") : fs3.readFileSync(path3.resolve(source), "utf-8");
|
|
1344
|
-
try {
|
|
1345
|
-
return JSON.parse(raw);
|
|
1346
|
-
} catch {
|
|
1347
|
-
throw new CliValidationError(`--json source "${source}" is not valid JSON.`);
|
|
1348
|
-
}
|
|
1349
|
-
}
|
|
1350
|
-
function pickFrom(answers, key, flagValue) {
|
|
1351
|
-
return answers[key] ?? flagValue;
|
|
1352
|
-
}
|
|
1353
1334
|
function assertFramework(value) {
|
|
1335
|
+
if (value === void 0) return "react";
|
|
1354
1336
|
if (typeof value === "string" && FRAMEWORKS.includes(value)) return value;
|
|
1355
|
-
throw new CliValidationError(`--framework
|
|
1337
|
+
throw new CliValidationError(`--framework must be one of: ${FRAMEWORKS.join(", ")}`);
|
|
1356
1338
|
}
|
|
1357
1339
|
function parseAiTool(value) {
|
|
1358
1340
|
if (value === void 0 || value === "none") return null;
|
|
@@ -1362,8 +1344,19 @@ function parseAiTool(value) {
|
|
|
1362
1344
|
function parseRequiredAiTool(value) {
|
|
1363
1345
|
if (typeof value === "string" && AI_TOOLS.includes(value)) return value;
|
|
1364
1346
|
throw new CliValidationError(
|
|
1365
|
-
`--ai-tool
|
|
1347
|
+
`--ai-tool must be one of: ${AI_TOOLS.join(", ")}. Pass the assistant you are running as.`
|
|
1348
|
+
);
|
|
1349
|
+
}
|
|
1350
|
+
var AI_TOOL_MARKERS = {
|
|
1351
|
+
copilot: [".github/copilot-instructions.md", ".github/instructions", ".github/prompts", ".github/skills"],
|
|
1352
|
+
cursor: [".cursor", ".cursorrules"],
|
|
1353
|
+
"claude-code": [".claude", "CLAUDE.md"]
|
|
1354
|
+
};
|
|
1355
|
+
function detectRepoAiTool(cwd) {
|
|
1356
|
+
const matches = Object.keys(AI_TOOL_MARKERS).filter(
|
|
1357
|
+
(tool) => AI_TOOL_MARKERS[tool].some((marker) => fs3.existsSync(path3.join(cwd, marker)))
|
|
1366
1358
|
);
|
|
1359
|
+
return matches.length === 1 ? matches[0] : void 0;
|
|
1367
1360
|
}
|
|
1368
1361
|
function parseBoolean(value, fallback) {
|
|
1369
1362
|
if (value === void 0) return fallback;
|
|
@@ -1384,36 +1377,36 @@ function parseCategories(value) {
|
|
|
1384
1377
|
return entries;
|
|
1385
1378
|
}
|
|
1386
1379
|
function resolveNonInteractiveUserChoices(args) {
|
|
1387
|
-
const
|
|
1388
|
-
const projectName = pickFrom(answers, "projectName", args.name);
|
|
1380
|
+
const projectName = args.name;
|
|
1389
1381
|
if (typeof projectName !== "string" || !projectName) {
|
|
1390
|
-
throw new CliValidationError(
|
|
1382
|
+
throw new CliValidationError("Project name is required (--name).");
|
|
1391
1383
|
}
|
|
1392
1384
|
if (!PROJECT_NAME_RE.test(projectName)) {
|
|
1393
1385
|
throw new CliValidationError("Invalid project name. Use lowercase letters, numbers, hyphens, and dots.");
|
|
1394
1386
|
}
|
|
1395
|
-
const framework = assertFramework(
|
|
1396
|
-
const aiTool = parseAiTool(
|
|
1387
|
+
const framework = assertFramework(args.framework);
|
|
1388
|
+
const aiTool = parseAiTool(args.aiTool);
|
|
1397
1389
|
const isReactNative = framework === "react-native";
|
|
1398
|
-
const includeTemplates = isReactNative ? true : parseBoolean(
|
|
1399
|
-
const componentCategories = isReactNative ? ["fields", "templates", "infra"] : includeTemplates ? parseCategories(
|
|
1390
|
+
const includeTemplates = isReactNative ? true : parseBoolean(args.templates, true);
|
|
1391
|
+
const componentCategories = isReactNative ? ["fields", "templates", "infra"] : includeTemplates ? parseCategories(args.categories) : [];
|
|
1400
1392
|
return {
|
|
1401
1393
|
projectName,
|
|
1402
1394
|
framework,
|
|
1403
1395
|
aiTool,
|
|
1404
1396
|
includeTemplates,
|
|
1405
|
-
componentCategories
|
|
1406
|
-
serverUrl: pickFrom(answers, "serverUrl", args.serverUrl),
|
|
1407
|
-
appAlias: pickFrom(answers, "appAlias", args.appAlias),
|
|
1408
|
-
clientId: pickFrom(answers, "clientId", args.clientId),
|
|
1409
|
-
mcpRuleName: pickFrom(answers, "mcpRuleName", args.mcpRuleName),
|
|
1410
|
-
mcpRuleInsKey: pickFrom(answers, "mcpRuleInsKey", args.mcpRuleInsKey)
|
|
1397
|
+
componentCategories
|
|
1411
1398
|
};
|
|
1412
1399
|
}
|
|
1413
|
-
function resolveNonInteractiveEmbeddedChoices(args) {
|
|
1414
|
-
const
|
|
1415
|
-
const
|
|
1416
|
-
|
|
1400
|
+
function resolveNonInteractiveEmbeddedChoices(args, cwd) {
|
|
1401
|
+
const requestedAiTool = args.aiTool;
|
|
1402
|
+
const inferredAiTool = requestedAiTool === void 0 ? detectRepoAiTool(cwd) : void 0;
|
|
1403
|
+
if (inferredAiTool) {
|
|
1404
|
+
console.warn(
|
|
1405
|
+
`Warning: --ai-tool was not passed; guessed "${inferredAiTool}" from this repo's existing config. If you are a different assistant, re-run with --ai-tool set to yourself.`
|
|
1406
|
+
);
|
|
1407
|
+
}
|
|
1408
|
+
const aiTool = parseRequiredAiTool(requestedAiTool ?? inferredAiTool);
|
|
1409
|
+
const conflictPolicyRaw = args.conflictPolicy;
|
|
1417
1410
|
let conflictPolicy;
|
|
1418
1411
|
if (conflictPolicyRaw !== void 0) {
|
|
1419
1412
|
if (conflictPolicyRaw !== "merge" && conflictPolicyRaw !== "override") {
|
|
@@ -1423,11 +1416,6 @@ function resolveNonInteractiveEmbeddedChoices(args) {
|
|
|
1423
1416
|
}
|
|
1424
1417
|
return {
|
|
1425
1418
|
aiTool,
|
|
1426
|
-
serverUrl: pickFrom(answers, "serverUrl", args.serverUrl),
|
|
1427
|
-
appAlias: pickFrom(answers, "appAlias", args.appAlias),
|
|
1428
|
-
clientId: pickFrom(answers, "clientId", args.clientId),
|
|
1429
|
-
mcpRuleName: pickFrom(answers, "mcpRuleName", args.mcpRuleName),
|
|
1430
|
-
mcpRuleInsKey: pickFrom(answers, "mcpRuleInsKey", args.mcpRuleInsKey),
|
|
1431
1419
|
conflictPolicy
|
|
1432
1420
|
};
|
|
1433
1421
|
}
|
|
@@ -1435,16 +1423,11 @@ function resolveNonInteractiveEmbeddedChoices(args) {
|
|
|
1435
1423
|
// src/index.ts
|
|
1436
1424
|
var require2 = createRequire(import.meta.url);
|
|
1437
1425
|
var { version: sdkVersion } = require2("../package.json");
|
|
1438
|
-
async function runNonInteractive(cwd,
|
|
1439
|
-
const flags = parseCliArgs(rawArgs);
|
|
1440
|
-
if (flags.help) {
|
|
1441
|
-
console.log(HELP_TEXT);
|
|
1442
|
-
return;
|
|
1443
|
-
}
|
|
1426
|
+
async function runNonInteractive(cwd, flags) {
|
|
1444
1427
|
try {
|
|
1445
1428
|
const mode = flags.mode ?? (detectExistingProject(cwd) ? "embedded" : "new");
|
|
1446
1429
|
if (mode === "embedded") {
|
|
1447
|
-
const choices = resolveNonInteractiveEmbeddedChoices(flags);
|
|
1430
|
+
const choices = resolveNonInteractiveEmbeddedChoices(flags, cwd);
|
|
1448
1431
|
const conflicts = collectEmbeddedConflicts(cwd, choices);
|
|
1449
1432
|
if (conflicts.length > 0) {
|
|
1450
1433
|
console.log(
|
|
@@ -1463,22 +1446,40 @@ async function runNonInteractive(cwd, rawArgs) {
|
|
|
1463
1446
|
}
|
|
1464
1447
|
} catch (err) {
|
|
1465
1448
|
console.error(pc.red(err instanceof CliValidationError || err instanceof Error ? err.message : String(err)));
|
|
1449
|
+
console.error(`
|
|
1450
|
+
${AGENT_QUICKSTART}
|
|
1451
|
+
|
|
1452
|
+
Full flag reference: npx --yes @pega/sdk init --help`);
|
|
1466
1453
|
process.exit(1);
|
|
1467
1454
|
}
|
|
1468
1455
|
}
|
|
1469
1456
|
async function main() {
|
|
1470
1457
|
const args = process.argv.slice(2);
|
|
1458
|
+
if (isHelpRequest(args)) {
|
|
1459
|
+
console.log(HELP_TEXT);
|
|
1460
|
+
return;
|
|
1461
|
+
}
|
|
1471
1462
|
const command = args[0];
|
|
1472
1463
|
if (command !== "init") {
|
|
1473
1464
|
console.error(`Unknown command "${command ?? ""}".
|
|
1474
|
-
|
|
1475
|
-
|
|
1465
|
+
`);
|
|
1466
|
+
console.error(HELP_TEXT);
|
|
1476
1467
|
process.exit(1);
|
|
1477
1468
|
}
|
|
1478
1469
|
const cwd = process.cwd();
|
|
1479
1470
|
const rest = args.slice(1);
|
|
1480
|
-
|
|
1481
|
-
|
|
1471
|
+
let flags;
|
|
1472
|
+
try {
|
|
1473
|
+
flags = parseCliArgs(rest);
|
|
1474
|
+
} catch (err) {
|
|
1475
|
+
console.error(pc.red(err instanceof Error ? err.message : String(err)));
|
|
1476
|
+
console.error(`
|
|
1477
|
+
${HELP_TEXT}`);
|
|
1478
|
+
process.exit(1);
|
|
1479
|
+
return;
|
|
1480
|
+
}
|
|
1481
|
+
if (isNonInteractive(flags)) {
|
|
1482
|
+
await runNonInteractive(cwd, flags);
|
|
1482
1483
|
return;
|
|
1483
1484
|
}
|
|
1484
1485
|
console.clear();
|
|
@@ -1631,11 +1632,6 @@ Usage: npx @pega/sdk init`);
|
|
|
1631
1632
|
process.exit(0);
|
|
1632
1633
|
}
|
|
1633
1634
|
let aiTool = null;
|
|
1634
|
-
let serverUrl;
|
|
1635
|
-
let appAlias;
|
|
1636
|
-
let clientId;
|
|
1637
|
-
let mcpRuleName;
|
|
1638
|
-
let mcpRuleInsKey;
|
|
1639
1635
|
if (aiIntegration) {
|
|
1640
1636
|
const aiToolResult = await p.select({
|
|
1641
1637
|
message: "Which AI coding tool will you use?",
|
|
@@ -1770,12 +1766,7 @@ Usage: npx @pega/sdk init`);
|
|
|
1770
1766
|
framework,
|
|
1771
1767
|
aiTool,
|
|
1772
1768
|
includeTemplates,
|
|
1773
|
-
componentCategories
|
|
1774
|
-
serverUrl,
|
|
1775
|
-
appAlias,
|
|
1776
|
-
clientId,
|
|
1777
|
-
mcpRuleName,
|
|
1778
|
-
mcpRuleInsKey
|
|
1769
|
+
componentCategories
|
|
1779
1770
|
};
|
|
1780
1771
|
const spinner2 = p.spinner();
|
|
1781
1772
|
spinner2.start("Scaffolding your project\u2026");
|
package/package.json
CHANGED
|
@@ -55,7 +55,7 @@ Determine which workflow governs the request, then execute it by reading the ref
|
|
|
55
55
|
|
|
56
56
|
### Step 0.0 — Ensure the Pega server connection is configured
|
|
57
57
|
|
|
58
|
-
Before executing any data source below, read and execute `{{SKILL_DIR}}/connect-pega-server.md`. It is a no-op when the connection is already configured,
|
|
58
|
+
Before executing any data source below, read and execute `{{SKILL_DIR}}/connect-pega-server.md`. It is a no-op when the connection is already configured. Otherwise, it resolves existing values from the active MCP config, browser-served `sdk-config.json`, and environment files, then asks once for every missing non-secret value. The user may provide those values in chat or write them into any allowed configuration file named by the gate; the assistant re-reads the files and synchronizes every applicable destination. The gate may pause for an environment-sourced Pega MCP client secret (Cursor only) or an MCP reconnect. After the user retries, rerun the gate before executing any data source.
|
|
59
59
|
|
|
60
60
|
### Step 0.1 — Check for a case type in the prompt
|
|
61
61
|
|
|
@@ -65,11 +65,11 @@ Scan the user's message for a **Pega case type class name** (hyphen-separated se
|
|
|
65
65
|
|
|
66
66
|
Execute the data source **`D_pzGetCaseTypesInApp`**, which returns the case types available in the application. Pass the name exactly as written — this data page is part of the Pega platform and does not vary between installations.
|
|
67
67
|
|
|
68
|
-
Execute it directly.
|
|
68
|
+
Execute it directly, by name. An execution tool may instruct you to look up the available data pages before calling it; you may run that lookup to satisfy the tool, but **its output is not authoritative**. The advertised list omits platform data pages that execute perfectly well, so a data page missing from it is not evidence that it is unavailable — and never a reason to stop, to ask the user to expose it through the MCP rule, or to abandon the flow. Only a failed execution attempt counts as a failure. Resolve the means of executing a Pega data source by what it does, not by what it is called; tool names and the server's label differ between editors and AI tools. Never look for a configuration file, never assume a path or a server name, and never tell the user which file to edit.
|
|
69
69
|
|
|
70
70
|
If nothing in this session can execute a Pega data source, stop. Tell the user the Pega MCP server is not available to this conversation, and name both common causes: the server is not running, or it was started after this conversation began — in which case its tools are not bound to this session and a new session is needed. Do not guess at its configuration, and do not fall back to a hardcoded component list.
|
|
71
71
|
|
|
72
|
-
If the execution runs but fails, report the failure
|
|
72
|
+
If the execution runs but fails, report the failure and the error the server returned. You may list the server's advertised data sources alongside it as context, but never present that list as evidence the data page is unavailable. Do not substitute a different data page.
|
|
73
73
|
|
|
74
74
|
1. Check `applicationIsConstellationCompatible` in the response. If it is `false`, stop and tell the user the application is not Constellation-compatible and cannot be embedded.
|
|
75
75
|
2. If the user's message did NOT contain a case type class name, present the returned case types as a numbered pick-list of `name — ID` pairs and ask which one to embed.
|
|
@@ -81,7 +81,7 @@ If the execution runs but fails, report the failure, and if the server's data so
|
|
|
81
81
|
|
|
82
82
|
Execute the data source **`D_pyCaseViewComponentsByType`** for the confirmed case type. Pass the name exactly as written — like the data page above, it is part of the platform. It returns the components the case type uses, each mapped to the views it appears in.
|
|
83
83
|
|
|
84
|
-
Pass the confirmed case type **ID** — the fully qualified class name, not the display label
|
|
84
|
+
Pass the confirmed case type **ID** — the fully qualified class name, not the display label — as the **`CaseType`** parameter. That is the only parameter this data page takes: do not add others, and do not invent values for ones you cannot resolve.
|
|
85
85
|
|
|
86
86
|
Reduce that response to a generatable list by applying the following in order. Points 2 and 3 are independent rules that remove components for unrelated reasons — keep those reasons distinct when reporting the outcome rather than presenting one merged list of removals.
|
|
87
87
|
|
|
@@ -127,13 +127,13 @@ Step 0 of the wire-integration skill resolves both, checks whether the named con
|
|
|
127
127
|
|
|
128
128
|
If the user named a launch point but no render target, the skill infers it from what the control already does. If they named neither, the skill asks using surfaces drawn from the customer's own application. Never assume either one.
|
|
129
129
|
|
|
130
|
-
| Workflow | Required inputs
|
|
131
|
-
| ------------------ |
|
|
132
|
-
| Analyze | Case type class + access to the customer's codebase
|
|
133
|
-
| Generate | Case type class + component list + Design Profile (or permission to create one)
|
|
134
|
-
| Full embedded flow | Case type class + server-resolved component list + codebase access + launch point + render target
|
|
135
|
-
| Wire integration | Case type class + generated
|
|
136
|
-
| Verify integration | A wired integration + a human available to authenticate when the flow reaches the Pega login form
|
|
130
|
+
| Workflow | Required inputs |
|
|
131
|
+
| ------------------ | --------------------------------------------------------------------------------------------------------------------------- |
|
|
132
|
+
| Analyze | Case type class + access to the customer's codebase |
|
|
133
|
+
| Generate | Case type class + component list + Design Profile (or permission to create one) |
|
|
134
|
+
| Full embedded flow | Case type class + server-resolved component list + codebase access + launch point + render target |
|
|
135
|
+
| Wire integration | Case type class + generated components folder (location resolved during analysis) must exist + launch point + render target |
|
|
136
|
+
| Verify integration | A wired integration + a human available to authenticate when the flow reaches the Pega login form |
|
|
137
137
|
|
|
138
138
|
---
|
|
139
139
|
|
|
@@ -143,7 +143,7 @@ If the user named a launch point but no render target, the skill infers it from
|
|
|
143
143
|
2. **Always read before executing** — read the skill or prompt file fully before acting on it. Never paraphrase from memory.
|
|
144
144
|
3. **Follow steps in order** — each skill/prompt defines numbered steps. Execute them sequentially without skipping.
|
|
145
145
|
4. **Design Profile is the bridge** — the analysis skill produces it, the generation prompt consumes it. If generation is requested and no profile exists, analysis must run first. Step 0 of the wiring skill consumes it too, when it has to ask where the flow should render — which is why analysis precedes that gate rather than following it.
|
|
146
|
-
5. **Never modify the customer's existing code** except at the seams that the wiring and verification skills enumerate. The
|
|
146
|
+
5. **Never modify the customer's existing code** except at the seams that the wiring and verification skills enumerate. The generated components folder (location resolved during analysis and recorded in the Design Profile) is purely additive, and a failing verification check is never permission to edit customer code outside those seams to make it pass.
|
|
147
147
|
6. **Component skills are authoritative** — the behavioral contracts in `{{SKILL_DIR}}/field-skills/`, `template-skills/`, `widget-skills/`, `design-system-extension-skills/`, and `infra-skills/` define what each component must do. These are non-negotiable.
|
|
148
148
|
7. **Adapt to the customer's framework** — the skills and prompts are framework-agnostic. Determine the customer's framework from their project configuration and apply conventions accordingly.
|
|
149
149
|
8. **Verification is mandatory, and it is not only static** — the generation prompt's checks confirm the generated files are well-formed. They do not confirm the flow runs. Whenever the integration has been wired, finish with `{{SKILL_DIR}}/embedded-flow/run-and-verify-integration.md` and report how far it got. Never declare success on static checks alone.
|
|
@@ -157,7 +157,7 @@ If the user named a launch point but no render target, the skill infers it from
|
|
|
157
157
|
| Situation | Response |
|
|
158
158
|
| ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
159
159
|
| A Pega data source cannot be executed in this session | Stop. Report that the Pega MCP server is not available to this conversation — either it is not running, or it was started after the conversation began and its tools are not bound to this session, which a new session fixes. Do not name a configuration file, a path, or a server label, and do not fall back to a hardcoded component list |
|
|
160
|
-
| A required data page is not among the data sources offered |
|
|
160
|
+
| A required data page is not among the data sources offered | Not a failure state. The advertised list omits platform data pages that execute correctly — execute it by name anyway. Only an actual execution error is a failure, and only then report what the server offered |
|
|
161
161
|
| `D_pzGetCaseTypesInApp` returns no case types | The operator has no creatable case types. Ask the user to verify the application alias and the operator's permissions |
|
|
162
162
|
| `D_pyCaseViewComponentsByType` returns no components | Report the case type that was queried and ask the user to confirm it has views configured in its lifecycle. Do not fall back to a hardcoded list |
|
|
163
163
|
| The starting-fields capability is unavailable or returns nothing | Record that no property names were confirmed and continue. The wiring step seeds nothing and reports the gap — it must not infer names from the customer's domain model |
|
|
@@ -47,7 +47,7 @@ Pega SDK packages (`.tgz` files) have been added to the project root and referen
|
|
|
47
47
|
|
|
48
48
|
### SDK Configuration
|
|
49
49
|
|
|
50
|
-
`sdk-config.json` contains connection details
|
|
50
|
+
The browser-served `sdk-config.json` contains non-secret Pega server connection details. The first workflow that needs a live server runs `{{SKILL_DIR}}/connect-pega-server.md`, which resolves values from the active SDK config, MCP config, and environment files, then asks once for all missing non-secret values. The user may provide those values in chat or update an allowed file; the assistant synchronizes every applicable destination. Only a Cursor-style MCP entry requires a Pega MCP client secret, which must come from a private environment variable and is never requested in chat or written to `sdk-config.json`. The browser SDK's OAuth grant configuration remains the user's choice. The case type is resolved from the server during the embedded flow itself.
|
|
51
51
|
|
|
52
52
|
---
|
|
53
53
|
|
|
@@ -100,7 +100,7 @@ Most infrastructure components ship from the SDK and are never generated. Three
|
|
|
100
100
|
|
|
101
101
|
## 5. Rules
|
|
102
102
|
|
|
103
|
-
1. **Never modify the customer's existing components during analysis or generation** — the
|
|
103
|
+
1. **Never modify the customer's existing components during analysis or generation** — the generated components folder (location resolved during analysis and recorded in the Design Profile) is additive. Wiring is the exception: it edits the integration seams listed in the wire-integration skill, plus the verification tooling seams listed in the run-and-verify-integration skill. A failing verification check never widens that set
|
|
104
104
|
2. **Match the customer's conventions** — generated code should look like the customer wrote it
|
|
105
105
|
3. **Satisfy the Pega behavioral contract** — every component must implement what its skill file requires
|
|
106
106
|
4. **Use the customer's design system** — never introduce a new UI library
|
|
@@ -8,6 +8,10 @@ You are assisting developers in a reference application that demonstrates the Pe
|
|
|
8
8
|
|
|
9
9
|
This is a **reference implementation** — not a framework. The UI components (fields, templates, widgets) are intentionally simple sample implementations meant to be swapped out. The SDK wiring (bootstrap, PConnect bridge, component registry) is reusable infrastructure that should NOT be modified.
|
|
10
10
|
|
|
11
|
+
### SDK Configuration
|
|
12
|
+
|
|
13
|
+
`sdk-config.json` and the generated MCP config file are scaffolded blank/placeholder — connection details are never collected at scaffold time. The first workflow that needs a live Pega server runs `{{SKILL_DIR}}/connect-pega-server.md`, which resolves values from the active SDK config, MCP config, and environment files, then asks once for all missing non-secret values. The user may provide those values in chat or update an allowed file; the assistant synchronizes every applicable destination. A Pega MCP client secret is required only when the active MCP entry has `auth.clientSecret` (currently Cursor), must come from a private environment variable, and is never requested in chat or written to browser-served SDK configuration.
|
|
14
|
+
|
|
11
15
|
---
|
|
12
16
|
|
|
13
17
|
## 2. What Is Replaceable vs. Infrastructure
|
|
@@ -10,7 +10,7 @@ When generating or modifying Pega components in the `pega-components/` folder (c
|
|
|
10
10
|
|
|
11
11
|
## Context
|
|
12
12
|
|
|
13
|
-
These components are different from the reference app's sample components. They are generated to live **inside a customer's existing project**, using the customer's design system so that they are visually indistinguishable from the rest of their UI. The Design Profile (
|
|
13
|
+
These components are different from the reference app's sample components. They are generated to live **inside a customer's existing project**, using the customer's design system so that they are visually indistinguishable from the rest of their UI. The Design Profile (`docs/design-profile.md` at the project root) describes the customer's visual conventions.
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
ai-globs: **/
|
|
2
|
+
ai-globs: **/{AutoComplete,CancelAlert,Checkbox,Currency,Date,DateTime,Decimal,Dropdown,Email,Group,Integer,Location,Multiselect,ObjectReference,Percentage,Phone,RadioButtons,RichText,ScalarList,SelectableCard,SemanticLink,TextArea,TextContent,TextInput,Time,URL,UserReference}/**/*
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
# Field Component Rules
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
ai-globs: **/
|
|
2
|
+
ai-globs: **/{AdvancedSearch,AppShell,BannerPage,CaseSummary,CaseView,CaseViewActionsMenu,Confirmation,DataReference,DefaultForm,DefaultPage,Details,FieldGroupTemplate,HierarchicalForm,InlineDashboard,InlineDashboardPage,ListPage,ListView,MultiReferenceReadOnly,NarrowWide,ObjectPage,OneColumn,PromotedFilters,SelfServiceCaseView,SimpleTable,SingleReferenceReadOnly,SubTabs,TwoColumn,WideNarrow,WssNavBar,SearchForm,SearchGroups}/**/*
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
# Template Component Rules
|
|
@@ -105,7 +105,7 @@ Record the visual treatment only. Generated components read validation messages
|
|
|
105
105
|
|
|
106
106
|
### 4. File Conventions
|
|
107
107
|
|
|
108
|
-
**Context only.** Generated components use Pega-owned paths and names (`
|
|
108
|
+
**Context only.** Generated components use Pega-owned paths and names (`{output_path}/{ComponentName}/`), so Structure and Naming below do not apply to them. Re-exports, test location, and style location still inform co-located files.
|
|
109
109
|
|
|
110
110
|
| Field | Description |
|
|
111
111
|
| -------------- | ------------------------------------------------------- |
|
|
@@ -126,7 +126,7 @@ Record the visual treatment only. Generated components read validation messages
|
|
|
126
126
|
| Default/named preference | Which import style dominates |
|
|
127
127
|
| Style imports | How stylesheets or style modules are imported |
|
|
128
128
|
|
|
129
|
-
Generated components import the customer's UI primitives through their path aliases. Relative paths are used only _within_ `
|
|
129
|
+
Generated components import the customer's UI primitives through their path aliases. Relative paths are used only _within_ the generated output folder (`{output_path}`) — for shared helpers, barrel files, and the registry file's component imports. One Pega component is never imported into another to render it; that always goes through the component registry.
|
|
130
130
|
|
|
131
131
|
---
|
|
132
132
|
|
|
@@ -57,17 +57,17 @@ Once design system, base folder, and type scope are resolved, continue to Step 0
|
|
|
57
57
|
|
|
58
58
|
### 0.5.0 — Ensure the Pega server connection is configured
|
|
59
59
|
|
|
60
|
-
Before executing any data source below, read and execute `{{SKILL_DIR}}/connect-pega-server.md`. It is a no-op when the connection is already configured,
|
|
60
|
+
Before executing any data source below, read and execute `{{SKILL_DIR}}/connect-pega-server.md`. It is a no-op when the connection is already configured. Otherwise, it resolves existing values from the active MCP config, application `sdk-config.json`, and environment files; asks once for every missing non-secret value; and writes chat-provided values to every applicable file. It may pause for an environment-sourced Pega MCP client secret (Cursor only) or an MCP reconnect. After the user retries, rerun the gate before executing any data source.
|
|
61
61
|
|
|
62
62
|
### 0.5.1 — Resolve the portal
|
|
63
63
|
|
|
64
64
|
Execute the data source **`D_pyAvailablePortals`**, which returns the portals available in the application. Pass the name exactly as written — this data page is part of the Pega platform and does not vary between installations.
|
|
65
65
|
|
|
66
|
-
Execute it directly.
|
|
66
|
+
Execute it directly, by name. An execution tool may instruct you to look up the available data pages before calling it; you may run that lookup to satisfy the tool, but **its output is not authoritative**. The advertised list omits platform data pages that execute perfectly well, so a data page missing from it is not evidence that it is unavailable — and never a reason to stop, to ask the user to expose it through the MCP rule, or to abandon the flow. Only a failed execution attempt counts as a failure. Resolve the means of executing a Pega data source by what it does, not by what it is called; tool names and the server's label differ between editors and AI tools. Never look for a configuration file, never assume a path or a server name, and never tell the user which file to edit.
|
|
67
67
|
|
|
68
68
|
If nothing in this session can execute a Pega data source, stop. Tell the user the Pega MCP server is not available to this conversation, and name both common causes: the server is not running, or it was started after this conversation began — in which case its tools are not bound to this session and a new session is needed. Do not guess at its configuration, and do not fall back to generating the full component catalogue.
|
|
69
69
|
|
|
70
|
-
If the execution runs but fails, report the failure
|
|
70
|
+
If the execution runs but fails, report the failure and the error the server returned. You may list the server's advertised data sources alongside it as context, but never present that list as evidence the data page is unavailable. Do not substitute a different data page.
|
|
71
71
|
|
|
72
72
|
The response's `pxResults` array holds one entry per portal. Then:
|
|
73
73
|
|
|
@@ -88,6 +88,8 @@ Ask the user which parts of the portal to convert: **Landing pages**, **Cases**,
|
|
|
88
88
|
|
|
89
89
|
Execute the data source **`D_pyPortalPages`** with `PortalName` set to the `pyPortalLayout` value recorded in Step 0.5.1. It returns the landing pages configured in that portal, in a `pxResults` array whose entries carry `pxObjClass`, `pyRuleName`, and `pyClassName`.
|
|
90
90
|
|
|
91
|
+
Execute it — and every data page this step dispatches to — by name. Step 0.5.1's rule still applies: absence from an availability lookup is not evidence a data page is unavailable, and never a reason to stop.
|
|
92
|
+
|
|
91
93
|
If `pxResults` is empty, tell the user this portal has no landing pages and, if Cases was not already selected, offer to fall back to it. If Cases was also not selected or also comes back empty, stop — there is nothing to generate.
|
|
92
94
|
|
|
93
95
|
Present the returned pages as a **checkbox list** (label each with its `pyRuleName`, and show its `pxObjClass` alongside so the user can tell a view from a landing page) and let the user select one or more. This is a gate.
|
|
@@ -115,17 +117,59 @@ Do not supply `ApplicationName` or `ApplicationVersion` to `D_pyLandingPageCompo
|
|
|
115
117
|
|
|
116
118
|
Execute the data source **`D_pyPortalCaseTypes`** with `PortalName` set to the `pyPortalLayout` value recorded in Step 0.5.1. It returns the case types configured in that portal, in a `pxResults` array whose entries carry `pyClassName`.
|
|
117
119
|
|
|
120
|
+
Execute it — and `D_pyCaseViewComponentsByType` below — by name. Step 0.5.1's rule still applies: absence from an availability lookup is not evidence a data page is unavailable, and never a reason to stop.
|
|
121
|
+
|
|
118
122
|
If `pxResults` is empty, tell the user this portal has no case types and, if Landing pages was not already selected, offer to fall back to it. If Landing pages was also not selected or also comes back empty, stop — there is nothing to generate.
|
|
119
123
|
|
|
120
124
|
Present the returned case types as a **checkbox list** and let the user select one or more. This is a gate.
|
|
121
125
|
|
|
122
126
|
For **each selected case type**, execute **`D_pyCaseViewComponentsByType`** with `CaseType` set to its `pyClassName`. Make one call per selected case type. Discard malformed entries from each response, then union the results across all selected case types and drop duplicates. Keep a note of which case type(s) contributed each component.
|
|
123
127
|
|
|
124
|
-
### 0.5.5 —
|
|
128
|
+
### 0.5.5 — Resolve names, present, and ask which components to generate
|
|
125
129
|
|
|
126
130
|
Union whatever Step 0.5.3 and Step 0.5.4 produced (only one ran unless the user chose Both) and drop duplicates across the two.
|
|
127
131
|
|
|
128
|
-
|
|
132
|
+
Every component you generate must correspond to one canonical component listed in `{{PROMPT_DIR}}/component-manifest.json`. Pega defines a fixed set of components, and the manifest is the complete list of them. Read it once. It contains three things you need here:
|
|
133
|
+
|
|
134
|
+
- `components` — the canonical names, grouped by type (`field`, `template`, `widget`, `designSystemExtension`, `infra`)
|
|
135
|
+
- `aliases` — known non-canonical names and the canonical name each one maps to
|
|
136
|
+
- `behaviorClasses` — a fixed list of behaviour descriptions, each mapping to one or more canonical names
|
|
137
|
+
|
|
138
|
+
For each name the server returned, work through the following stages in order. Stop at the first stage that produces an answer. These names come from Pega's own view metadata, so most are already canonical and settle at Stage 1 — the later stages exist for the ones that do not.
|
|
139
|
+
|
|
140
|
+
#### Stage 1 — Normalized name match
|
|
141
|
+
|
|
142
|
+
1. Take the returned name and make a normalized form: convert it to lowercase, then remove all spaces, hyphens, and underscores, then remove a trailing `field`, `component`, `input`, or `control` if one is present.
|
|
143
|
+
2. Build the same normalized form for every canonical name in `components`.
|
|
144
|
+
3. If exactly one canonical name has the same normalized form, that is the answer.
|
|
145
|
+
|
|
146
|
+
#### Stage 2 — Alias lookup
|
|
147
|
+
|
|
148
|
+
1. Look up the returned name in the `aliases` object. Compare using the normalized form from Stage 1 so that casing and spacing do not matter.
|
|
149
|
+
2. If the name is present, the value it maps to is the answer.
|
|
150
|
+
|
|
151
|
+
#### Stage 3 — Behaviour class match
|
|
152
|
+
|
|
153
|
+
Do not search for a "similar sounding" name here. Follow these three sub-steps exactly.
|
|
154
|
+
|
|
155
|
+
1. Decide what the component does for the end user, and choose **exactly one** entry from the `behaviorClasses` list that describes that behaviour. You must pick one of the listed classes. If you cannot honestly place the component in any of them, go to Stage 4.
|
|
156
|
+
2. Read the canonical names that class maps to. If the class maps to exactly one name, that is the candidate.
|
|
157
|
+
3. Read the candidate's skill file — `{{SKILL_DIR}}/{type}-skills/<CanonicalName>.md`, where `{type}` is `field`, `template`, `widget`, `design-system-extension`, or `infra` — and confirm its behavioural contract covers what the component does. If it clearly does not, go to Stage 4.
|
|
158
|
+
|
|
159
|
+
If the class maps to more than one canonical name, choose between them using the page or case type the name came from and the surrounding context. If the context does not make the choice obvious, go to Stage 4.
|
|
160
|
+
|
|
161
|
+
#### Stage 4 — Ask the user
|
|
162
|
+
|
|
163
|
+
Tell the user the name could not be resolved to a Pega component, list the two or three canonical names you considered most likely, and ask which one they want — or whether to leave it out. Do not guess, and do not proceed with that component until they answer. Collect every unresolved name into a **single** question rather than asking once per name; a portal can return a long list, and one question per name is not a usable gate.
|
|
164
|
+
|
|
165
|
+
#### Rules for this step
|
|
166
|
+
|
|
167
|
+
- This process always ends in one of two ways: a single canonical name, or a question to the user. It never ends with you inventing a component.
|
|
168
|
+
- Once a canonical name is chosen, use it everywhere afterwards: the type grouping, the skill file you read, the generated folder name, and the registry key. The name the server returned is only used when reporting the substitution back to the user.
|
|
169
|
+
- Record the pairing of returned name and canonical name. You will report it in the Step 9 summary.
|
|
170
|
+
- If two different returned names resolve to the same canonical name, generate that component once.
|
|
171
|
+
|
|
172
|
+
Group the resolved canonical names by type — `field`, `template`, `widget`, `designSystemExtension`, or `infra`.
|
|
129
173
|
|
|
130
174
|
Present the grouped list — noting, for any component contributed by more than one page or case type, which ones — and ask which components to generate as a **checkbox list**, offering selecting all of them as an explicit option. Where a returned component is one the SDK provides and this prompt never generates — anything named in Step 8's never-generate list, or a structural wrapper such as `View`, `Region`, or `FlowContainer` — keep it visible in the list but mark it as not generatable and say why, so the user can see it's used without expecting a folder for it.
|
|
131
175
|
|
|
@@ -544,3 +588,4 @@ Report a final summary:
|
|
|
544
588
|
- `FieldValueList`, if it was added automatically under Step 0.5.6
|
|
545
589
|
- All entries added to the component map
|
|
546
590
|
- Any components skipped (if "skip existing" was chosen in Step 3, or because they are SDK-provided infrastructure)
|
|
591
|
+
- Any name the server returned that resolved to a **different** canonical name, with the stage that produced the match (normalized name, alias, behaviour class, or the user's choice). Say it in your reply as well — the user must never discover a substitution only by reading the generated files
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
ai-description: "Generate Pega components styled to match a customer's existing application — given a component list and a Design Profile, produces a
|
|
2
|
+
ai-description: "Generate Pega components styled to match a customer's existing application — given a component list and a Design Profile, produces a generated components folder (location chosen by the user during analysis) with implementations that satisfy the Pega behavioral contract while rendering with the customer's design system, tokens, and styling approach, and reading like their own code"
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
## Step 0 — Resolve missing information
|
|
@@ -7,8 +7,8 @@ ai-description: "Generate Pega components styled to match a customer's existing
|
|
|
7
7
|
Read the user's message carefully. Extract:
|
|
8
8
|
|
|
9
9
|
- **Component list** — the Pega component names to generate (e.g., `TextInput`, `Dropdown`, `DefaultForm`, `TwoColumn`, `ToDo`)
|
|
10
|
-
- **Design Profile location** —
|
|
11
|
-
- **Output path** —
|
|
10
|
+
- **Design Profile location** — always `docs/design-profile.md` at the project root. This location is fixed and never moves with the output path
|
|
11
|
+
- **Output path** — read the **Output Path** field from Section 1 of the Design Profile and use it verbatim as `{output_path}`. Never re-ask for it and never fall back to a hardcoded default — it was already resolved once, with the user, during analysis
|
|
12
12
|
|
|
13
13
|
The **component list** is required. If it is not provided and cannot be inferred from prior conversation context, stop and ask the user.
|
|
14
14
|
|
|
@@ -77,7 +77,7 @@ After every name is resolved, group the canonical names by type so you can read
|
|
|
77
77
|
|
|
78
78
|
Read the following **once** before the generation loop:
|
|
79
79
|
|
|
80
|
-
1. **The Design Profile** — read the full
|
|
80
|
+
1. **The Design Profile** — read the full `docs/design-profile.md`. Its design tokens, styling convention, and validation display convention are authoritative and must be replicated. Its component patterns (input interface, state management, event handling) and file conventions describe the customer's own code — read them for context, but generated components follow the Pega contract and Pega-owned paths instead.
|
|
81
81
|
|
|
82
82
|
2. **General type-level skills** — for each component type present in the grouped list, read the corresponding general skill:
|
|
83
83
|
|
|
@@ -161,17 +161,16 @@ Create the component implementation that satisfies **both**:
|
|
|
161
161
|
|
|
162
162
|
State, events, validation wiring, and prop contracts are deliberately absent from that list — they come from the Pega behavioral contract above, no matter how the customer writes their own forms. A customer whose own screens use a form library does not change how a generated component holds a value or commits it.
|
|
163
163
|
|
|
164
|
-
**Exception — the
|
|
164
|
+
**Exception — the layout beneath `{output_path}` is not the customer's to choose.** The folder the user resolved during analysis is a generated, Pega-owned tree that the customer never authors in, so what goes underneath it is fixed rather than inferred from the Design Profile. Write the component to:
|
|
165
165
|
|
|
166
166
|
```
|
|
167
|
-
{output_path}/{
|
|
167
|
+
{output_path}/{ComponentName}/{ComponentName}.{ext}
|
|
168
168
|
```
|
|
169
169
|
|
|
170
|
-
- `{
|
|
171
|
-
- `{ComponentName}` is the canonical Pega component name resolved in Step 1, verbatim, for both the folder and the implementation file, whatever the customer's naming style is. `Checkbox` produces `Checkbox/Checkbox.*`, never `checkbox/CheckboxField.*`; `Date` produces `Date/Date.*`, never `date/DateField.*`.
|
|
170
|
+
- `{ComponentName}` is the canonical Pega component name resolved in Step 1, verbatim, for both the folder and the implementation file, whatever the customer's naming style is. `Checkbox` produces `Checkbox/Checkbox.*`, never `checkbox/CheckboxField.*`; `Date` produces `Date/Date.*`, never `date/DateField.*`. Components are never grouped into a further `field`/`template`/`widget`/`designSystemExtension`/`infra` subfolder — every component's own folder sits directly under `{output_path}`.
|
|
172
171
|
- `{ext}` follows the customer's framework, not their file-naming style.
|
|
173
172
|
|
|
174
|
-
Deterministic paths are what let tooling
|
|
173
|
+
Deterministic paths are what let tooling and regeneration find components again. Customer conventions govern the _contents_ of these files, not their location or name. If the customer's repo lints file names, exclude `{output_path}` from that rule rather than bending the paths.
|
|
175
174
|
|
|
176
175
|
### 4d. Register the component
|
|
177
176
|
|
|
@@ -277,7 +276,7 @@ Read the generated registry file and confirm:
|
|
|
277
276
|
Verify that every import in the generated files resolves to either:
|
|
278
277
|
|
|
279
278
|
- A package present in the customer's dependency manifest
|
|
280
|
-
- A file within the generated
|
|
279
|
+
- A file within the generated components folder (`{output_path}`)
|
|
281
280
|
- The Pega SDK packages (added in integration wiring)
|
|
282
281
|
|
|
283
282
|
If any import references a package not in the project, either:
|
|
@@ -304,7 +303,7 @@ Whenever `Requested Name` and `Resolved As` differ, say so in your reply as well
|
|
|
304
303
|
|
|
305
304
|
1. **Never invent UI primitives** — only use components available in the customer's UI library or their custom component set
|
|
306
305
|
2. **Never skip behavioral requirements** — every item in the skill file's contract must be implemented, even if the customer's equivalent doesn't natively support it (add the behavior manually)
|
|
307
|
-
3. **Never break existing code** — the generated
|
|
306
|
+
3. **Never break existing code** — the generated components folder (`{output_path}`) is additive; never modify the customer's existing components. This prompt does not wire the integration or verify it — those are separate steps, and they are the only ones permitted to edit the customer's files, confined to the seams their skills enumerate
|
|
308
307
|
4. **Never hardcode design tokens** — always reference the customer's token system (variables, constants, theme values)
|
|
309
308
|
5. **Respect the customer's code conventions** — the contents of generated files should look like the customer wrote them
|
|
310
309
|
6. **One folder per component** — each component gets its own folder named after its canonical Pega name; paths are Pega's, contents are the customer's
|
|
@@ -4,51 +4,104 @@ ai-description: Ensure the Pega server connection (MCP config + sdk-config.json)
|
|
|
4
4
|
|
|
5
5
|
# Pega Server Connection Gate
|
|
6
6
|
|
|
7
|
-
Run this **before** the first Pega data source is executed in a chat session. It
|
|
7
|
+
Run this **before** the first Pega data source is executed in a chat session. It applies to both full-portal and embedded workflows. It resolves connection values from the project's existing configuration, asks once for everything still missing, and synchronizes the active MCP config and browser-served `sdk-config.json`.
|
|
8
8
|
|
|
9
|
-
## 1. Locate the
|
|
9
|
+
## 1. Locate the active configuration files
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
### 1.1 MCP config
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Find the file this session's AI tool reads its Pega MCP server list from. Do not assume a fixed name or ask the user which file to edit. Search the project for a server entry whose key starts with `pega-` and whose `url` contains `/api/service/v1/mcp/`.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Classify the active MCP entry by its shape:
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
- **Cursor-style authenticated MCP** — the entry contains `auth.clientId` and `auth.clientSecret`. Both fields are required for this MCP connection.
|
|
18
|
+
- **URL-only MCP** — the entry has no auth fields, as in Copilot and Claude Code. Do not require, search for, warn about, or mention a client secret for this entry.
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
### 1.2 Browser-served SDK config
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
Find the `sdk-config.json` the application actually serves to the browser. Do not automatically choose a same-named file at the project root:
|
|
22
23
|
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
- MCP rule name
|
|
27
|
-
- MCP rule instance key
|
|
28
|
-
- **Client secret — only if the MCP config's server entry has an `auth` block containing a `clientSecret` field.** Some MCP configurations authenticate this way and some do not; let the file's own structure decide whether to ask, rather than deciding by which AI tool is running. If there is no `auth` block, do not ask for a client secret at all.
|
|
24
|
+
- For an embedded project, identify the framework's served static directory (for example `public/`, `static/`, or another directory established by its build configuration) and use the `sdk-config.json` there.
|
|
25
|
+
- For a full-portal project, use its scaffolded application `sdk-config.json`.
|
|
26
|
+
- If more than one copy exists, determine which one the application serves. Report other copies as potentially stale and do not update them.
|
|
29
27
|
|
|
30
|
-
|
|
28
|
+
### 1.3 Environment files
|
|
31
29
|
|
|
32
|
-
|
|
30
|
+
Environment files are commonly excluded by `.gitignore`, so do not rely on an ordinary recursive file search or glob to discover them. First enumerate the project-root directory, including dotfiles, then read every root entry named `.env` or beginning with `.env.` directly by its exact path. If directory enumeration is unavailable, search with ignored files explicitly included. A zero-result ignore-aware search is not evidence that no environment files exist.
|
|
33
31
|
|
|
34
|
-
|
|
32
|
+
Read project-root environment files such as `.env`, `.env.local`, `.env.development`, `.env.production`, and other `.env.*` variants. Never print a secret value.
|
|
35
33
|
|
|
36
|
-
|
|
37
|
-
- `{AppAlias}` → application alias
|
|
38
|
-
- `{MCPRuleName}` → MCP rule name (in both the server key and the URL path)
|
|
39
|
-
- `{MCPRuleInsKey}` → MCP rule instance key
|
|
40
|
-
- `{ClientId}` → client ID, if the entry has an `auth` block
|
|
41
|
-
- `{ClientSecret}` → client secret, if the `auth` block has that field
|
|
34
|
+
## 2. Build the connection inventory
|
|
42
35
|
|
|
43
|
-
|
|
36
|
+
Resolve each field independently from every applicable source. A value is unresolved when it is blank or still contains its scaffold marker.
|
|
44
37
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
38
|
+
| Field | Allowed sources | Destinations |
|
|
39
|
+
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
|
|
40
|
+
| Pega server URL | `serverConfig.infinityRestServerUrl`, the MCP URL, or a matching environment key | SDK config and MCP URL |
|
|
41
|
+
| Application alias | `serverConfig.appAlias`, the MCP URL, or a matching environment key | SDK config and MCP URL |
|
|
42
|
+
| MCP rule name | The `pega-*` MCP server key or a matching environment key | MCP server key only |
|
|
43
|
+
| MCP rule instance key | The MCP URL segment after `/api/service/v1/mcp/` or a matching environment key | MCP URL |
|
|
44
|
+
| OAuth client ID | `authConfig.mashupClientId`, `authConfig.portalClientId`, MCP `auth.clientId` when present, or a matching environment key | Both SDK client-ID fields and MCP `auth.clientId` when present |
|
|
45
|
+
| Pega MCP client secret | A private environment key only, and only when the MCP entry contains `auth.clientSecret` | MCP `auth.clientSecret` only |
|
|
49
46
|
|
|
50
|
-
|
|
47
|
+
The five non-secret fields are required for every tool. The client ID is a public OAuth identifier and may be supplied in chat. A client secret is required only by a Cursor-style authenticated MCP entry; it is not required for the browser SDK or a URL-only Copilot/Claude Code MCP entry.
|
|
51
48
|
|
|
52
|
-
|
|
49
|
+
For non-secret environment values, recognize clear semantic keys ending in `INFINITY_URL`, `SERVER_URL`, `APP_ALIAS`, `MCP_RULE_NAME`, `MCP_RULE_INS_KEY`, `MCP_RULE_INSTANCE_KEY`, or `CLIENT_ID`, including project conventions such as `PEGA_`, `VITE_`, `NEXT_PUBLIC_`, `REACT_APP_`, and `EXPO_PUBLIC_`.
|
|
53
50
|
|
|
54
|
-
|
|
51
|
+
For a client secret, accept only a private key such as `PEGA_CLIENT_SECRET` or `CLIENT_SECRET`. Reject browser-exposed names beginning with `VITE_`, `NEXT_PUBLIC_`, `REACT_APP_`, or `EXPO_PUBLIC_`. Never infer a value from an unrelated key merely because it is the only candidate.
|
|
52
|
+
|
|
53
|
+
Record each field's source and status: configured, missing, or conflicting. For a secret, record presence and source only, never its value.
|
|
54
|
+
|
|
55
|
+
## 3. Resolve conflicts before writing
|
|
56
|
+
|
|
57
|
+
If allowed sources contain different real values for the same non-secret field, show the field and each source/value pair and ask the user which source is authoritative. Do not silently choose or overwrite.
|
|
58
|
+
|
|
59
|
+
If private environment files contain different client-secret values for a Cursor-style MCP entry, state that the secret sources disagree and name only the files and keys. Ask which environment key is authoritative without exposing either value.
|
|
60
|
+
|
|
61
|
+
Equivalent values across sources are not conflicts; consolidate them without asking.
|
|
62
|
+
|
|
63
|
+
## 4. Ask once for every unresolved value
|
|
64
|
+
|
|
65
|
+
After reading all sources, make the first user interaction complete. Show a status table for every applicable field, using only **configured**, **missing**, or **conflicting**. For the client secret, show presence only.
|
|
66
|
+
|
|
67
|
+
In that same response:
|
|
68
|
+
|
|
69
|
+
1. Ask for every missing or conflict-resolved **non-secret** value as one batch. The user may provide these values directly in chat.
|
|
70
|
+
2. Explain that the user may instead edit any allowed source and say when it is ready. Name the exact destinations:
|
|
71
|
+
- Environment: matching semantic keys for any non-secret field.
|
|
72
|
+
- Active `sdk-config.json`: `serverConfig.infinityRestServerUrl`, `serverConfig.appAlias`, `authConfig.mashupClientId`, and `authConfig.portalClientId`.
|
|
73
|
+
- MCP config: `{InfinityURL}` and `{AppAlias}` in the URL, `{MCPRuleName}` in the server key, `{MCPRuleInsKey}` in the URL, and `auth.clientId` when that field exists.
|
|
74
|
+
3. If and only if the active MCP entry contains `auth.clientSecret` and no valid private environment value exists, explain in this first response that the **Pega MCP client secret** is missing. Tell the user to add `PEGA_CLIENT_SECRET` or another private `CLIENT_SECRET`-style key to a project-root environment file and confirm when ready. Never ask them to paste it into chat, put it in `sdk-config.json`, use a browser-exposed env key, or enter it directly into the MCP JSON.
|
|
75
|
+
|
|
76
|
+
Do not stop after reporting a missing Cursor secret without also asking for every missing non-secret value. On every reply or retry, re-read all configuration sources and ask again only for fields that remain unresolved.
|
|
77
|
+
|
|
78
|
+
## 5. Synchronize resolved values
|
|
79
|
+
|
|
80
|
+
Once all five non-secret fields are resolved — and, for a Cursor-style MCP entry, a private environment-sourced secret is resolved — update the active files while preserving unrelated fields.
|
|
81
|
+
|
|
82
|
+
**MCP config:**
|
|
83
|
+
|
|
84
|
+
- Replace `{InfinityURL}` with the server URL.
|
|
85
|
+
- Replace `{AppAlias}` with the application alias.
|
|
86
|
+
- Rename the `pega-{MCPRuleName}` server key with the real MCP rule name. The rule name belongs in the server key, not the endpoint URL.
|
|
87
|
+
- Replace `{MCPRuleInsKey}` with the MCP rule instance key.
|
|
88
|
+
- When `auth.clientId` exists, write the resolved client ID.
|
|
89
|
+
- When `auth.clientSecret` exists, write the private environment-sourced secret without displaying it.
|
|
90
|
+
- Never add an auth block to a URL-only MCP entry.
|
|
91
|
+
|
|
92
|
+
**Active browser-served `sdk-config.json`:**
|
|
93
|
+
|
|
94
|
+
- Write the server URL to `serverConfig.infinityRestServerUrl`.
|
|
95
|
+
- Write the application alias to `serverConfig.appAlias`.
|
|
96
|
+
- Write the client ID to both `authConfig.mashupClientId` and `authConfig.portalClientId`.
|
|
97
|
+
- Do not create new fields or change unrelated values.
|
|
98
|
+
|
|
99
|
+
Never write a client secret into `sdk-config.json`, including `authConfig.portalClientSecret`. This file is served to the browser and is publicly readable. The user's browser-runtime OAuth grant configuration remains their choice; this gate only ensures the required public client ID is present.
|
|
100
|
+
|
|
101
|
+
After writing, re-read both active files and repeat the inventory check. Continue only when all applicable markers are gone, the five non-secret values are non-empty and consistent, and a Cursor-style `auth.clientSecret` was populated from a private environment value. A URL-only MCP entry is complete without any client secret.
|
|
102
|
+
|
|
103
|
+
## 6. Continue or reconnect
|
|
104
|
+
|
|
105
|
+
- If the MCP config changed, tell the user it must be reloaded or reconnected before a Pega data source can run, then stop. Ask them to retry the original request; the invoking workflow must rerun this gate first. If MCP tools were started or attached after the conversation began and are not bound to this session, tell the user to start a new chat session.
|
|
106
|
+
- If only `sdk-config.json` changed and the MCP connection is already available to the conversation, continue the invoking workflow without requiring an unnecessary MCP reconnect.
|
|
107
|
+
- If neither file needed changes, continue immediately.
|
package/templates/react-sdk-reference/.ai-common/skills/embedded-flow/analyze-customer-repo.md
CHANGED
|
@@ -28,6 +28,15 @@ When Pega case flows are embedded into a customer's existing application, the ge
|
|
|
28
28
|
|
|
29
29
|
## Steps
|
|
30
30
|
|
|
31
|
+
### Step 0 — Resolve the output path
|
|
32
|
+
|
|
33
|
+
Every downstream skill and prompt writes generated components to one location: `{output_path}`. Resolve it once, here, before anything else runs. This is separate from the Design Profile document itself, which always lives at the fixed `docs/design-profile.md` and never moves with `{output_path}`.
|
|
34
|
+
|
|
35
|
+
1. **Check for an existing profile first.** Look for `docs/design-profile.md` at the project root — this is its one fixed location, independent of `{output_path}`. If it exists, read its **Output Path** field (Section 1) and reuse that value as `{output_path}` — skip the rest of this step entirely. Do not ask the user again.
|
|
36
|
+
2. **Otherwise, ask.** Ask the user where in their own project the generated Pega components should live. Suggest `pega-components/` at the project root as the default/recommended answer, but accept any path they type instead.
|
|
37
|
+
3. **Validate the answer stays inside the project.** Reject an absolute path outside the project, any `..` traversal segment, or anything else that would resolve outside the project root — re-ask when this happens rather than writing outside the repo.
|
|
38
|
+
4. **Record the confirmed path** as `{output_path}`. It is written into the Design Profile in Step 6 below, and every other embedded-flow skill/prompt reads it from there instead of asking again or assuming a fixed name.
|
|
39
|
+
|
|
31
40
|
### Step 1 — Identify project fundamentals
|
|
32
41
|
|
|
33
42
|
Read the project's dependency manifest (the file that lists packages and their versions). From it, determine:
|
|
@@ -105,7 +114,7 @@ This is what makes it possible to offer the user real choices in their own vocab
|
|
|
105
114
|
|
|
106
115
|
Produce a structured markdown document with the following sections. Use heading levels for organization and bullet lists for values. Do not include source code — describe patterns in prose and reference file paths for the AI to read during generation.
|
|
107
116
|
|
|
108
|
-
**Section 1: Project Fundamentals** — list the framework name and version, language and configuration, UI library name and version (or "Custom"), styling approach, form handling library or pattern, and module system.
|
|
117
|
+
**Section 1: Project Fundamentals** — start with the **Output Path** resolved in Step 0, then list the framework name and version, language and configuration, UI library name and version (or "Custom"), styling approach, form handling library or pattern, and module system.
|
|
109
118
|
|
|
110
119
|
**Section 2: Design Tokens** — organize by sub-sections: Colors (primary, secondary, accent, error, warning, success, background, text, border with their semantic names and values), Spacing (scale), Typography (font families, sizes, weights), Borders and Radii (values), Shadows (elevation values), Breakpoints (if defined).
|
|
111
120
|
|
|
@@ -119,7 +128,7 @@ Produce a structured markdown document with the following sections. Use heading
|
|
|
119
128
|
|
|
120
129
|
**Section 7: Navigation and Surfaces** — list the routing approach and how navigation is triggered, the overlay primitives the project uses and where they come from, the layout shell that wraps a routed page, and a table of existing task entry points giving each control's visible label, its file, and what it does today. The wiring workflow reads this section to decide where the embedded flow renders.
|
|
121
130
|
|
|
122
|
-
Save this document to `
|
|
131
|
+
Save this document to `docs/design-profile.md` in the customer's project, creating the `docs/` folder at the project root first if it does not already exist. This location is fixed — it does not move with `{output_path}`.
|
|
123
132
|
|
|
124
133
|
---
|
|
125
134
|
|
|
@@ -6,7 +6,7 @@ Guide the wiring of generated Pega components into a customer's existing applica
|
|
|
6
6
|
|
|
7
7
|
## Purpose
|
|
8
8
|
|
|
9
|
-
After the
|
|
9
|
+
After the generated components folder (its location is resolved during analysis and recorded in the Design Profile) has been generated (via the component generation prompt), this skill describes the steps to integrate the Pega SDK into the customer's application — connecting authentication, bootstrapping the runtime, launching case flows on user action, and rendering the flow within the application's layout.
|
|
10
10
|
|
|
11
11
|
**Step 0 is the exception.** It is a gate, not an integration step, and it runs _before_ generation — see its own heading for why.
|
|
12
12
|
|
|
@@ -14,7 +14,7 @@ After the `pega-components/` folder has been generated (via the component genera
|
|
|
14
14
|
|
|
15
15
|
## Prerequisites
|
|
16
16
|
|
|
17
|
-
- The
|
|
17
|
+
- The generated components folder (its location is resolved during analysis and recorded in the Design Profile) exists with generated components and a registry file named `component-map` (the extension follows the customer's framework) — required from **Step 1 onward**. Step 0 runs before generation and needs only the customer's codebase and the Design Profile
|
|
18
18
|
- The customer has a Pega Infinity environment with:
|
|
19
19
|
- An OAuth 2.0 client configured for the application
|
|
20
20
|
- A case type to embed (e.g., a service request, onboarding flow)
|
|
@@ -155,7 +155,7 @@ Add the following Pega packages to the project's dependency manifest:
|
|
|
155
155
|
| The Pega SDK types package | Shared type definitions and interfaces |
|
|
156
156
|
| The Pega ConstellationJS core package | Runtime engine that provides the PCore global |
|
|
157
157
|
|
|
158
|
-
These are the only external additions. The generated
|
|
158
|
+
These are the only external additions. The generated components use the customer's existing UI library — no additional design system packages are needed.
|
|
159
159
|
|
|
160
160
|
If SDK packages were provided as `.tgz` files (interim distribution), they should already be in the project root with `file:` references in the dependency manifest. Run the project's package install command to link them.
|
|
161
161
|
|
|
@@ -295,7 +295,7 @@ The module-scoped singleton avoids this entirely because it is immune to compone
|
|
|
295
295
|
|
|
296
296
|
1. **Authenticate** — use the SDK auth client to perform login (OAuth redirect or silent renewal)
|
|
297
297
|
2. **Bootstrap in embedded/mashup mode** — initialize the SDK runtime in mashup mode (not full portal), capturing the render context returned by bootstrap
|
|
298
|
-
3. **Create the component registry** — instantiate the SDK's component registry and register all components exported by the generated `
|
|
298
|
+
3. **Create the component registry** — instantiate the SDK's component registry and register all components exported by the generated components folder's `component-map` registry file
|
|
299
299
|
4. **Register the component creator** — tell the SDK runtime to use the registered components for rendering
|
|
300
300
|
|
|
301
301
|
This module should expose:
|
|
@@ -433,7 +433,7 @@ Do not report the integration as complete on static checks alone.
|
|
|
433
433
|
│ │ │ │ │ │
|
|
434
434
|
│ │ │ SDK Bridge Component │ │ │
|
|
435
435
|
│ │ │ ↓ resolves from registry │ │ │
|
|
436
|
-
│ │ │
|
|
436
|
+
│ │ │ generated components folder (generated, customer-styled) │ │ │
|
|
437
437
|
│ │ │ ↓ wired to │ │ │
|
|
438
438
|
│ │ │ PConnect (Pega runtime state + actions) │ │ │
|
|
439
439
|
│ │ └─────────────────────────────────────────────────┘ │ │
|
|
@@ -470,7 +470,7 @@ Common integration failures and their resolutions. These are ordered by how they
|
|
|
470
470
|
|
|
471
471
|
## Notes
|
|
472
472
|
|
|
473
|
-
- The generated
|
|
473
|
+
- The generated components folder (its location is resolved during analysis and recorded in the Design Profile) has zero dependencies beyond the customer's existing UI library and the Pega SDK packages. No additional design system installation is needed.
|
|
474
474
|
- The bootstrap module should handle token refresh/silent renewal automatically via the SDK auth client.
|
|
475
475
|
- If the customer's app already has authentication, coordinate with their auth flow — the SDK auth client can share tokens if the same OAuth provider is used.
|
|
476
476
|
- The Pega rendering container can be a modal, a sidebar, an inline section, or a full page. Which one is not a matter of taste — it is resolved in Step 0.2 from what the user asked for or from what their application already does.
|