@atlaskit/ads-mcp 0.13.2 → 0.13.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # @atlaskit/ads-mcp
2
2
 
3
+ ## 0.13.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [`5a1b4e5c8ff80`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/5a1b4e5c8ff80) -
8
+ Remove eslint disable in i18n mcp guide
9
+
3
10
  ## 0.13.2
4
11
 
5
12
  ### Patch Changes
@@ -14,36 +14,36 @@ var i18nConversionGuide = exports.i18nConversionGuide = {
14
14
  description: 'Comprehensive guide for converting hardcoded strings to use formatMessage from @atlassian/jira-intl or react-intl-next',
15
15
  purpose: 'This guide instructs LLM agents to convert hardcoded strings to use formatMessage. The import source depends on the file path: use @atlassian/jira-intl for Jira files (path contains "jira"), and react-intl-next for any non-Jira files. The goal is to find all hardcoded strings in JSX and convert them to internationalized messages.',
16
16
  scope: "**CRITICAL SCOPE**: This process should **ONLY** focus on converting hardcoded strings (literal strings in JSX, eslint-disable comments for no-literal-string-in-jsx, etc.) to use formatMessage. Do NOT modify pre-existing messages that were already in the codebase, even if they have poor descriptions, incorrect placeholder names, or other quality issues. Only convert NEW hardcoded strings.\n\n**PATH SCOPE LIMITATION**: If a specific file, package name, or path is provided, **ONLY** find and convert hardcoded strings within that specified path. Do NOT modify files outside the provided scope.\n\n**CRITICAL: FIX ALL VIOLATIONS IN FILE**: When a specific file is mentioned (even with line numbers like `@file.tsx:139-142`), you MUST find and fix **ALL** @atlassian/i18n/no-literal-string-in-jsx violations in that entire file, not just the specific lines mentioned. Scan the entire file for hardcoded strings and convert them all. The line numbers are just a reference point - the goal is to fix all i18n violations in the provided file.\n\n**CRITICAL: STRING FILTERING**: When finding eslint-disable comments, you MUST examine the actual string content. Only convert strings that contain **user-facing English text**. Many English strings are technical/non-user-facing and should NOT be converted (e.g., product names like \"Jira\", URLs like \"https://example.com\", technical IDs, symbols). Use the ESLint ignore patterns to identify which English strings to skip - strings matching ignore patterns should be LEFT AS-IS with their eslint-disable comments intact.",
17
- implementationChecklist: ['Create message constants using defineMessage (singular) at the top of the file - use defineMessage for each individual message, NOT defineMessages', 'Import: Use "@atlassian/jira-intl" for Jira files (with eslint-disable) or "react-intl-next" for non-Jira files', '**CRITICAL: i18n ID Format**: For files in `/next/packages/`, i18n ids MUST start with the package name (with dashes). Format: `{package-name}.{component-or-feature}.{message-key}`. Example: For package `comment-extension-handlers`, use `comment-extension-handlers.legacy-content-modal.close-button`. For package `rovo-ai-search`, use `rovo-ai-search.view-profile-text` or `rovo-ai-search.knowledge-cards.copy-email-address`.', '**CRITICAL: ai-non-final Suffix**: ALL new message IDs MUST end with `.ai-non-final` suffix. This applies to ALL newly created messages, regardless of whether existing messages in the file have this suffix. Format: `{message-key}.ai-non-final`. Example: `applinks.administration.list.applinks-table.system-label.ai-non-final`. This suffix indicates the message is AI-generated and may need review before finalization.', 'Add useIntl hook: const { formatMessage } = useIntl();', 'Replace hardcoded strings with formatMessage(messageKey)', 'Add descriptions (40+ chars, unique from defaultMessage) and descriptive placeholder names', 'Remove eslint-disable for no-literal-string-in-jsx ONLY after converting', '**VERIFICATION**: To verify all hardcoded strings are fixed, search for `@atlassian/i18n/no-literal-string-in-jsx` in the file. Do NOT run eslint - just search for the eslint-disable comments. If no matches are found, all hardcoded strings have been converted.'],
17
+ implementationChecklist: ['Create message constants using defineMessage (singular) at the top of the file - use defineMessage for each individual message, NOT defineMessages', 'Import: Use "@atlassian/jira-intl" for Jira files or "react-intl-next" for non-Jira files', '**CRITICAL: i18n ID Format**: For files in `/next/packages/`, i18n ids MUST start with the package name (with dashes). Format: `{package-name}.{component-or-feature}.{message-key}`. Example: For package `comment-extension-handlers`, use `comment-extension-handlers.legacy-content-modal.close-button`. For package `rovo-ai-search`, use `rovo-ai-search.view-profile-text` or `rovo-ai-search.knowledge-cards.copy-email-address`.', '**CRITICAL: ai-non-final Suffix**: ALL new message IDs MUST end with `.ai-non-final` suffix. This applies to ALL newly created messages, regardless of whether existing messages in the file have this suffix. Format: `{message-key}.ai-non-final`. Example: `applinks.administration.list.applinks-table.system-label.ai-non-final`. This suffix indicates the message is AI-generated and may need review before finalization.', 'Add useIntl hook: const { formatMessage } = useIntl();', 'Replace hardcoded strings with formatMessage(messageKey)', 'Add descriptions (40+ chars, unique from defaultMessage) and descriptive placeholder names', 'Remove eslint-disable for no-literal-string-in-jsx ONLY after converting', '**VERIFICATION**: To verify all hardcoded strings are fixed, search for `@atlassian/i18n/no-literal-string-in-jsx` in the file. Do NOT run eslint - just search for the eslint-disable comments. If no matches are found, all hardcoded strings have been converted.'],
18
18
  patterns: [{
19
19
  title: 'Button Text and Action Labels',
20
20
  description: 'Converting hardcoded button text with variables',
21
21
  before: "<Button style={styles} onClick={onClearVersion}>\n Exit {description.join(' ')}\n</Button>",
22
- after: "// eslint-disable-next-line jira/deprecations/ban-identifiers\nimport { useIntl, defineMessage } from '@atlassian/jira-intl';\n\n// eslint-disable-next-line jira/deprecations/ban-identifiers\nconst exitButton = defineMessage({\n id: 'rovo-ai-search.exit-button.ai-non-final',\n defaultMessage: 'Exit {version}',\n description: 'The text is shown as a button when the user needs to exit the version view. The placeholder {version} will be substituted with the version name. Used in the version header to return to the main view.',\n});\n\nexport function MyComponent() {\n const { formatMessage } = useIntl();\n\n return (\n <Button style={styles} onClick={onClearVersion}>\n {formatMessage(exitButton, { version: description.join(' ') })}\n </Button>\n );\n}",
22
+ after: "import { useIntl, defineMessage } from '@atlassian/jira-intl';\n\nconst exitButton = defineMessage({\n id: 'rovo-ai-search.exit-button.ai-non-final',\n defaultMessage: 'Exit {version}',\n description: 'The text is shown as a button when the user needs to exit the version view. The placeholder {version} will be substituted with the version name. Used in the version header to return to the main view.',\n});\n\nexport function MyComponent() {\n const { formatMessage } = useIntl();\n\n return (\n <Button style={styles} onClick={onClearVersion}>\n {formatMessage(exitButton, { version: description.join(' ') })}\n </Button>\n );\n}",
23
23
  explanation: 'Convert button text with variables to use placeholders in the message. Use descriptive placeholder names that match the variable context.'
24
24
  }, {
25
25
  title: 'Loading States and Status Messages',
26
26
  description: 'Converting hardcoded loading and status messages',
27
27
  before: "return <Box>Loading...</Box>;",
28
- after: "// eslint-disable-next-line jira/deprecations/ban-identifiers\nimport { useIntl, defineMessage } from '@atlassian/jira-intl';\n\n// eslint-disable-next-line jira/deprecations/ban-identifiers\nconst loading = defineMessage({\n id: 'rovo-ai-search.loading.ai-non-final',\n defaultMessage: 'Loading...',\n description: 'The text is shown as a loading indicator when work item data is being fetched from the server. Appears in the work item tab to inform users that content is loading.',\n});\n\nexport function MyComponent() {\n const { formatMessage } = useIntl();\n\n return (\n <Box>\n {formatMessage(loading)}\n </Box>\n );\n}",
28
+ after: "import { useIntl, defineMessage } from '@atlassian/jira-intl';\n\nconst loading = defineMessage({\n id: 'rovo-ai-search.loading.ai-non-final',\n defaultMessage: 'Loading...',\n description: 'The text is shown as a loading indicator when work item data is being fetched from the server. Appears in the work item tab to inform users that content is loading.',\n});\n\nexport function MyComponent() {\n const { formatMessage } = useIntl();\n\n return (\n <Box>\n {formatMessage(loading)}\n </Box>\n );\n}",
29
29
  explanation: 'Convert simple hardcoded status messages to use defineMessage (singular). Use defineMessage for each individual message, NOT defineMessages. Ensure descriptions explain when and where the message appears.'
30
30
  }, {
31
31
  title: 'Partial String Translation (Prefixes and Labels)',
32
32
  description: 'Translating only part of a string while preserving UI structure',
33
33
  before: "<Box xcss={styles}>\n <span>Status: {selectedOptions[0]?.label}</span>\n {selectedOptions.length > 1 && (\n <Badge appearance=\"primary\" max={99}>\n +{selectedOptions.length - 1}\n </Badge>\n )}\n</Box>",
34
- after: "// eslint-disable-next-line jira/deprecations/ban-identifiers\nimport { useIntl, defineMessage } from '@atlassian/jira-intl';\n\n// eslint-disable-next-line jira/deprecations/ban-identifiers\nconst statusLabel = defineMessage({\n id: 'filter.status-label.ai-non-final',\n defaultMessage: 'Status: {status}',\n description: 'The text is shown as a button label prefix for the status filter. The placeholder {status} will be substituted with the selected status label. Appears in the filter button to show the current filter state.',\n});\n\nexport function MyComponent() {\n const { formatMessage } = useIntl();\n\n return (\n <Box xcss={styles}>\n <span>\n {formatMessage(statusLabel, {\n status: selectedOptions[0]?.label,\n })}\n </span>\n {selectedOptions.length > 1 && (\n <Badge appearance=\"primary\" max={99}>\n +{selectedOptions.length - 1}\n </Badge>\n )}\n </Box>\n );\n}",
34
+ after: "import { useIntl, defineMessage } from '@atlassian/jira-intl';\n\nconst statusLabel = defineMessage({\n id: 'filter.status-label.ai-non-final',\n defaultMessage: 'Status: {status}',\n description: 'The text is shown as a button label prefix for the status filter. The placeholder {status} will be substituted with the selected status label. Appears in the filter button to show the current filter state.',\n});\n\nexport function MyComponent() {\n const { formatMessage } = useIntl();\n\n return (\n <Box xcss={styles}>\n <span>\n {formatMessage(statusLabel, {\n status: selectedOptions[0]?.label,\n })}\n </span>\n {selectedOptions.length > 1 && (\n <Badge appearance=\"primary\" max={99}>\n +{selectedOptions.length - 1}\n </Badge>\n )}\n </Box>\n );\n}",
35
35
  explanation: 'When only part of a string needs translation (like a prefix "Status: " or label), create a message with a placeholder for the dynamic part. Preserve the existing UI structure (components, conditional rendering, etc.) and only translate the literal string portion.'
36
36
  }, {
37
37
  title: 'Dynamic Text Content',
38
38
  description: 'Converting conditional hardcoded strings',
39
39
  before: "<Text>{state.hasMore ? 'Show more' : 'Show less'}</Text>",
40
- after: "// eslint-disable-next-line jira/deprecations/ban-identifiers\nimport { useIntl, defineMessage } from '@atlassian/jira-intl';\n\n// eslint-disable-next-line jira/deprecations/ban-identifiers\nconst showMore = defineMessage({\n id: 'work-item.comments.show-more.ai-non-final',\n defaultMessage: 'Show more',\n description: 'The text is shown as a button when there are more items available to display. Used in the comments section footer to expand and show additional content.',\n});\n\n// eslint-disable-next-line jira/deprecations/ban-identifiers\nconst showLess = defineMessage({\n id: 'work-item.comments.show-less.ai-non-final',\n defaultMessage: 'Show less',\n description: 'The text is shown as a button when the user wants to collapse the expanded view. Used in the comments section footer to hide additional content.',\n});\n\nexport function MyComponent() {\n const { formatMessage } = useIntl();\n\n return (\n <Text>\n {state.hasMore\n ? formatMessage(showMore)\n : formatMessage(showLess)}\n </Text>\n );\n}",
40
+ after: "import { useIntl, defineMessage } from '@atlassian/jira-intl';\n\nconst showMore = defineMessage({\n id: 'work-item.comments.show-more.ai-non-final',\n defaultMessage: 'Show more',\n description: 'The text is shown as a button when there are more items available to display. Used in the comments section footer to expand and show additional content.',\n});\n\nconst showLess = defineMessage({\n id: 'work-item.comments.show-less.ai-non-final',\n defaultMessage: 'Show less',\n description: 'The text is shown as a button when the user wants to collapse the expanded view. Used in the comments section footer to hide additional content.',\n});\n\nexport function MyComponent() {\n const { formatMessage } = useIntl();\n\n return (\n <Text>\n {state.hasMore\n ? formatMessage(showMore)\n : formatMessage(showLess)}\n </Text>\n );\n}",
41
41
  explanation: 'Create separate message constants for each conditional string. Use descriptive names that reflect the context.'
42
42
  }, {
43
43
  title: 'ICU Format for Numeric Values',
44
44
  description: 'Converting numeric placeholders to ICU plural format',
45
45
  before: "const moreMessage = `+ ${count} more`;",
46
- after: "// eslint-disable-next-line jira/deprecations/ban-identifiers\nimport { useIntl, defineMessage } from '@atlassian/jira-intl';\n\n// eslint-disable-next-line jira/deprecations/ban-identifiers\nconst moreProjectsMessage = defineMessage({\n id: 'app.more-projects.ai-non-final',\n defaultMessage: '{count, plural, one {+ # more} other {+ # more}}',\n description: 'The text is shown when there are additional projects available. The placeholder {count} will be substituted with the number of remaining projects. Uses ICU plural format for proper localization.',\n});\n\nexport function MyComponent() {\n const { formatMessage } = useIntl();\n const count = 5;\n\n return (\n <Text>\n {formatMessage(moreProjectsMessage, { count })}\n </Text>\n );\n}",
46
+ after: "import { useIntl, defineMessage } from '@atlassian/jira-intl';\n\nconst moreProjectsMessage = defineMessage({\n id: 'app.more-projects.ai-non-final',\n defaultMessage: '{count, plural, one {+ # more} other {+ # more}}',\n description: 'The text is shown when there are additional projects available. The placeholder {count} will be substituted with the number of remaining projects. Uses ICU plural format for proper localization.',\n});\n\nexport function MyComponent() {\n const { formatMessage } = useIntl();\n const count = 5;\n\n return (\n <Text>\n {formatMessage(moreProjectsMessage, { count })}\n </Text>\n );\n}",
47
47
  explanation: 'Use ICU plural format for numeric values that may need pluralization. The # symbol represents the numeric value in ICU format.'
48
48
  }],
49
49
  bestPractices: ['Only convert NEW hardcoded strings - do NOT modify pre-existing messages', '**CRITICAL: Use defineMessage (singular)** - Always use defineMessage for each individual message, NOT defineMessages (plural). Each message should be defined separately: `const messageKey = defineMessage({ ... })`', '**CRITICAL: Always use .ai-non-final suffix** - ALL new message IDs MUST end with `.ai-non-final`. This applies even if existing messages in the file don\'t have this suffix. Format: `{message-key}.ai-non-final`. This indicates the message is AI-generated and may need review.', 'Use descriptive names (userNameLabel not label) and placeholders (userName not value)', 'Add descriptions (40+ chars, unique from defaultMessage)', 'Use ICU plural format for numeric values: {count, plural, one {...} other {...}}', 'Place message constants at top of file, use formatMessage consistently', 'Convert arrow functions to body form if needed for hooks'],
@@ -14,17 +14,15 @@ export const i18nConversionGuide = {
14
14
  **CRITICAL: FIX ALL VIOLATIONS IN FILE**: When a specific file is mentioned (even with line numbers like \`@file.tsx:139-142\`), you MUST find and fix **ALL** @atlassian/i18n/no-literal-string-in-jsx violations in that entire file, not just the specific lines mentioned. Scan the entire file for hardcoded strings and convert them all. The line numbers are just a reference point - the goal is to fix all i18n violations in the provided file.
15
15
 
16
16
  **CRITICAL: STRING FILTERING**: When finding eslint-disable comments, you MUST examine the actual string content. Only convert strings that contain **user-facing English text**. Many English strings are technical/non-user-facing and should NOT be converted (e.g., product names like "Jira", URLs like "https://example.com", technical IDs, symbols). Use the ESLint ignore patterns to identify which English strings to skip - strings matching ignore patterns should be LEFT AS-IS with their eslint-disable comments intact.`,
17
- implementationChecklist: ['Create message constants using defineMessage (singular) at the top of the file - use defineMessage for each individual message, NOT defineMessages', 'Import: Use "@atlassian/jira-intl" for Jira files (with eslint-disable) or "react-intl-next" for non-Jira files', '**CRITICAL: i18n ID Format**: For files in `/next/packages/`, i18n ids MUST start with the package name (with dashes). Format: `{package-name}.{component-or-feature}.{message-key}`. Example: For package `comment-extension-handlers`, use `comment-extension-handlers.legacy-content-modal.close-button`. For package `rovo-ai-search`, use `rovo-ai-search.view-profile-text` or `rovo-ai-search.knowledge-cards.copy-email-address`.', '**CRITICAL: ai-non-final Suffix**: ALL new message IDs MUST end with `.ai-non-final` suffix. This applies to ALL newly created messages, regardless of whether existing messages in the file have this suffix. Format: `{message-key}.ai-non-final`. Example: `applinks.administration.list.applinks-table.system-label.ai-non-final`. This suffix indicates the message is AI-generated and may need review before finalization.', 'Add useIntl hook: const { formatMessage } = useIntl();', 'Replace hardcoded strings with formatMessage(messageKey)', 'Add descriptions (40+ chars, unique from defaultMessage) and descriptive placeholder names', 'Remove eslint-disable for no-literal-string-in-jsx ONLY after converting', '**VERIFICATION**: To verify all hardcoded strings are fixed, search for `@atlassian/i18n/no-literal-string-in-jsx` in the file. Do NOT run eslint - just search for the eslint-disable comments. If no matches are found, all hardcoded strings have been converted.'],
17
+ implementationChecklist: ['Create message constants using defineMessage (singular) at the top of the file - use defineMessage for each individual message, NOT defineMessages', 'Import: Use "@atlassian/jira-intl" for Jira files or "react-intl-next" for non-Jira files', '**CRITICAL: i18n ID Format**: For files in `/next/packages/`, i18n ids MUST start with the package name (with dashes). Format: `{package-name}.{component-or-feature}.{message-key}`. Example: For package `comment-extension-handlers`, use `comment-extension-handlers.legacy-content-modal.close-button`. For package `rovo-ai-search`, use `rovo-ai-search.view-profile-text` or `rovo-ai-search.knowledge-cards.copy-email-address`.', '**CRITICAL: ai-non-final Suffix**: ALL new message IDs MUST end with `.ai-non-final` suffix. This applies to ALL newly created messages, regardless of whether existing messages in the file have this suffix. Format: `{message-key}.ai-non-final`. Example: `applinks.administration.list.applinks-table.system-label.ai-non-final`. This suffix indicates the message is AI-generated and may need review before finalization.', 'Add useIntl hook: const { formatMessage } = useIntl();', 'Replace hardcoded strings with formatMessage(messageKey)', 'Add descriptions (40+ chars, unique from defaultMessage) and descriptive placeholder names', 'Remove eslint-disable for no-literal-string-in-jsx ONLY after converting', '**VERIFICATION**: To verify all hardcoded strings are fixed, search for `@atlassian/i18n/no-literal-string-in-jsx` in the file. Do NOT run eslint - just search for the eslint-disable comments. If no matches are found, all hardcoded strings have been converted.'],
18
18
  patterns: [{
19
19
  title: 'Button Text and Action Labels',
20
20
  description: 'Converting hardcoded button text with variables',
21
21
  before: `<Button style={styles} onClick={onClearVersion}>
22
22
  Exit {description.join(' ')}
23
23
  </Button>`,
24
- after: `// eslint-disable-next-line jira/deprecations/ban-identifiers
25
- import { useIntl, defineMessage } from '@atlassian/jira-intl';
24
+ after: `import { useIntl, defineMessage } from '@atlassian/jira-intl';
26
25
 
27
- // eslint-disable-next-line jira/deprecations/ban-identifiers
28
26
  const exitButton = defineMessage({
29
27
  id: 'rovo-ai-search.exit-button.ai-non-final',
30
28
  defaultMessage: 'Exit {version}',
@@ -45,10 +43,8 @@ export function MyComponent() {
45
43
  title: 'Loading States and Status Messages',
46
44
  description: 'Converting hardcoded loading and status messages',
47
45
  before: `return <Box>Loading...</Box>;`,
48
- after: `// eslint-disable-next-line jira/deprecations/ban-identifiers
49
- import { useIntl, defineMessage } from '@atlassian/jira-intl';
46
+ after: `import { useIntl, defineMessage } from '@atlassian/jira-intl';
50
47
 
51
- // eslint-disable-next-line jira/deprecations/ban-identifiers
52
48
  const loading = defineMessage({
53
49
  id: 'rovo-ai-search.loading.ai-non-final',
54
50
  defaultMessage: 'Loading...',
@@ -76,10 +72,8 @@ export function MyComponent() {
76
72
  </Badge>
77
73
  )}
78
74
  </Box>`,
79
- after: `// eslint-disable-next-line jira/deprecations/ban-identifiers
80
- import { useIntl, defineMessage } from '@atlassian/jira-intl';
75
+ after: `import { useIntl, defineMessage } from '@atlassian/jira-intl';
81
76
 
82
- // eslint-disable-next-line jira/deprecations/ban-identifiers
83
77
  const statusLabel = defineMessage({
84
78
  id: 'filter.status-label.ai-non-final',
85
79
  defaultMessage: 'Status: {status}',
@@ -109,17 +103,14 @@ export function MyComponent() {
109
103
  title: 'Dynamic Text Content',
110
104
  description: 'Converting conditional hardcoded strings',
111
105
  before: `<Text>{state.hasMore ? 'Show more' : 'Show less'}</Text>`,
112
- after: `// eslint-disable-next-line jira/deprecations/ban-identifiers
113
- import { useIntl, defineMessage } from '@atlassian/jira-intl';
106
+ after: `import { useIntl, defineMessage } from '@atlassian/jira-intl';
114
107
 
115
- // eslint-disable-next-line jira/deprecations/ban-identifiers
116
108
  const showMore = defineMessage({
117
109
  id: 'work-item.comments.show-more.ai-non-final',
118
110
  defaultMessage: 'Show more',
119
111
  description: 'The text is shown as a button when there are more items available to display. Used in the comments section footer to expand and show additional content.',
120
112
  });
121
113
 
122
- // eslint-disable-next-line jira/deprecations/ban-identifiers
123
114
  const showLess = defineMessage({
124
115
  id: 'work-item.comments.show-less.ai-non-final',
125
116
  defaultMessage: 'Show less',
@@ -142,10 +133,8 @@ export function MyComponent() {
142
133
  title: 'ICU Format for Numeric Values',
143
134
  description: 'Converting numeric placeholders to ICU plural format',
144
135
  before: `const moreMessage = \`+ \${count} more\`;`,
145
- after: `// eslint-disable-next-line jira/deprecations/ban-identifiers
146
- import { useIntl, defineMessage } from '@atlassian/jira-intl';
136
+ after: `import { useIntl, defineMessage } from '@atlassian/jira-intl';
147
137
 
148
- // eslint-disable-next-line jira/deprecations/ban-identifiers
149
138
  const moreProjectsMessage = defineMessage({
150
139
  id: 'app.more-projects.ai-non-final',
151
140
  defaultMessage: '{count, plural, one {+ # more} other {+ # more}}',
@@ -8,36 +8,36 @@ export var i18nConversionGuide = {
8
8
  description: 'Comprehensive guide for converting hardcoded strings to use formatMessage from @atlassian/jira-intl or react-intl-next',
9
9
  purpose: 'This guide instructs LLM agents to convert hardcoded strings to use formatMessage. The import source depends on the file path: use @atlassian/jira-intl for Jira files (path contains "jira"), and react-intl-next for any non-Jira files. The goal is to find all hardcoded strings in JSX and convert them to internationalized messages.',
10
10
  scope: "**CRITICAL SCOPE**: This process should **ONLY** focus on converting hardcoded strings (literal strings in JSX, eslint-disable comments for no-literal-string-in-jsx, etc.) to use formatMessage. Do NOT modify pre-existing messages that were already in the codebase, even if they have poor descriptions, incorrect placeholder names, or other quality issues. Only convert NEW hardcoded strings.\n\n**PATH SCOPE LIMITATION**: If a specific file, package name, or path is provided, **ONLY** find and convert hardcoded strings within that specified path. Do NOT modify files outside the provided scope.\n\n**CRITICAL: FIX ALL VIOLATIONS IN FILE**: When a specific file is mentioned (even with line numbers like `@file.tsx:139-142`), you MUST find and fix **ALL** @atlassian/i18n/no-literal-string-in-jsx violations in that entire file, not just the specific lines mentioned. Scan the entire file for hardcoded strings and convert them all. The line numbers are just a reference point - the goal is to fix all i18n violations in the provided file.\n\n**CRITICAL: STRING FILTERING**: When finding eslint-disable comments, you MUST examine the actual string content. Only convert strings that contain **user-facing English text**. Many English strings are technical/non-user-facing and should NOT be converted (e.g., product names like \"Jira\", URLs like \"https://example.com\", technical IDs, symbols). Use the ESLint ignore patterns to identify which English strings to skip - strings matching ignore patterns should be LEFT AS-IS with their eslint-disable comments intact.",
11
- implementationChecklist: ['Create message constants using defineMessage (singular) at the top of the file - use defineMessage for each individual message, NOT defineMessages', 'Import: Use "@atlassian/jira-intl" for Jira files (with eslint-disable) or "react-intl-next" for non-Jira files', '**CRITICAL: i18n ID Format**: For files in `/next/packages/`, i18n ids MUST start with the package name (with dashes). Format: `{package-name}.{component-or-feature}.{message-key}`. Example: For package `comment-extension-handlers`, use `comment-extension-handlers.legacy-content-modal.close-button`. For package `rovo-ai-search`, use `rovo-ai-search.view-profile-text` or `rovo-ai-search.knowledge-cards.copy-email-address`.', '**CRITICAL: ai-non-final Suffix**: ALL new message IDs MUST end with `.ai-non-final` suffix. This applies to ALL newly created messages, regardless of whether existing messages in the file have this suffix. Format: `{message-key}.ai-non-final`. Example: `applinks.administration.list.applinks-table.system-label.ai-non-final`. This suffix indicates the message is AI-generated and may need review before finalization.', 'Add useIntl hook: const { formatMessage } = useIntl();', 'Replace hardcoded strings with formatMessage(messageKey)', 'Add descriptions (40+ chars, unique from defaultMessage) and descriptive placeholder names', 'Remove eslint-disable for no-literal-string-in-jsx ONLY after converting', '**VERIFICATION**: To verify all hardcoded strings are fixed, search for `@atlassian/i18n/no-literal-string-in-jsx` in the file. Do NOT run eslint - just search for the eslint-disable comments. If no matches are found, all hardcoded strings have been converted.'],
11
+ implementationChecklist: ['Create message constants using defineMessage (singular) at the top of the file - use defineMessage for each individual message, NOT defineMessages', 'Import: Use "@atlassian/jira-intl" for Jira files or "react-intl-next" for non-Jira files', '**CRITICAL: i18n ID Format**: For files in `/next/packages/`, i18n ids MUST start with the package name (with dashes). Format: `{package-name}.{component-or-feature}.{message-key}`. Example: For package `comment-extension-handlers`, use `comment-extension-handlers.legacy-content-modal.close-button`. For package `rovo-ai-search`, use `rovo-ai-search.view-profile-text` or `rovo-ai-search.knowledge-cards.copy-email-address`.', '**CRITICAL: ai-non-final Suffix**: ALL new message IDs MUST end with `.ai-non-final` suffix. This applies to ALL newly created messages, regardless of whether existing messages in the file have this suffix. Format: `{message-key}.ai-non-final`. Example: `applinks.administration.list.applinks-table.system-label.ai-non-final`. This suffix indicates the message is AI-generated and may need review before finalization.', 'Add useIntl hook: const { formatMessage } = useIntl();', 'Replace hardcoded strings with formatMessage(messageKey)', 'Add descriptions (40+ chars, unique from defaultMessage) and descriptive placeholder names', 'Remove eslint-disable for no-literal-string-in-jsx ONLY after converting', '**VERIFICATION**: To verify all hardcoded strings are fixed, search for `@atlassian/i18n/no-literal-string-in-jsx` in the file. Do NOT run eslint - just search for the eslint-disable comments. If no matches are found, all hardcoded strings have been converted.'],
12
12
  patterns: [{
13
13
  title: 'Button Text and Action Labels',
14
14
  description: 'Converting hardcoded button text with variables',
15
15
  before: "<Button style={styles} onClick={onClearVersion}>\n Exit {description.join(' ')}\n</Button>",
16
- after: "// eslint-disable-next-line jira/deprecations/ban-identifiers\nimport { useIntl, defineMessage } from '@atlassian/jira-intl';\n\n// eslint-disable-next-line jira/deprecations/ban-identifiers\nconst exitButton = defineMessage({\n id: 'rovo-ai-search.exit-button.ai-non-final',\n defaultMessage: 'Exit {version}',\n description: 'The text is shown as a button when the user needs to exit the version view. The placeholder {version} will be substituted with the version name. Used in the version header to return to the main view.',\n});\n\nexport function MyComponent() {\n const { formatMessage } = useIntl();\n\n return (\n <Button style={styles} onClick={onClearVersion}>\n {formatMessage(exitButton, { version: description.join(' ') })}\n </Button>\n );\n}",
16
+ after: "import { useIntl, defineMessage } from '@atlassian/jira-intl';\n\nconst exitButton = defineMessage({\n id: 'rovo-ai-search.exit-button.ai-non-final',\n defaultMessage: 'Exit {version}',\n description: 'The text is shown as a button when the user needs to exit the version view. The placeholder {version} will be substituted with the version name. Used in the version header to return to the main view.',\n});\n\nexport function MyComponent() {\n const { formatMessage } = useIntl();\n\n return (\n <Button style={styles} onClick={onClearVersion}>\n {formatMessage(exitButton, { version: description.join(' ') })}\n </Button>\n );\n}",
17
17
  explanation: 'Convert button text with variables to use placeholders in the message. Use descriptive placeholder names that match the variable context.'
18
18
  }, {
19
19
  title: 'Loading States and Status Messages',
20
20
  description: 'Converting hardcoded loading and status messages',
21
21
  before: "return <Box>Loading...</Box>;",
22
- after: "// eslint-disable-next-line jira/deprecations/ban-identifiers\nimport { useIntl, defineMessage } from '@atlassian/jira-intl';\n\n// eslint-disable-next-line jira/deprecations/ban-identifiers\nconst loading = defineMessage({\n id: 'rovo-ai-search.loading.ai-non-final',\n defaultMessage: 'Loading...',\n description: 'The text is shown as a loading indicator when work item data is being fetched from the server. Appears in the work item tab to inform users that content is loading.',\n});\n\nexport function MyComponent() {\n const { formatMessage } = useIntl();\n\n return (\n <Box>\n {formatMessage(loading)}\n </Box>\n );\n}",
22
+ after: "import { useIntl, defineMessage } from '@atlassian/jira-intl';\n\nconst loading = defineMessage({\n id: 'rovo-ai-search.loading.ai-non-final',\n defaultMessage: 'Loading...',\n description: 'The text is shown as a loading indicator when work item data is being fetched from the server. Appears in the work item tab to inform users that content is loading.',\n});\n\nexport function MyComponent() {\n const { formatMessage } = useIntl();\n\n return (\n <Box>\n {formatMessage(loading)}\n </Box>\n );\n}",
23
23
  explanation: 'Convert simple hardcoded status messages to use defineMessage (singular). Use defineMessage for each individual message, NOT defineMessages. Ensure descriptions explain when and where the message appears.'
24
24
  }, {
25
25
  title: 'Partial String Translation (Prefixes and Labels)',
26
26
  description: 'Translating only part of a string while preserving UI structure',
27
27
  before: "<Box xcss={styles}>\n <span>Status: {selectedOptions[0]?.label}</span>\n {selectedOptions.length > 1 && (\n <Badge appearance=\"primary\" max={99}>\n +{selectedOptions.length - 1}\n </Badge>\n )}\n</Box>",
28
- after: "// eslint-disable-next-line jira/deprecations/ban-identifiers\nimport { useIntl, defineMessage } from '@atlassian/jira-intl';\n\n// eslint-disable-next-line jira/deprecations/ban-identifiers\nconst statusLabel = defineMessage({\n id: 'filter.status-label.ai-non-final',\n defaultMessage: 'Status: {status}',\n description: 'The text is shown as a button label prefix for the status filter. The placeholder {status} will be substituted with the selected status label. Appears in the filter button to show the current filter state.',\n});\n\nexport function MyComponent() {\n const { formatMessage } = useIntl();\n\n return (\n <Box xcss={styles}>\n <span>\n {formatMessage(statusLabel, {\n status: selectedOptions[0]?.label,\n })}\n </span>\n {selectedOptions.length > 1 && (\n <Badge appearance=\"primary\" max={99}>\n +{selectedOptions.length - 1}\n </Badge>\n )}\n </Box>\n );\n}",
28
+ after: "import { useIntl, defineMessage } from '@atlassian/jira-intl';\n\nconst statusLabel = defineMessage({\n id: 'filter.status-label.ai-non-final',\n defaultMessage: 'Status: {status}',\n description: 'The text is shown as a button label prefix for the status filter. The placeholder {status} will be substituted with the selected status label. Appears in the filter button to show the current filter state.',\n});\n\nexport function MyComponent() {\n const { formatMessage } = useIntl();\n\n return (\n <Box xcss={styles}>\n <span>\n {formatMessage(statusLabel, {\n status: selectedOptions[0]?.label,\n })}\n </span>\n {selectedOptions.length > 1 && (\n <Badge appearance=\"primary\" max={99}>\n +{selectedOptions.length - 1}\n </Badge>\n )}\n </Box>\n );\n}",
29
29
  explanation: 'When only part of a string needs translation (like a prefix "Status: " or label), create a message with a placeholder for the dynamic part. Preserve the existing UI structure (components, conditional rendering, etc.) and only translate the literal string portion.'
30
30
  }, {
31
31
  title: 'Dynamic Text Content',
32
32
  description: 'Converting conditional hardcoded strings',
33
33
  before: "<Text>{state.hasMore ? 'Show more' : 'Show less'}</Text>",
34
- after: "// eslint-disable-next-line jira/deprecations/ban-identifiers\nimport { useIntl, defineMessage } from '@atlassian/jira-intl';\n\n// eslint-disable-next-line jira/deprecations/ban-identifiers\nconst showMore = defineMessage({\n id: 'work-item.comments.show-more.ai-non-final',\n defaultMessage: 'Show more',\n description: 'The text is shown as a button when there are more items available to display. Used in the comments section footer to expand and show additional content.',\n});\n\n// eslint-disable-next-line jira/deprecations/ban-identifiers\nconst showLess = defineMessage({\n id: 'work-item.comments.show-less.ai-non-final',\n defaultMessage: 'Show less',\n description: 'The text is shown as a button when the user wants to collapse the expanded view. Used in the comments section footer to hide additional content.',\n});\n\nexport function MyComponent() {\n const { formatMessage } = useIntl();\n\n return (\n <Text>\n {state.hasMore\n ? formatMessage(showMore)\n : formatMessage(showLess)}\n </Text>\n );\n}",
34
+ after: "import { useIntl, defineMessage } from '@atlassian/jira-intl';\n\nconst showMore = defineMessage({\n id: 'work-item.comments.show-more.ai-non-final',\n defaultMessage: 'Show more',\n description: 'The text is shown as a button when there are more items available to display. Used in the comments section footer to expand and show additional content.',\n});\n\nconst showLess = defineMessage({\n id: 'work-item.comments.show-less.ai-non-final',\n defaultMessage: 'Show less',\n description: 'The text is shown as a button when the user wants to collapse the expanded view. Used in the comments section footer to hide additional content.',\n});\n\nexport function MyComponent() {\n const { formatMessage } = useIntl();\n\n return (\n <Text>\n {state.hasMore\n ? formatMessage(showMore)\n : formatMessage(showLess)}\n </Text>\n );\n}",
35
35
  explanation: 'Create separate message constants for each conditional string. Use descriptive names that reflect the context.'
36
36
  }, {
37
37
  title: 'ICU Format for Numeric Values',
38
38
  description: 'Converting numeric placeholders to ICU plural format',
39
39
  before: "const moreMessage = `+ ${count} more`;",
40
- after: "// eslint-disable-next-line jira/deprecations/ban-identifiers\nimport { useIntl, defineMessage } from '@atlassian/jira-intl';\n\n// eslint-disable-next-line jira/deprecations/ban-identifiers\nconst moreProjectsMessage = defineMessage({\n id: 'app.more-projects.ai-non-final',\n defaultMessage: '{count, plural, one {+ # more} other {+ # more}}',\n description: 'The text is shown when there are additional projects available. The placeholder {count} will be substituted with the number of remaining projects. Uses ICU plural format for proper localization.',\n});\n\nexport function MyComponent() {\n const { formatMessage } = useIntl();\n const count = 5;\n\n return (\n <Text>\n {formatMessage(moreProjectsMessage, { count })}\n </Text>\n );\n}",
40
+ after: "import { useIntl, defineMessage } from '@atlassian/jira-intl';\n\nconst moreProjectsMessage = defineMessage({\n id: 'app.more-projects.ai-non-final',\n defaultMessage: '{count, plural, one {+ # more} other {+ # more}}',\n description: 'The text is shown when there are additional projects available. The placeholder {count} will be substituted with the number of remaining projects. Uses ICU plural format for proper localization.',\n});\n\nexport function MyComponent() {\n const { formatMessage } = useIntl();\n const count = 5;\n\n return (\n <Text>\n {formatMessage(moreProjectsMessage, { count })}\n </Text>\n );\n}",
41
41
  explanation: 'Use ICU plural format for numeric values that may need pluralization. The # symbol represents the numeric value in ICU format.'
42
42
  }],
43
43
  bestPractices: ['Only convert NEW hardcoded strings - do NOT modify pre-existing messages', '**CRITICAL: Use defineMessage (singular)** - Always use defineMessage for each individual message, NOT defineMessages (plural). Each message should be defined separately: `const messageKey = defineMessage({ ... })`', '**CRITICAL: Always use .ai-non-final suffix** - ALL new message IDs MUST end with `.ai-non-final`. This applies even if existing messages in the file don\'t have this suffix. Format: `{message-key}.ai-non-final`. This indicates the message is AI-generated and may need review.', 'Use descriptive names (userNameLabel not label) and placeholders (userName not value)', 'Add descriptions (40+ chars, unique from defaultMessage)', 'Use ICU plural format for numeric values: {count, plural, one {...} other {...}}', 'Place message constants at top of file, use formatMessage consistently', 'Convert arrow functions to body form if needed for hooks'],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/ads-mcp",
3
- "version": "0.13.2",
3
+ "version": "0.13.3",
4
4
  "description": "The official Atlassian Design System MCP server to develop apps and user interfaces matching the Atlassian style.",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -34,7 +34,7 @@
34
34
  }
35
35
  },
36
36
  "dependencies": {
37
- "@atlaskit/icon": "^29.3.0",
37
+ "@atlaskit/icon": "^29.4.0",
38
38
  "@atlaskit/platform-feature-flags": "^1.1.0",
39
39
  "@atlaskit/tokens": "^9.1.0",
40
40
  "@axe-core/playwright": "^4.8.0",