@salesforce/b2c-dx-mcp 2.0.0 → 2.1.1

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.
Files changed (124) hide show
  1. package/README.md +15 -62
  2. package/dist/commands/mcp.d.ts +1 -0
  3. package/dist/commands/mcp.js +41 -9
  4. package/dist/registry.d.ts +3 -4
  5. package/dist/registry.js +7 -14
  6. package/dist/server.d.ts +1 -1
  7. package/dist/server.js +3 -1
  8. package/dist/services.d.ts +21 -1
  9. package/dist/services.js +39 -6
  10. package/dist/tools/adapter.d.ts +18 -2
  11. package/dist/tools/adapter.js +49 -10
  12. package/dist/tools/cartridges/index.js +16 -22
  13. package/dist/tools/diagnostics/config-inspect.js +4 -8
  14. package/dist/tools/diagnostics/debug-capture-at-breakpoint.js +3 -9
  15. package/dist/tools/diagnostics/debug-list-sessions.js +1 -0
  16. package/dist/tools/diagnostics/debug-start-session.js +16 -9
  17. package/dist/tools/diagnostics/debug-wait-for-stop.js +1 -3
  18. package/dist/tools/diagnostics/log-watch-registry.d.ts +3 -0
  19. package/dist/tools/diagnostics/log-watch-registry.js +1 -0
  20. package/dist/tools/diagnostics/logs-get-recent.js +2 -5
  21. package/dist/tools/diagnostics/logs-list-files.js +1 -1
  22. package/dist/tools/diagnostics/logs-watch-list.js +1 -0
  23. package/dist/tools/diagnostics/logs-watch-start.js +10 -10
  24. package/dist/tools/diagnostics/mrt-log-watch-registry.d.ts +3 -0
  25. package/dist/tools/diagnostics/mrt-log-watch-registry.js +0 -0
  26. package/dist/tools/diagnostics/mrt-logs-watch-list.js +1 -0
  27. package/dist/tools/diagnostics/mrt-logs-watch-poll.js +2 -5
  28. package/dist/tools/diagnostics/mrt-logs-watch-start.js +9 -8
  29. package/dist/tools/diagnostics/session-registry.d.ts +3 -0
  30. package/dist/tools/diagnostics/session-registry.js +2 -1
  31. package/dist/tools/docs/docs-list.js +4 -6
  32. package/dist/tools/docs/docs-read.js +2 -9
  33. package/dist/tools/docs/docs-schema-list.js +1 -1
  34. package/dist/tools/docs/docs-schema-read.js +2 -3
  35. package/dist/tools/docs/docs-schema-search.js +2 -2
  36. package/dist/tools/docs/docs-search.js +3 -9
  37. package/dist/tools/docs/storefront.js +3 -3
  38. package/dist/tools/docs/topics.js +3 -1
  39. package/dist/tools/index.d.ts +0 -1
  40. package/dist/tools/index.js +0 -1
  41. package/dist/tools/mrt/index.js +6 -2
  42. package/dist/tools/project-context.d.ts +38 -11
  43. package/dist/tools/project-context.js +32 -15
  44. package/dist/tools/pwav3/pwa-kit-development-guidelines.js +3 -43
  45. package/dist/tools/scapi/metrics-get.js +5 -23
  46. package/dist/tools/scapi/scapi-custom-api-generate-scaffold.d.ts +4 -0
  47. package/dist/tools/scapi/scapi-custom-api-generate-scaffold.js +29 -14
  48. package/dist/tools/scapi/scapi-custom-apis-get-status.js +3 -14
  49. package/dist/tools/scapi/scapi-schemas-list.js +11 -11
  50. package/dist/utils/constants.d.ts +2 -13
  51. package/dist/utils/constants.js +1 -20
  52. package/oclif.manifest.json +2 -2
  53. package/package.json +5 -5
  54. package/content/sfnext/auth.md +0 -62
  55. package/content/sfnext/components.md +0 -123
  56. package/content/sfnext/config.md +0 -180
  57. package/content/sfnext/data-fetching.md +0 -323
  58. package/content/sfnext/extensions.md +0 -80
  59. package/content/sfnext/i18n.md +0 -121
  60. package/content/sfnext/page-designer.md +0 -78
  61. package/content/sfnext/performance.md +0 -80
  62. package/content/sfnext/pitfalls.md +0 -141
  63. package/content/sfnext/quick-reference.md +0 -226
  64. package/content/sfnext/state-management.md +0 -75
  65. package/content/sfnext/styling.md +0 -51
  66. package/content/sfnext/testing.md +0 -232
  67. package/dist/tools/storefrontnext/figma/figma-to-component/figma-url-parser.d.ts +0 -24
  68. package/dist/tools/storefrontnext/figma/figma-to-component/figma-url-parser.js +0 -53
  69. package/dist/tools/storefrontnext/figma/figma-to-component/index.d.ts +0 -45
  70. package/dist/tools/storefrontnext/figma/figma-to-component/index.js +0 -332
  71. package/dist/tools/storefrontnext/figma/generate-component/decision.d.ts +0 -40
  72. package/dist/tools/storefrontnext/figma/generate-component/decision.js +0 -312
  73. package/dist/tools/storefrontnext/figma/generate-component/formatter.d.ts +0 -9
  74. package/dist/tools/storefrontnext/figma/generate-component/formatter.js +0 -92
  75. package/dist/tools/storefrontnext/figma/generate-component/index.d.ts +0 -117
  76. package/dist/tools/storefrontnext/figma/generate-component/index.js +0 -102
  77. package/dist/tools/storefrontnext/figma/map-tokens/css-parser.d.ts +0 -71
  78. package/dist/tools/storefrontnext/figma/map-tokens/css-parser.js +0 -260
  79. package/dist/tools/storefrontnext/figma/map-tokens/index.d.ts +0 -64
  80. package/dist/tools/storefrontnext/figma/map-tokens/index.js +0 -242
  81. package/dist/tools/storefrontnext/figma/map-tokens/token-matcher.d.ts +0 -65
  82. package/dist/tools/storefrontnext/figma/map-tokens/token-matcher.js +0 -268
  83. package/dist/tools/storefrontnext/index.d.ts +0 -30
  84. package/dist/tools/storefrontnext/index.js +0 -28
  85. package/dist/tools/storefrontnext/page-designer-decorator/analyzer.d.ts +0 -169
  86. package/dist/tools/storefrontnext/page-designer-decorator/analyzer.js +0 -567
  87. package/dist/tools/storefrontnext/page-designer-decorator/index.d.ts +0 -259
  88. package/dist/tools/storefrontnext/page-designer-decorator/index.js +0 -622
  89. package/dist/tools/storefrontnext/page-designer-decorator/rules/1-mode-selection.d.ts +0 -8
  90. package/dist/tools/storefrontnext/page-designer-decorator/rules/1-mode-selection.js +0 -65
  91. package/dist/tools/storefrontnext/page-designer-decorator/rules/2a-auto-mode.d.ts +0 -13
  92. package/dist/tools/storefrontnext/page-designer-decorator/rules/2a-auto-mode.js +0 -87
  93. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-0-interactive-overview.d.ts +0 -4
  94. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-0-interactive-overview.js +0 -55
  95. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-1-interactive-analyze.d.ts +0 -22
  96. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-1-interactive-analyze.js +0 -109
  97. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-2-interactive-select-props.d.ts +0 -21
  98. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-2-interactive-select-props.js +0 -60
  99. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-3-interactive-configure-attrs.d.ts +0 -27
  100. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-3-interactive-configure-attrs.js +0 -68
  101. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-4-interactive-configure-regions.d.ts +0 -4
  102. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-4-interactive-configure-regions.js +0 -65
  103. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-5-interactive-confirm-generation.d.ts +0 -11
  104. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-5-interactive-confirm-generation.js +0 -92
  105. package/dist/tools/storefrontnext/page-designer-decorator/rules.d.ts +0 -51
  106. package/dist/tools/storefrontnext/page-designer-decorator/rules.js +0 -70
  107. package/dist/tools/storefrontnext/page-designer-decorator/templates/decorator-generator.d.ts +0 -116
  108. package/dist/tools/storefrontnext/page-designer-decorator/templates/decorator-generator.js +0 -350
  109. package/dist/tools/storefrontnext/sfnext-development-guidelines.d.ts +0 -9
  110. package/dist/tools/storefrontnext/sfnext-development-guidelines.js +0 -141
  111. package/dist/tools/storefrontnext/site-theming/color-contrast.d.ts +0 -92
  112. package/dist/tools/storefrontnext/site-theming/color-contrast.js +0 -186
  113. package/dist/tools/storefrontnext/site-theming/color-mapping.d.ts +0 -16
  114. package/dist/tools/storefrontnext/site-theming/color-mapping.js +0 -131
  115. package/dist/tools/storefrontnext/site-theming/guidance-merger.d.ts +0 -11
  116. package/dist/tools/storefrontnext/site-theming/guidance-merger.js +0 -78
  117. package/dist/tools/storefrontnext/site-theming/index.d.ts +0 -14
  118. package/dist/tools/storefrontnext/site-theming/index.js +0 -128
  119. package/dist/tools/storefrontnext/site-theming/response-builder.d.ts +0 -16
  120. package/dist/tools/storefrontnext/site-theming/response-builder.js +0 -316
  121. package/dist/tools/storefrontnext/site-theming/theming-store.d.ts +0 -64
  122. package/dist/tools/storefrontnext/site-theming/theming-store.js +0 -417
  123. package/dist/tools/storefrontnext/site-theming/types.d.ts +0 -36
  124. package/dist/tools/storefrontnext/site-theming/types.js +0 -7
