@fundamental-styles/mcp 0.41.6 → 0.41.7-rc.0

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/src/server.js CHANGED
@@ -25,7 +25,9 @@ catch (_a) {
25
25
  accessibility: null,
26
26
  utilityClasses: null,
27
27
  designTokens: [],
28
- htmlExamples: null
28
+ htmlExamples: null,
29
+ componentUseCases: null,
30
+ componentGuidance: null
29
31
  };
30
32
  console.error('Warning: Failed to load component catalog data.');
31
33
  }
@@ -74,13 +76,19 @@ Use this to discover what CSS components are available.`, {
74
76
  ((_b = c.subcategory) === null || _b === void 0 ? void 0 : _b.toLowerCase().includes(lowerCategory));
75
77
  });
76
78
  }
77
- const summary = components.map((c) => ({
78
- id: c.id,
79
- name: c.name,
80
- baseClass: c.baseClass,
81
- category: c.category,
82
- description: (0, helpers_1.truncate)(c.description, 120)
83
- }));
79
+ const summary = components.map((c) => {
80
+ var _a;
81
+ // Try to get enhanced description from component guidance
82
+ const guidance = (_a = data.componentGuidance) === null || _a === void 0 ? void 0 : _a.components[c.id];
83
+ const description = (guidance === null || guidance === void 0 ? void 0 : guidance.description) || c.description;
84
+ return {
85
+ id: c.id,
86
+ name: c.name,
87
+ baseClass: c.baseClass,
88
+ category: (guidance === null || guidance === void 0 ? void 0 : guidance.category) || c.category,
89
+ description: (0, helpers_1.truncate)(description, 120)
90
+ };
91
+ });
84
92
  return {
85
93
  content: [
86
94
  {
@@ -532,6 +540,82 @@ Use this when building complex layouts to understand component composition.`, {
532
540
  };
533
541
  }));
534
542
  // ---------------------------------------------------------------------------
543
+ // Tool: get_component_guidance
544
+ // ---------------------------------------------------------------------------
545
+ server.tool('get_component_guidance', `Get Fiori Design Guidelines for a component including when to use it, when to avoid it, and best practices.
546
+ Returns detailed guidance from SAP Fiori Design System including use cases, anti-patterns, and design tips.
547
+ Use this when you need to understand if a component is appropriate for a specific use case or need design guidance.`, {
548
+ component: zod_1.z.string().describe('Component name or id (e.g., "avatar", "button", "table")')
549
+ }, (_a) => tslib_1.__awaiter(void 0, [_a], void 0, function* ({ component }) {
550
+ const comp = findComponent(component);
551
+ if (!comp) {
552
+ return {
553
+ content: [
554
+ {
555
+ type: 'text',
556
+ text: `Component "${component}" not found. Use search_components to find available components.`
557
+ }
558
+ ]
559
+ };
560
+ }
561
+ // Check if component guidance data is available
562
+ if (!data.componentGuidance) {
563
+ return {
564
+ content: [
565
+ {
566
+ type: 'text',
567
+ text: JSON.stringify({ component: comp.name, error: 'Component guidance data not available.' }, null, 2)
568
+ }
569
+ ]
570
+ };
571
+ }
572
+ // Get guidance from the new comprehensive guidance file
573
+ const guidance = data.componentGuidance.components[comp.id];
574
+ if (!guidance) {
575
+ return {
576
+ content: [
577
+ {
578
+ type: 'text',
579
+ text: JSON.stringify({
580
+ component: comp.name,
581
+ baseClass: comp.baseClass,
582
+ note: 'No specific Fiori design guidance available for this component yet.',
583
+ suggestion: 'Check the component catalog or Fiori Design Guidelines website.'
584
+ }, null, 2)
585
+ }
586
+ ]
587
+ };
588
+ }
589
+ const result = {
590
+ component: comp.name,
591
+ baseClass: comp.baseClass,
592
+ category: guidance.category
593
+ };
594
+ if (guidance.description) {
595
+ result['description'] = guidance.description;
596
+ }
597
+ if (guidance.whenToUse && guidance.whenToUse.length > 0) {
598
+ result['whenToUse'] = guidance.whenToUse;
599
+ }
600
+ if (guidance.whenToAvoid && guidance.whenToAvoid.length > 0) {
601
+ result['whenToAvoid'] = guidance.whenToAvoid;
602
+ }
603
+ if (guidance.bestPractices && guidance.bestPractices.length > 0) {
604
+ result['bestPractices'] = guidance.bestPractices;
605
+ }
606
+ if (guidance.relatedComponents && guidance.relatedComponents.length > 0) {
607
+ result['relatedComponents'] = guidance.relatedComponents;
608
+ }
609
+ return {
610
+ content: [
611
+ {
612
+ type: 'text',
613
+ text: JSON.stringify(result, null, 2)
614
+ }
615
+ ]
616
+ };
617
+ }));
618
+ // ---------------------------------------------------------------------------
535
619
  // Tool: get_theme_info