@@ -1,622 +0,0 @@
1
- /*
2
- * Copyright (c) 2025, Salesforce, Inc.
3
- * SPDX-License-Identifier: Apache-2
4
- * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
5
- */
6
- import { z } from 'zod';
7
- import { componentAnalyzer, generateTypeSuggestions, resolveComponent } from './analyzer.js';
8
- import { generateDecoratorCode } from './templates/decorator-generator.js';
9
- import { pageDesignerDecoratorRules } from './rules.js';
10
- import { projectContextInputSchema } from '../../project-context.js';
11
- // ============================================================================
12
- // SCHEMA DEFINITION
13
- // ============================================================================
14
- export const pageDesignerDecoratorSchema = z
15
- .object({
16
- component: z
17
- .string()
18
- .describe('Component name (e.g., "ProductItem", "ProductTile") or file path (e.g., "src/components/ProductItem.tsx"). ' +
19
- 'When a name is provided, the tool automatically searches common component directories. ' +
20
- 'For backward compatibility, file paths are also supported.'),
21
- searchPaths: z
22
- .array(z.string())
23
- .optional()
24
- .describe('Additional directories to search for components (e.g., ["packages/retail/src", "app/features"]). ' +
25
- 'Only used when component is specified by name (not path).'),
26
- autoMode: z
27
- .boolean()
28
- .optional()
29
- .describe('Auto-generate all configurations with sensible defaults (skip interactive workflow). When enabled, automatically selects suitable props, infers types, and generates decorators without user confirmation.'),
30
- componentId: z.string().optional().describe('Override component ID (default: auto-generated from component name)'),
31
- conversationContext: z
32
- .object({
33
- step: z
34
- .enum(['analyze', 'select_props', 'configure_attrs', 'configure_regions', 'confirm_generation'])
35
- .optional()
36
- .describe('Current step in the conversation workflow'),
37
- componentInfo: z
38
- .record(z.string(), z.any())
39
- .optional()
40
- .describe('Cached component analysis from previous step'),
41
- selectedProps: z
42
- .array(z.string())
43
- .optional()
44
- .describe('Props from component interface selected to expose in Page Designer'),
45
- newAttributes: z
46
- .array(z.object({
47
- name: z.string(),
48
- description: z.string().optional(),
49
- required: z.boolean().optional(),
50
- }))
51
- .optional()
52
- .describe('New attributes to add (not in existing props)'),
53
- attributeConfig: z
54
- .record(z.string(), z.object({
55
- type: z.string().optional(),
56
- name: z.string().optional(),
57
- defaultValue: z.any().optional(),
58
- values: z.array(z.string()).optional(),
59
- }))
60
- .optional()
61
- .describe('Configuration for each attribute (explicit types, names, etc.)'),
62
- componentMetadata: z
63
- .object({
64
- id: z.string(),
65
- name: z.string(),
66
- description: z.string(),
67
- group: z.string().optional(),
68
- })
69
- .optional()
70
- .describe('Component decorator configuration'),
71
- regionConfig: z
72
- .object({
73
- enabled: z.boolean().describe('Whether to include @RegionDefinition decorator'),
74
- regions: z
75
- .array(z.object({
76
- id: z.string().describe('Region identifier (e.g., "main", "sidebar")'),
77
- name: z.string().describe('Display name for the region'),
78
- description: z.string().optional().describe('Description of the region purpose'),
79
- maxComponents: z.number().optional().describe('Maximum number of components allowed in region'),
80
- componentTypeInclusions: z
81
- .array(z.string())
82
- .optional()
83
- .describe('Allowed component types (whitelist)'),
84
- componentTypeExclusions: z
85
- .array(z.string())
86
- .optional()
87
- .describe('Disallowed component types (blacklist)'),
88
- }))
89
- .optional()
90
- .describe('Array of region definitions'),
91
- })
92
- .optional()
93
- .describe('Region configuration for nested content areas'),
94
- })
95
- .optional()
96
- .describe('Conversation state for multi-turn interaction'),
97
- ...projectContextInputSchema,
98
- })
99
- .strict();
100
- // ============================================================================
101
- // HELPER FUNCTIONS
102
- // ============================================================================
103
- /**
104
- * Convert component name to kebab-case for use as component ID
105
- *
106
- * Page Designer component IDs should be lowercase with hyphens.
107
- *
108
- * @param name - PascalCase or camelCase name
109
- * @returns kebab-case identifier
110
- *
111
- * @example
112
- * toKebabCase('ProductCard') // => 'product-card'
113
- * toKebabCase('TwoColumnLayout') // => 'two-column-layout'
114
- *
115
- * @internal
116
- */
117
- function toKebabCase(name) {
118
- return name
119
- .replaceAll(/([a-z])([A-Z])/g, '$1-$2')
120
- .replaceAll(/[\s_]+/g, '-')
121
- .toLowerCase();
122
- }
123
- /**
124
- * Convert camelCase prop name to human-readable display name
125
- *
126
- * Used for attribute names shown to merchants in Page Designer UI.
127
- *
128
- * @param fieldName - camelCase field name
129
- * @returns Human-readable name with proper capitalization
130
- *
131
- * @example
132
- * toHumanReadableName('imageUrl') // => 'Image Url'
133
- * toHumanReadableName('ctaButtonText') // => 'Cta Button Text'
134
- *
135
- * @internal
136
- */
137
- function toHumanReadableName(fieldName) {
138
- return fieldName
139
- .replaceAll(/([A-Z])/g, ' $1')
140
- .replace(/^./, (str) => str.toUpperCase())
141
- .trim();
142
- }
143
- // ============================================================================
144
- // WORKFLOW STEP HANDLERS
145
- // ============================================================================
146
- /**
147
- * Handle Interactive Mode - Step 1: Analyze
148
- *
149
- * Parses the component file and provides analysis to the LLM:
150
- * - Component name and structure
151
- * - All props with types
152
- * - Categorization (editable, complex, UI-only)
153
- * - Suggested component ID and name
154
- *
155
- * **LLM should then:**
156
- * - Present findings to user
157
- * - Ask which props to expose in Page Designer
158
- * - Collect component metadata (ID, name, description, group)
159
- * - Call next step with selectedProps and componentMetadata
160
- *
161
- * @internal
162
- */
163
- function handleAnalyzeStep(args, workspaceRoot) {
164
- const fullPath = resolveComponent(args.component, workspaceRoot, args.searchPaths);
165
- const componentInfo = componentAnalyzer.analyzeComponent(fullPath);
166
- const editableProps = componentInfo.props.filter((p) => !p.isComplex && !p.isUIOnly);
167
- const complexProps = componentInfo.props.filter((p) => p.isComplex);
168
- const uiProps = componentInfo.props.filter((p) => p.isUIOnly && !p.isComplex);
169
- const suggestedComponentId = args.componentId || toKebabCase(componentInfo.componentName);
170
- const suggestedComponentName = toHumanReadableName(componentInfo.componentName);
171
- const instructions = pageDesignerDecoratorRules.getAnalyzeInstructions({
172
- componentName: componentInfo.componentName,
173
- file: args.component,
174
- hasDecorators: componentInfo.hasDecorators,
175
- interfaceName: componentInfo.interfaceName || 'None found',
176
- totalProps: componentInfo.props.length,
177
- exportType: componentInfo.exportType,
178
- hasEditableProps: editableProps.length > 0,
179
- editableProps,
180
- hasComplexProps: complexProps.length > 0,
181
- complexProps,
182
- hasUIProps: uiProps.length > 0,
183
- uiProps,
184
- suggestedComponentId,
185
- suggestedComponentName,
186
- });
187
- return {
188
- content: [
189
- {
190
- type: 'text',
191
- text: instructions,
192
- },
193
- ],
194
- };
195
- }
196
- function handleSelectPropsStep(args, _workspaceRoot) {
197
- const selectedProps = args.conversationContext?.selectedProps || [];
198
- const newAttributes = args.conversationContext?.newAttributes || [];
199
- const componentMetadata = args.conversationContext?.componentMetadata;
200
- if (!componentMetadata) {
201
- return {
202
- content: [
203
- {
204
- type: 'text',
205
- text: '⚠️ Missing component metadata. Please provide component ID, name, description, and group from the analyze step.',
206
- },
207
- ],
208
- isError: true,
209
- };
210
- }
211
- const confirmation = pageDesignerDecoratorRules.getSelectPropsConfirmation({
212
- componentMetadata: {
213
- id: componentMetadata.id,
214
- name: componentMetadata.name,
215
- description: componentMetadata.description,
216
- group: componentMetadata.group || 'odyssey_base',
217
- },
218
- selectedProps,
219
- newAttributes,
220
- selectedPropsCount: selectedProps.length,
221
- newAttributesCount: newAttributes.length,
222
- totalAttributeCount: selectedProps.length + newAttributes.length,
223
- hasSelectedProps: selectedProps.length > 0,
224
- hasNewAttributes: newAttributes.length > 0,
225
- });
226
- return {
227
- content: [
228
- {
229
- type: 'text',
230
- text: confirmation,
231
- },
232
- ],
233
- };
234
- }
235
- function handleConfigureAttrsStep(args, workspaceRoot) {
236
- const selectedProps = args.conversationContext?.selectedProps || [];
237
- const newAttributes = args.conversationContext?.newAttributes || [];
238
- const fullPath = resolveComponent(args.component, workspaceRoot, args.searchPaths);
239
- const componentInfo = componentAnalyzer.analyzeComponent(fullPath);
240
- const attributeAnalysis = [];
241
- for (const propName of selectedProps) {
242
- const prop = componentInfo.props.find((p) => p.name === propName);
243
- if (!prop)
244
- continue;
245
- const suggestions = generateTypeSuggestions(propName, prop.type);
246
- attributeAnalysis.push({
247
- name: propName,
248
- source: 'existing',
249
- tsType: prop.type,
250
- autoInferred: suggestions.length === 0,
251
- suggestions,
252
- });
253
- }
254
- for (const attr of newAttributes) {
255
- const suggestions = generateTypeSuggestions(attr.name, 'string');
256
- attributeAnalysis.push({
257
- name: attr.name,
258
- source: 'new',
259
- tsType: 'string',
260
- autoInferred: suggestions.length === 0,
261
- suggestions,
262
- });
263
- }
264
- const autoInferredAttrs = attributeAnalysis.filter((a) => a.autoInferred);
265
- const needsConfigAttrs = attributeAnalysis.filter((a) => !a.autoInferred);
266
- const instructions = pageDesignerDecoratorRules.getConfigureAttrsInstructions({
267
- totalAttributes: attributeAnalysis.length,
268
- autoInferredCount: autoInferredAttrs.length,
269
- needsConfigCount: needsConfigAttrs.length,
270
- hasAutoInferred: autoInferredAttrs.length > 0,
271
- autoInferredAttrs: autoInferredAttrs.map((a) => ({ name: a.name, tsType: a.tsType })),
272
- hasNeedsConfig: needsConfigAttrs.length > 0,
273
- needsConfigAttrs: needsConfigAttrs.map((attr) => ({
274
- name: attr.name,
275
- tsType: attr.tsType,
276
- source: attr.source === 'existing' ? 'Existing prop' : 'New attribute',
277
- hasSuggestions: attr.suggestions.length > 0,
278
- suggestions: attr.suggestions,
279
- suggestedTypes: attr.suggestions.map((s) => s.type).join(', ') || 'string',
280
- humanReadableName: toHumanReadableName(attr.name),
281
- hasEnumSuggestion: attr.suggestions.some((s) => s.type === 'enum'),
282
- })),
283
- });
284
- return {
285
- content: [
286
- {
287
- type: 'text',
288
- text: instructions,
289
- },
290
- ],
291
- };
292
- }
293
- function handleConfigureRegionsStep(args, workspaceRoot) {
294
- const fullPath = resolveComponent(args.component, workspaceRoot, args.searchPaths);
295
- const componentInfo = componentAnalyzer.analyzeComponent(fullPath);
296
- const instructions = pageDesignerDecoratorRules.getConfigureRegionsInstructions({
297
- componentName: componentInfo.componentName,
298
- });
299
- return {
300
- content: [
301
- {
302
- type: 'text',
303
- text: instructions,
304
- },
305
- ],
306
- };
307
- }
308
- function hasNonEmptyConfig(config) {
309
- return config !== null && config !== undefined && Object.keys(config).length > 0;
310
- }
311
- function buildAttributesFromProps(selectedProps, props, attributeConfig) {
312
- return selectedProps.flatMap((propName) => {
313
- const prop = props.find((p) => p.name === propName);
314
- if (!prop)
315
- return [];
316
- const config = attributeConfig[propName];
317
- return [
318
- {
319
- name: propName,
320
- tsType: prop.type,
321
- optional: prop.optional,
322
- hasConfig: hasNonEmptyConfig(config),
323
- config,
324
- },
325
- ];
326
- });
327
- }
328
- function buildAttributesFromNewAttrs(newAttributes, attributeConfig) {
329
- return newAttributes.map((attr) => {
330
- const config = attributeConfig[attr.name];
331
- return {
332
- name: attr.name,
333
- tsType: 'string',
334
- optional: !attr.required,
335
- hasConfig: hasNonEmptyConfig(config),
336
- config,
337
- };
338
- });
339
- }
340
- function resolveRegions(conversationContext) {
341
- const regionConfig = conversationContext?.regionConfig;
342
- const enabled = Boolean(regionConfig?.enabled && regionConfig.regions?.length);
343
- return {
344
- hasRegions: enabled,
345
- regions: enabled ? regionConfig.regions : [],
346
- regionCount: enabled ? regionConfig.regions.length : 0,
347
- };
348
- }
349
- function handleConfirmGenerationStep(args, workspaceRoot) {
350
- const { componentMetadata, selectedProps = [], newAttributes = [], attributeConfig = {}, } = args.conversationContext ?? {};
351
- if (!componentMetadata) {
352
- return {
353
- content: [
354
- {
355
- type: 'text',
356
- text: 'Error: Missing component metadata. Please start from the beginning.',
357
- },
358
- ],
359
- isError: true,
360
- };
361
- }
362
- const fullPath = resolveComponent(args.component, workspaceRoot, args.searchPaths);
363
- const componentInfo = componentAnalyzer.analyzeComponent(fullPath);
364
- const attributes = [
365
- ...buildAttributesFromProps(selectedProps, componentInfo.props, attributeConfig),
366
- ...buildAttributesFromNewAttrs(newAttributes, attributeConfig),
367
- ];
368
- const { hasRegions, regions, regionCount } = resolveRegions(args.conversationContext);
369
- const componentGroup = componentMetadata.group ?? 'odyssey_base';
370
- const context = {
371
- needsImports: true,
372
- componentId: componentMetadata.id,
373
- componentName: componentMetadata.name,
374
- componentDescription: componentMetadata.description,
375
- componentGroup,
376
- metadataClassName: `${componentInfo.componentName}Metadata`,
377
- hasAttributes: attributes.length > 0,
378
- hasRegions,
379
- hasLoader: false,
380
- regions,
381
- attributes,
382
- };
383
- const decoratorCode = generateDecoratorCode(context);
384
- const userResponse = pageDesignerDecoratorRules.getConfirmGenerationInstructions({
385
- decoratorCode,
386
- componentName: componentInfo.componentName,
387
- componentId: componentMetadata.id,
388
- componentGroup,
389
- file: args.component,
390
- attributeCount: attributes.length,
391
- hasRegions,
392
- regionCount,
393
- });
394
- return {
395
- content: [
396
- {
397
- type: 'text',
398
- text: userResponse,
399
- },
400
- ],
401
- };
402
- }
403
- /**
404
- * Handle Auto Mode - Single-step decorator generation
405
- *
406
- * **Fully automated workflow:**
407
- * 1. Analyzes component
408
- * 2. Auto-selects suitable props (excludes complex and UI-only)
409
- * 3. Auto-infers Page Designer types from naming patterns
410
- * 4. Generates decorator code immediately
411
- * 5. NO user interaction required
412
- *
413
- * **Selection criteria:**
414
- * - ✅ Simple types (string, number, boolean)
415
- * - ❌ Complex types (objects, functions, React nodes)
416
- * - ❌ UI-only props (className, style, onClick, etc.)
417
- *
418
- * **Auto-configuration:**
419
- * - High-confidence patterns get explicit types (url, image, enum)
420
- * - Others use auto-inferred types
421
- * - Human-readable names auto-generated
422
- * - No regions configured (interactive mode for advanced features)
423
- *
424
- * **Use cases:**
425
- * - Quick setup for standard components
426
- * - Batch processing multiple components
427
- * - Getting started quickly
428
- *
429
- * @internal
430
- */
431
- function handleAutoMode(args, workspaceRoot) {
432
- const fullPath = resolveComponent(args.component, workspaceRoot, args.searchPaths);
433
- const componentInfo = componentAnalyzer.analyzeComponent(fullPath);
434
- if (componentInfo.hasDecorators) {
435
- return {
436
- content: [
437
- {
438
- type: 'text',
439
- text: `# ⚠️ Component Already Decorated\n\nThe component \`${componentInfo.componentName}\` already has Page Designer decorators.\n\nWould you like to modify the existing decorators instead?`,
440
- },
441
- ],
442
- };
443
- }
444
- const selectedProps = componentInfo.props.filter((p) => !p.isComplex && !p.isUIOnly).map((p) => p.name);
445
- const attributeConfig = {};
446
- const attributes = [];
447
- for (const propName of selectedProps) {
448
- const prop = componentInfo.props.find((p) => p.name === propName);
449
- if (!prop)
450
- continue;
451
- const suggestions = generateTypeSuggestions(propName, prop.type);
452
- const config = {
453
- name: toHumanReadableName(propName),
454
- };
455
- const highPrioritySuggestion = suggestions.find((s) => s.priority === 'high');
456
- if (highPrioritySuggestion) {
457
- config.type = highPrioritySuggestion.type;
458
- if (highPrioritySuggestion.type === 'enum') {
459
- if (propName.toLowerCase().includes('size')) {
460
- config.values = ['sm', 'default', 'lg'];
461
- config.defaultValue = 'default';
462
- }
463
- else if (propName.toLowerCase().includes('variant')) {
464
- config.values = ['default', 'primary', 'secondary'];
465
- config.defaultValue = 'default';
466
- }
467
- }
468
- if (highPrioritySuggestion.type === 'boolean') {
469
- config.defaultValue = false;
470
- }
471
- }
472
- if (Object.keys(config).length > 1) {
473
- attributeConfig[propName] = config;
474
- }
475
- attributes.push({
476
- name: propName,
477
- tsType: prop.type,
478
- optional: prop.optional,
479
- hasConfig: Object.keys(config).length > 1,
480
- config: Object.keys(config).length > 1 ? config : undefined,
481
- });
482
- }
483
- const componentId = args.componentId || toKebabCase(componentInfo.componentName);
484
- const componentName = toHumanReadableName(componentInfo.componentName);
485
- const componentDescription = `${componentName} component for Page Designer`;
486
- const context = {
487
- needsImports: true,
488
- componentId,
489
- componentName,
490
- componentDescription,
491
- componentGroup: 'odyssey_base',
492
- metadataClassName: `${componentInfo.componentName}Metadata`,
493
- hasAttributes: attributes.length > 0,
494
- hasRegions: false,
495
- hasLoader: false,
496
- regions: [],
497
- attributes,
498
- };
499
- const decoratorCode = generateDecoratorCode(context);
500
- const response = pageDesignerDecoratorRules.getAutoModeInstructions({
501
- componentName: componentInfo.componentName,
502
- file: args.component,
503
- componentId,
504
- selectedPropCount: selectedProps.length,
505
- autoConfigCount: Object.keys(attributeConfig).length,
506
- autoInferredCount: selectedProps.length - Object.keys(attributeConfig).length,
507
- hasNoSuitableProps: selectedProps.length === 0,
508
- selectedProps: selectedProps.length > 0 ? selectedProps.map((p) => `\`${p}\``).join(', ') : 'None',
509
- decoratorCode,
510
- componentGroup: 'odyssey_base',
511
- });
512
- return {
513
- content: [
514
- {
515
- type: 'text',
516
- text: response,
517
- },
518
- ],
519
- };
520
- }
521
- // ============================================================================
522
- // TOOL EXPORT
523
- // ============================================================================
524
- /**
525
- * Creates the Page Designer decorator tool for Storefront Next.
526
- *
527
- * @param loadServices - Function that loads configuration and returns Services instance
528
- * @returns The configured MCP tool
529
- */
530
- export function createPageDesignerDecoratorTool(loadServices) {
531
- return {
532
- name: 'sfnext_add_page_designer_decorator',
533
- description: '[DEPRECATED] Superseded by the storefront-next and storefront-next-figma agent-skills plugins and NOT compatible with the Storefront Next 1.0 GA release. Will be removed in a future release. ' +
534
- 'Adds Page Designer decorators (@Component, @AttributeDefinition, @RegionDefinition) to React components. ' +
535
- 'Two modes: autoMode=true for quick setup with defaults, or interactive mode via conversationContext.step. ' +
536
- 'Component discovery uses --project-directory flag or SFCC_PROJECT_DIRECTORY env var. ' +
537
- 'Auto mode: selects suitable props, infers types, generates code immediately. ' +
538
- 'Interactive mode: multi-step workflow (analyze → select_props → configure_attrs → configure_regions → confirm_generation).',
539
- inputSchema: pageDesignerDecoratorSchema.shape,
540
- toolsets: ['STOREFRONTNEXT_DEPRECATED'],
541
- isGA: false,
542
- async handler(args) {
543
- try {
544
- // Validate and parse input
545
- const validatedArgs = pageDesignerDecoratorSchema.parse(args);
546
- // Use projectDirectory from services to ensure we search in the correct project directory
547
- // This prevents searches in the home folder when MCP clients spawn servers from ~
548
- const services = await loadServices({
549
- projectDirectory: validatedArgs.projectDirectory,
550
- configPath: validatedArgs.configPath,
551
- });
552
- const workspaceRoot = services.resolveWithProjectDirectory(undefined, validatedArgs.projectDirectory);
553
- if (validatedArgs.autoMode === undefined && !validatedArgs.conversationContext) {
554
- const fullPath = resolveComponent(validatedArgs.component, workspaceRoot, validatedArgs.searchPaths);
555
- const componentInfo = componentAnalyzer.analyzeComponent(fullPath);
556
- const instructions = pageDesignerDecoratorRules.getModeSelectionInstructions({
557
- componentName: componentInfo.componentName,
558
- file: validatedArgs.component,
559
- });
560
- return {
561
- content: [
562
- {
563
- type: 'text',
564
- text: instructions,
565
- },
566
- ],
567
- };
568
- }
569
- if (validatedArgs.autoMode) {
570
- return handleAutoMode(validatedArgs, workspaceRoot);
571
- }
572
- const step = validatedArgs.conversationContext?.step || 'analyze';
573
- switch (step) {
574
- case 'analyze': {
575
- return handleAnalyzeStep(validatedArgs, workspaceRoot);
576
- }
577
- case 'configure_attrs': {
578
- return handleConfigureAttrsStep(validatedArgs, workspaceRoot);
579
- }
580
- case 'configure_regions': {
581
- return handleConfigureRegionsStep(validatedArgs, workspaceRoot);
582
- }
583
- case 'confirm_generation': {
584
- return handleConfirmGenerationStep(validatedArgs, workspaceRoot);
585
- }
586
- case 'select_props': {
587
- return handleSelectPropsStep(validatedArgs, workspaceRoot);
588
- }
589
- default: {
590
- const unknownStep = step;
591
- throw new Error(`Unknown step: ${unknownStep}`);
592
- }
593
- }
594
- }
595
- catch (error) {
596
- const errorMessage = error instanceof Error ? error.message : String(error);
597
- // Check if it's a Zod validation error
598
- if (error instanceof Error && error.name === 'ZodError') {
599
- return {
600
- content: [
601
- {
602
- type: 'text',
603
- text: `# Error: Invalid Input\n\n${errorMessage}\n\nPlease check your input parameters and try again.`,
604
- },
605
- ],
606
- isError: true,
607
- };
608
- }
609
- return {
610
- content: [
611
- {
612
- type: 'text',
613
- text: `# Error Adding Page Designer Support\n\n${errorMessage}`,
614
- },
615
- ],
616
- isError: true,
617
- };
618
- }
619
- },
620
- };
621
- }
622
- //# sourceMappingURL=index.js.map
@@ -1,8 +0,0 @@
1
- /**
2
- * Mode selection rule - Entry point for Page Designer decorator tool
3
- */
4
- export interface ModeSelectionContext {
5
- componentName: string;
6
- file: string;
7
- }
8
- export declare function renderModeSelection(context: ModeSelectionContext): string;
@@ -1,65 +0,0 @@
1
- /*
2
- * Copyright (c) 2025, Salesforce, Inc.
3
- * SPDX-License-Identifier: Apache-2
4
- * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
5
- */
6
- export function renderModeSelection(context) {
7
- return `# 🎯 Choose Page Designer Setup Mode
8
-
9
- I need to know which mode you'd like to use for adding Page Designer support to **\`${context.componentName}\`**.
10
-
11
- ## Available Modes
12
-
13
- ### 🤖 Auto Mode (Quick & Automatic)
14
- - **Best for**: Quick setup, standard components, batch processing
15
- - **What happens**:
16
- - Automatically analyzes the component
17
- - Auto-selects suitable props (excludes complex types)
18
- - Auto-infers types based on naming patterns
19
- - Generates decorators immediately with sensible defaults
20
- - **No confirmation needed** - code generated instantly
21
- - **Time**: ~1 step
22
- - **Control**: Low (uses smart defaults)
23
-
24
- ### 👤 Interactive Mode (Step-by-Step)
25
- - **Best for**: Complex components, custom requirements, learning the process
26
- - **What happens**:
27
- - Multi-step workflow with your input at each stage
28
- - Review and approve prop selections
29
- - Configure attribute types, names, and defaults
30
- - Configure regions for nested content (optional)
31
- - **Requires confirmation** before generating code
32
- - **Time**: ~4-5 steps
33
- - **Control**: High (you decide everything)
34
-
35
- ## ⚡ How to Proceed
36
-
37
- **⚠️ IMPORTANT: WAIT for the user to choose a mode. DO NOT proceed automatically.**
38
-
39
- Please ask the user: **"Which mode would you like to use: Auto Mode or Interactive Mode?"**
40
-
41
- Once the user responds:
42
-
43
- **For Auto Mode**, call the tool again with:
44
- \`\`\`json
45
- {
46
- "file": "${context.file}",
47
- "autoMode": true
48
- }
49
- \`\`\`
50
-
51
- **For Interactive Mode**, call the tool again with:
52
- \`\`\`json
53
- {
54
- "file": "${context.file}",
55
- "conversationContext": {
56
- "step": "analyze"
57
- }
58
- }
59
- \`\`\`
60
-
61
- ---
62
-
63
- 💡 **Tip**: If unsure, try **Auto Mode** first. You can always modify the generated decorators later or rerun in Interactive Mode for more control.`;
64
- }
65
- //# sourceMappingURL=1-mode-selection.js.map