536
620
  // ---------------------------------------------------------------------------
537
621
  server.tool('get_theme_info', `List available SAP design themes for fundamental-styles and their descriptions.
@@ -675,6 +759,452 @@ or checking what changed between releases.`, {
675
759
  ]
676
760
  };
677
761
  }));
762
+ // ---------------------------------------------------------------------------
763
+ // Tool: setup_project
764
+ // ---------------------------------------------------------------------------
765
+ server.tool('setup_project', `Get comprehensive setup instructions for fundamental-styles in a new project.
766
+ Returns detailed step-by-step instructions for both CDN and NPM installation approaches,
767
+ including theme setup, font configuration, component imports, and best practices.
768
+ Use this when starting a new project or helping users get started with fundamental-styles.`, {
769
+ approach: zod_1.z
770
+ .enum(['cdn', 'npm', 'both'])
771
+ .default('both')
772
+ .describe('Installation approach: "cdn" for CDN-only, "npm" for NPM-only, "both" for both approaches'),
773
+ themes: zod_1.z
774
+ .union([
775
+ zod_1.z.enum([
776
+ 'sap_horizon',
777
+ 'sap_horizon_dark',
778
+ 'sap_horizon_hcb',
779
+ 'sap_horizon_hcw',
780
+ 'sap_fiori_3',
781
+ 'sap_fiori_3_dark',
782
+ 'sap_fiori_3_hcb',
783
+ 'sap_fiori_3_hcw',
784
+ 'all',
785
+ 'all_horizon',
786
+ 'all_fiori',
787
+ 'all_dark',
788
+ 'all_light',
789
+ 'all_high_contrast'
790
+ ]),
791
+ zod_1.z.array(zod_1.z.enum([
792
+ 'sap_horizon',
793
+ 'sap_horizon_dark',
794
+ 'sap_horizon_hcb',
795
+ 'sap_horizon_hcw',
796
+ 'sap_fiori_3',
797
+ 'sap_fiori_3_dark',
798
+ 'sap_fiori_3_hcb',
799
+ 'sap_fiori_3_hcw'
800
+ ]))
801
+ ])
802
+ .optional()
803
+ .describe('Theme(s) to include. Can be: single theme name, array of theme names, or preset ("all", "all_horizon", "all_fiori", "all_dark", "all_light", "all_high_contrast"). If not provided, defaults to "sap_horizon"'),
804
+ componentMode: zod_1.z
805
+ .enum(['all', 'selective'])
806
+ .default('selective')
807
+ .describe('Import all components or selective components (default: selective)'),
808
+ components: zod_1.z
809
+ .array(zod_1.z.string())
810
+ .optional()
811
+ .describe('Specific components to include (e.g., ["button", "input", "table"]). Only used with componentMode="selective"')
812
+ }, (_a) => tslib_1.__awaiter(void 0, [_a], void 0, function* ({ approach, themes, componentMode, components }) {
813
+ const componentList = components || ['button', 'input', 'form-item', 'form-label'];
814
+ // All available themes
815
+ const allThemes = [
816
+ 'sap_horizon',
817
+ 'sap_horizon_dark',
818
+ 'sap_horizon_hcb',
819
+ 'sap_horizon_hcw',
820
+ 'sap_fiori_3',
821
+ 'sap_fiori_3_dark',
822
+ 'sap_fiori_3_hcb',
823
+ 'sap_fiori_3_hcw'
824
+ ];
825
+ // Theme presets
826
+ const horizonThemes = ['sap_horizon', 'sap_horizon_dark', 'sap_horizon_hcb', 'sap_horizon_hcw'];
827
+ const fioriThemes = ['sap_fiori_3', 'sap_fiori_3_dark', 'sap_fiori_3_hcb', 'sap_fiori_3_hcw'];
828
+ const darkThemes = ['sap_horizon_dark', 'sap_fiori_3_dark'];
829
+ const lightThemes = ['sap_horizon', 'sap_fiori_3'];
830
+ const highContrastThemes = ['sap_horizon_hcb', 'sap_horizon_hcw', 'sap_fiori_3_hcb', 'sap_fiori_3_hcw'];
831
+ // Determine which themes to include
832
+ let selectedThemes;
833
+ if (!themes) {
834
+ // Default: single sap_horizon theme
835
+ selectedThemes = ['sap_horizon'];
836
+ }
837
+ else if (Array.isArray(themes)) {
838
+ // Array of specific themes
839
+ if (themes.includes('all')) {
840
+ selectedThemes = allThemes;
841
+ }
842
+ else {
843
+ selectedThemes = themes;
844
+ }
845
+ }
846
+ else {
847
+ // Single value (preset or theme name)
848
+ const preset = themes;
849
+ switch (preset) {
850
+ case 'all':
851
+ selectedThemes = allThemes;
852
+ break;
853
+ case 'all_horizon':
854
+ selectedThemes = horizonThemes;
855
+ break;
856
+ case 'all_fiori':
857
+ selectedThemes = fioriThemes;
858
+ break;
859
+ case 'all_dark':
860
+ selectedThemes = darkThemes;
861
+ break;
862
+ case 'all_light':
863
+ selectedThemes = lightThemes;
864
+ break;
865
+ case 'all_high_contrast':
866
+ selectedThemes = highContrastThemes;
867
+ break;
868
+ default:
869
+ // Single theme name
870
+ selectedThemes = [preset];
871
+ }
872
+ }
873
+ // Build theme-specific instructions for each selected theme
874
+ const themeInstructions = selectedThemes.map(theme => {
875
+ const themeBaseUrl = `https://unpkg.com/@sap-theming/theming-base-content/content/Base/baseLib/${theme}/css_variables.css`;
876
+ const themeFundUrl = `https://unpkg.com/fundamental-styles@latest/dist/theming/${theme}.css`;
877
+ const themeNpmBase = `@sap-theming/theming-base-content/content/Base/baseLib/${theme}/css_variables.css`;
878
+ const themeNpmFund = `fundamental-styles/dist/theming/${theme}.css`;
879
+ return {
880
+ theme,
881
+ cdn: {
882
+ themeBaseUrl,
883
+ themeFundUrl
884
+ },
885
+ npm: {
886
+ themeNpmBase,
887
+ themeNpmFund
888
+ }
889
+ };
890
+ });
891
+ // Use the first theme for the main example
892
+ const primaryTheme = themeInstructions[0];
893
+ const result = {
894
+ library: 'fundamental-styles',
895
+ version: data.version,
896
+ themes: selectedThemes,
897
+ primaryTheme: primaryTheme.theme,
898
+ approach: approach
899
+ };
900
+ // CDN Instructions
901
+ if (approach === 'cdn' || approach === 'both') {
902
+ const cdnComponentLinks = componentMode === 'all'
903
+ ? ['<link href="https://unpkg.com/fundamental-styles@latest/dist/fundamental-styles.css" rel="stylesheet">']
904
+ : componentList.map(c => `<link href="https://unpkg.com/fundamental-styles@latest/dist/${c}.css" rel="stylesheet">`);
905
+ result.cdn = {
906
+ description: 'CDN installation requires no build tools. Perfect for prototyping or static HTML projects.',
907
+ primaryTheme: primaryTheme.theme,
908
+ themes: themeInstructions.map(t => ({
909
+ theme: t.theme,
910
+ themeBaseUrl: t.cdn.themeBaseUrl,
911
+ themeFundUrl: t.cdn.themeFundUrl
912
+ })),
913
+ steps: [
914
+ {
915
+ step: 1,
916
+ title: 'Add Theme CSS to <head>',
917
+ description: selectedThemes.length > 1
918
+ ? `Two CSS files are REQUIRED for themes to work. Order matters. Showing setup for ${primaryTheme.theme} (primary theme). See 'themes' array for ${selectedThemes.length - 1} additional theme${selectedThemes.length > 2 ? 's' : ''}.`
919
+ : 'Two CSS files are REQUIRED for themes to work. Order matters.',
920
+ code: `<!-- 1. Theme base variables (required) -->
921
+ <link href="${primaryTheme.cdn.themeBaseUrl}" rel="stylesheet">
922
+
923
+ <!-- 2. Fundamental Styles theme customizations (required) -->
924
+ <link href="${primaryTheme.cdn.themeFundUrl}" rel="stylesheet">`,
925
+ language: 'html'
926
+ },
927
+ {
928
+ step: 2,
929
+ title: 'Add Component CSS',
930
+ description: componentMode === 'all'
931
+ ? 'Import all components in one file (larger bundle size)'
932
+ : 'Import only the components you need (smaller bundle size)',
933
+ code: cdnComponentLinks.join('\n'),
934
+ language: 'html'
935
+ },
936
+ {
937
+ step: 3,
938
+ title: 'Set Base Font Size',
939
+ description: 'Required for proper component sizing per SAP Design specifications',
940
+ code: `<style>
941
+ html {
942
+ font-size: 16px; /* Required for proper component sizing */
943
+ }
944
+ </style>`,
945
+ language: 'html'
946
+ },
947
+ {
948
+ step: 4,
949
+ title: 'Use Components in HTML',
950
+ description: 'Components use BEM naming: fd-{component}, fd-{component}--{modifier}',
951
+ code: `<button class="fd-button fd-button--emphasized">Click Me</button>
952
+ <input class="fd-input" type="text" placeholder="Enter text">`,
953
+ language: 'html'
954
+ }
955
+ ],
956
+ completeExample: `<!DOCTYPE html>
957
+ <html lang="en">
958
+ <head>
959
+ <meta charset="UTF-8">
960
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
961
+ <title>Fundamental Styles - Getting Started</title>
962
+
963
+ <!-- 1. Theme base variables (required) -->
964
+ <link href="${primaryTheme.cdn.themeBaseUrl}" rel="stylesheet">
965
+
966
+ <!-- 2. Fundamental Styles theme customizations (required) -->
967
+ <link href="${primaryTheme.cdn.themeFundUrl}" rel="stylesheet">
968
+
969
+ <!-- 3. Component CSS -->
970
+ ${cdnComponentLinks.map(l => ' ' + l).join('\n')}
971
+
972
+ <!-- 4. Base font size (required) -->
973
+ <style>
974
+ html {
975
+ font-size: 16px;
976
+ }
977
+ body {
978
+ font-family: '72', Arial, sans-serif;
979
+ padding: 2rem;
980
+ }
981
+ </style>
982
+ </head>
983
+ <body>
984
+ <h1>Hello Fundamental Styles!</h1>
985
+
986
+ <div class="fd-form-item" style="margin-bottom: 1rem;">
987
+ <label class="fd-form-label" for="name">Name</label>
988
+ <input class="fd-input" id="name" type="text" placeholder="Enter your name">
989
+ </div>
990
+
991
+ <button class="fd-button fd-button--emphasized">Submit</button>
992
+ <button class="fd-button fd-button--transparent">Cancel</button>
993
+ </body>
994
+ </html>`,
995
+ notes: [
996
+ '📌 Pin to specific version for production: https://unpkg.com/fundamental-styles@0.41.6/dist/...',
997
+ '⚠️ Always include BOTH theme CSS files (base variables + fundamental theme)',
998
+ '🎨 Fonts and icons are included automatically from @sap-theming/theming-base-content',
999
+ '🔗 No JavaScript required - fundamental-styles is CSS-only',
1000
+ selectedThemes.length > 1 ? `🎨 ${selectedThemes.length} theme${selectedThemes.length > 1 ? 's' : ''} available - see 'themes' array for alternatives` : null
1001
+ ].filter(Boolean)
1002
+ };
1003
+ }
1004
+ // NPM Instructions
1005
+ if (approach === 'npm' || approach === 'both') {
1006
+ const npmComponentImports = componentMode === 'all'
1007
+ ? ["import 'fundamental-styles/dist/fundamental-styles.css';"]
1008
+ : componentList.map(c => `import 'fundamental-styles/dist/${c}.css';`);
1009
+ result.npm = {
1010
+ description: 'NPM installation for modern build tools (Webpack, Vite, Rollup, etc.)',
1011
+ primaryTheme: primaryTheme.theme,
1012
+ themes: themeInstructions.map(t => ({
1013
+ theme: t.theme,
1014
+ themeNpmBase: t.npm.themeNpmBase,
1015
+ themeNpmFund: t.npm.themeNpmFund
1016
+ })),
1017
+ steps: [
1018
+ {
1019
+ step: 1,
1020
+ title: 'Install Packages',
1021
+ description: 'fundamental-styles requires @sap-theming/theming-base-content for themes, fonts, and icons',
1022
+ commands: [
1023
+ 'npm install fundamental-styles @sap-theming/theming-base-content'
1024
+ ],
1025
+ language: 'bash'
1026
+ },
1027
+ {
1028
+ step: 2,
1029
+ title: 'Import Theme CSS',
1030
+ description: selectedThemes.length > 1
1031
+ ? `Import in your main JavaScript/TypeScript entry point (e.g., main.js, index.js, App.tsx). Showing ${primaryTheme.theme} (primary theme). See 'themes' array for ${selectedThemes.length - 1} additional theme${selectedThemes.length > 2 ? 's' : ''}.`
1032
+ : 'Import in your main JavaScript/TypeScript entry point (e.g., main.js, index.js, App.tsx)',
1033
+ code: `// 1. Theme base variables (required)
1034
+ import '${primaryTheme.npm.themeNpmBase}';
1035
+
1036
+ // 2. Fundamental Styles theme customizations (required)
1037
+ import '${primaryTheme.npm.themeNpmFund}';`,
1038
+ language: 'javascript'
1039
+ },
1040
+ {
1041
+ step: 3,
1042
+ title: 'Import Component CSS',
1043
+ description: componentMode === 'all'
1044
+ ? 'Import all components (larger bundle, simpler imports)'
1045
+ : 'Import only what you need (smaller bundle, recommended for production)',
1046
+ code: npmComponentImports.join('\n'),
1047
+ language: 'javascript'
1048
+ },
1049
+ {
1050
+ step: 4,
1051
+ title: 'Configure Bundler for Fonts',
1052
+ description: 'Ensure your bundler can handle font files from node_modules',
1053
+ frameworks: {
1054
+ webpack: {
1055
+ description: 'Add file-loader or asset/resource rule',
1056
+ code: `// webpack.config.js
1057
+ module.exports = {
1058
+ module: {
1059
+ rules: [
1060
+ {
1061
+ test: /\\.(woff|woff2|ttf|eot|svg)$/,
1062
+ type: 'asset/resource'
1063
+ }
1064
+ ]
1065
+ }
1066
+ };`,
1067
+ language: 'javascript'
1068
+ },
1069
+ vite: {
1070
+ description: 'Vite handles fonts automatically, no configuration needed',
1071
+ code: '// No configuration needed - Vite handles fonts out of the box ✅',
1072
+ language: 'javascript'
1073
+ },
1074
+ create_react_app: {
1075
+ description: 'CRA handles fonts automatically',
1076
+ code: '// No configuration needed - CRA handles fonts out of the box ✅',
1077
+ language: 'javascript'
1078
+ },
1079
+ angular: {
1080
+ description: 'Add font paths to angular.json assets',
1081
+ code: `// angular.json
1082
+ {
1083
+ "projects": {
1084
+ "your-app": {
1085
+ "architect": {
1086
+ "build": {
1087
+ "options": {
1088
+ "assets": [
1089
+ {
1090
+ "glob": "**/*",
1091
+ "input": "node_modules/@sap-theming/theming-base-content/content/Base/baseLib/${primaryTheme.theme}/fonts",
1092
+ "output": "/fonts"
1093
+ }
1094
+ ]
1095
+ }
1096
+ }
1097
+ }
1098
+ }
1099
+ }
1100
+ }`,
1101
+ language: 'json'
1102
+ }
1103
+ }
1104
+ },
1105
+ {
1106
+ step: 5,
1107
+ title: 'Set Base Font Size',
1108
+ description: 'Add to your global CSS file (e.g., index.css, globals.css, styles.css)',
1109
+ code: `html {
1110
+ font-size: 16px; /* Required for proper component sizing */
1111
+ }
1112
+
1113
+ body {
1114
+ font-family: '72', Arial, sans-serif;
1115
+ margin: 0;
1116
+ padding: 0;
1117
+ }`,
1118
+ language: 'css'
1119
+ },
1120
+ {
1121
+ step: 6,
1122
+ title: 'Use Components',
1123
+ description: 'Use fundamental-styles classes in your HTML/JSX/templates',
1124
+ code: `<button className="fd-button fd-button--emphasized">Click Me</button>
1125
+ <input className="fd-input" type="text" placeholder="Enter text" />`,
1126
+ language: 'jsx'
1127
+ }
1128
+ ],
1129
+ notes: [
1130
+ '📦 Always install BOTH packages: fundamental-styles AND @sap-theming/theming-base-content',
1131
+ '⚠️ Import theme CSS before component CSS',
1132
+ '🎨 Fonts are located in @sap-theming/theming-base-content/content/Base/baseLib/{theme}/fonts/',
1133
+ '🔧 Most modern bundlers (Vite, CRA, Next.js) handle fonts automatically',
1134
+ '📝 For TypeScript, no @types package needed - fundamental-styles is CSS-only',
1135
+ selectedThemes.length > 1 ? `🎨 ${selectedThemes.length} theme${selectedThemes.length > 1 ? 's' : ''} available - see 'themes' array for alternatives` : null
1136
+ ].filter(Boolean)
1137
+ };
1138
+ }
1139
+ // Common information for both approaches
1140
+ result.nextSteps = {
1141
+ componentExplorer: 'https://sap.github.io/fundamental-styles/',
1142
+ documentation: [
1143
+ 'CLAUDE.md - Quick reference for AI agents: https://github.com/SAP/fundamental-styles/blob/main/CLAUDE.md',
1144
+ 'Component catalog (JSON): Use list_components tool',
1145
+ 'HTML examples: Use get_html_examples tool',
1146
+ 'Component guidance: Use get_component_guidance tool'
1147
+ ],
1148
+ commonPatterns: {
1149
+ buttons: 'fd-button, fd-button--emphasized (primary), fd-button--transparent (ghost)',
1150
+ forms: 'fd-form-item + fd-form-label + fd-input/fd-checkbox/fd-radio',
1151
+ states: 'is-error, is-success, is-warning, is-disabled, is-selected',
1152
+ sizing: 'fd-{component}--compact for dense UIs',
1153
+ icons: 'sap-icon--{icon-name} (e.g., sap-icon--add, sap-icon--delete)'
1154
+ },
1155
+ availableThemes: [
1156
+ 'sap_horizon (default, light)',
1157
+ 'sap_horizon_dark',
1158
+ 'sap_horizon_hcb (high contrast black)',
1159
+ 'sap_horizon_hcw (high contrast white)',
1160
+ 'sap_fiori_3 (Quartz light)',
1161
+ 'sap_fiori_3_dark'
1162
+ ]
1163
+ };
1164
+ result.troubleshooting = [
1165
+ {
1166
+ issue: 'Components look unstyled or broken',
1167
+ solution: 'Verify BOTH theme CSS files are loaded (base variables + fundamental theme)'
1168
+ },
1169
+ {
1170
+ issue: 'Fonts not loading',
1171
+ solution: 'Ensure @sap-theming/theming-base-content is installed and bundler can handle font files'
1172
+ },
1173
+ {
1174
+ issue: 'Icons not showing',
1175
+ solution: 'Icons come from @sap-theming/theming-base-content theme CSS. Verify theme is loaded correctly.'
1176
+ },
1177
+ {
1178
+ issue: 'Component sizes are wrong',
1179
+ solution: 'Set html { font-size: 16px; } in your global CSS'
1180
+ },
1181
+ {
1182
+ issue: 'CSS conflicts with existing styles',
1183
+ solution: 'fundamental-styles uses BEM naming (fd-*) to minimize conflicts. Check specificity issues.'
1184
+ }
1185
+ ];
1186
+ result.additionalPackages = [
1187
+ {
1188
+ name: '@fundamental-styles/common-css',
1189
+ description: 'Utility classes for margins, padding, flexbox, display (sap-* classes)',
1190
+ install: 'npm install @fundamental-styles/common-css',
1191
+ usage: "import '@fundamental-styles/common-css/dist/common-css.css';"
1192
+ },
1193
+ {
1194
+ name: '@fundamental-styles/cx',
1195
+ description: 'CX-specific components (customer experience)',
1196
+ install: 'npm install @fundamental-styles/cx'
1197
+ }
1198
+ ];
1199
+ return {
1200
+ content: [
1201
+ {
1202
+ type: 'text',
1203
+ text: JSON.stringify(result, null, 2)
1204
+ }
1205
+ ]
1206
+ };
1207
+ }));
678
1208
  /** Deduplicate changelog entries — collapse RC entries when the same change also appears in a stable release. */
679
1209
  function deduplicateEntries(entries) {
680
1210
  const stableEntries = entries.filter((e) => !e.version.includes('-'));