@wise/wds-codemods 1.3.1-experimental-3d40fd4 → 1.3.1-experimental-3cb03dd

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.
@@ -163,13 +163,16 @@ async function queryClaude(directory, filePath, options, task, isDebug = false)
163
163
  prompt: filePath
164
164
  });
165
165
  for await (const message of result) switch (message.type) {
166
- case "result": if (message.subtype === "success" && isDebug) {
167
- task.title = `\x1b[2m${formatPathOutput(directory, filePath)}\x1b[0m]`;
168
- task.output = `\x1b[2mMigrated in ${generateElapsedTime(startTime)}\x1b[0m`;
169
- } else if (message.is_error) {
170
- task.title = `\x1b[2m${formatPathOutput(directory, filePath)}\x1b[0m]`;
171
- task.output = `${CONSOLE_ICONS.error} Claude encountered an error: ${JSON.stringify(message)}`;
172
- }
166
+ case "result":
167
+ if (message.subtype === "success" && isDebug) {
168
+ task.title = `\x1b[2m${formatPathOutput(directory, filePath)}\x1b[0m]`;
169
+ task.output = `\x1b[2mMigrated in ${generateElapsedTime(startTime)}\x1b[0m`;
170
+ } else if (message.is_error) {
171
+ task.title = `\x1b[2m${formatPathOutput(directory, filePath)}\x1b[0m]`;
172
+ task.output = `${CONSOLE_ICONS.error} Claude encountered an error: ${JSON.stringify(message)}`;
173
+ }
174
+ break;
175
+ default:
173
176
  }
174
177
  }
175
178
  //#endregion
@@ -233,4 +236,4 @@ const claudeTransformer = async ({ targetDirectories, componentGrepPattern, addi
233
236
  //#endregion
234
237
  export { claudeTransformer as t };
235
238
 
236
- //# sourceMappingURL=claude-BYOsggA3.js.map
239
+ //# sourceMappingURL=claude-D6IwFmoz.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"claude-BYOsggA3.js","names":[],"sources":["../src/constants/claude.ts","../src/helpers/common/pathUtils.ts","../src/helpers/common/timerUtils.ts","../src/helpers/common/vpnUtils.ts","../src/helpers/claude/query.ts","../src/helpers/claude/transformer.ts"],"sourcesContent":["export const CLAUDE_SETTINGS_FILE = '.claude/settings.json';\nexport const VPN_COUNTDOWN_TIMEOUT = 5;\nexport const DIRECTORY_CONCURRENCY_LIMIT = 3;\nexport const FILE_CONCURRENCY_LIMIT = 10;\n\nexport const INITIAL_CLAUDE_PROMPT = `You are a code migration assistant that helps migrate TypeScript/JSX code of deprecated Wise Design System (WDS) components from '@transferwise/components', to their new replacement components as detailed in the relevant migration guide. The following is a list of rules that must always be followed throughout the migration process.\n\nRules:\n1. Only ever modify files via the Edit tool - do not use the Write tool\n2. When identifying what code to migrate within a file, explain how you identified it first.\n3. Migrate components per provided migration rules\n4. Maintain TypeScript type safety and update types to match new API\n5. Map props: handle renamed, deprecated, new required, and changed types\n6. Update imports to new WDS components and types\n7. Preserve code style, formatting, and calculated logic\n8. Handle conditional rendering, spread props, and complex expressions\n9. Note: New components may lack feature parity with legacy versions\n10. Only modify code requiring changes per migration rules, and any impacted surrounding code for context.\n11. Final result response should just be whether the migration was successful overall, or if any errors were encountered\n - Do not summarise or explain the changes made\n12. Explain your reasoning and justification before making changes, as you edit each file.\n - Keep it concise and succinct, as only bullet points\n13. After modifying the file, do not summarise the changes made.\n14. If you do not have permission to edit a file, still attempt to edit it and then move onto the next file.\n\nAdditionally you'll receive:\n- File paths to migrate in individual queries\n- Deprecated component names at the end of this prompt\n- Migration context and guide for each deprecated component`;\n","/** Split the path string to get the relative path after the directory, and wrap with ANSI color codes */\nexport function formatPathOutput(directory: string, path?: string, asDim?: boolean): string {\n const relativePath = path ? (path.split(directory.replace('.', ''))[1] ?? path) : directory;\n return asDim ? `\\x1b[2m${relativePath}\\x1b[0m` : `\\x1b[32m${relativePath}\\x1b[0m`;\n}\n","/** Generates a formatted string representing the total elapsed time since the given start time */\nexport function generateElapsedTime(startTime: number): string {\n const endTime = Date.now();\n const elapsedTime = Math.floor((endTime - startTime) / 1000);\n const hours = Math.floor(elapsedTime / 3600);\n const minutes = Math.floor((elapsedTime % 3600) / 60);\n const seconds = elapsedTime % 60;\n\n return `${hours ? `${hours}h ` : ''}${minutes ? `${minutes}m ` : ''}${seconds ? `${seconds}s` : ''}`;\n}\n","import https from 'node:https';\n\nimport type { DefaultRenderer, ListrTaskWrapper, SimpleRenderer } from 'listr2';\n\nimport { VPN_COUNTDOWN_TIMEOUT } from '../../constants/claude';\n\n/** Checks VPN connectivity by pinging the provided base URL's /health endpoint, with countdown retries */\nexport async function checkVPN(\n baseUrl: string | undefined,\n task: ListrTaskWrapper<never, typeof DefaultRenderer, typeof SimpleRenderer>,\n): Promise<void> {\n if (!baseUrl) return;\n\n const checkOnce = async (): Promise<boolean> =>\n new Promise<boolean>((resolveCheck) => {\n const url = new URL('/health', baseUrl);\n const req = https.get(url, { timeout: 2000, rejectUnauthorized: false }, (res) => {\n const ok = !!(res.statusCode && res.statusCode >= 200 && res.statusCode < 400);\n res.resume();\n resolveCheck(ok);\n });\n req.on('timeout', () => {\n req.destroy(new Error('timeout'));\n });\n req.on('error', () => resolveCheck(false));\n });\n\n while (true) {\n const ok = await checkOnce();\n if (ok) {\n // eslint-disable-next-line no-param-reassign\n task.title = 'Connected to VPN';\n break;\n }\n\n // Countdown from 5s\n for (let countdown = VPN_COUNTDOWN_TIMEOUT; countdown > 0; countdown -= 1) {\n // eslint-disable-next-line no-param-reassign\n task.output = `Please connect to VPN... retrying in ${countdown}s`;\n await new Promise<void>((response) => {\n setTimeout(response, 1000);\n });\n }\n }\n}\n","import { execSync } from 'node:child_process';\nimport { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport { type Options, query } from '@anthropic-ai/claude-agent-sdk';\nimport type { Manager } from '@listr2/manager';\nimport type { DefaultRenderer, ListrTaskWrapper, SimpleRenderer } from 'listr2';\n\nimport {\n CLAUDE_SETTINGS_FILE,\n DIRECTORY_CONCURRENCY_LIMIT,\n INITIAL_CLAUDE_PROMPT,\n} from '../../constants/claude';\nimport { CONSOLE_ICONS } from '../../constants/common';\nimport { checkVPN, formatPathOutput, generateElapsedTime } from '../common';\n\ninterface ClaudeSettings {\n apiKeyHelper?: string;\n env?: {\n ANTHROPIC_BASE_URL?: string;\n ANTHROPIC_CUSTOM_HEADERS?: string;\n ANTHROPIC_DEFAULT_SONNET_MODEL?: string;\n ANTHROPIC_DEFAULT_HAIKU_MODEL?: string;\n ANTHROPIC_DEFAULT_OPUS_MODEL?: string;\n API_TIMEOUT_MS?: string;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n}\n\ninterface QueryOptionProps {\n additionalPromptContext?: string;\n}\n\n/**\n * Creates reusable query options for Claude session, including authentication and system prompt\n */\nfunction getQueryOptions({ additionalPromptContext }: QueryOptionProps): Options {\n // Read settings from ~/.claude/settings.json to get headers and apiKeyHelper\n const claudeSettingsPath = resolve(process.env.HOME || '', CLAUDE_SETTINGS_FILE);\n const settings = JSON.parse(readFileSync(claudeSettingsPath, 'utf-8')) as ClaudeSettings;\n\n // Get API key by executing the apiKeyHelper script, for authenticating with Okta via LLM Gateway\n let apiKey;\n try {\n apiKey = execSync(`bash ${settings.apiKeyHelper}`, {\n encoding: 'utf-8',\n }).trim();\n } catch {}\n\n if (!apiKey || !settings.env?.ANTHROPIC_BASE_URL) {\n throw new Error(\n 'Failed to retrieve Anthropic API key or Base URL. Please check your Claude Code x LLM Gateway configuration - https://transferwise.atlassian.net/wiki/x/_YUe3Q',\n );\n }\n\n const { ANTHROPIC_CUSTOM_HEADERS, ...restEnvVars } = settings?.env ?? {};\n\n const envVars = {\n ANTHROPIC_AUTH_TOKEN: apiKey,\n ANTHROPIC_CUSTOM_HEADERS,\n ...restEnvVars,\n PATH: process.env.PATH, // Specifying PATH, as Claude Agent SDK seems to struggle consuming the actual environment PATH\n };\n\n return {\n env: envVars,\n permissionMode: 'acceptEdits',\n systemPrompt: {\n type: 'preset',\n preset: 'claude_code',\n append: `${INITIAL_CLAUDE_PROMPT}\\n${additionalPromptContext}`,\n },\n allowedTools: ['Grep', 'Read'],\n settingSources: ['local', 'project', 'user'],\n };\n}\n\n/**\n * Initiate a new Claude session/conversation and return reusable options\n */\nexport async function initiateClaudeSessionOptions(\n manager: Manager,\n additionalPromptContext: string,\n): Promise<Options> {\n let options: Options = {};\n\n manager.add([\n {\n title: 'Configuring Claude connection and instance...',\n task: async (ctx, task) => {\n options = getQueryOptions({ additionalPromptContext });\n\n return task.newListr([\n {\n title: 'Checking VPN connection...',\n task: async (\n subCtx,\n subtask: ListrTaskWrapper<never, typeof DefaultRenderer, typeof SimpleRenderer>,\n ) => checkVPN(options.env?.ANTHROPIC_BASE_URL, subtask),\n },\n {\n title: 'Initialising Claude session',\n task: async (\n subCtx,\n subtask: ListrTaskWrapper<never, typeof DefaultRenderer, typeof SimpleRenderer>,\n ) => {\n // eslint-disable-next-line no-param-reassign\n subtask.output = 'Your browser may open for Okta authentication if required';\n\n const result = query({\n options,\n prompt: `This is an initialisation query to start a new Claude code migration session. No text response is needed.`,\n });\n\n for await (const message of result) {\n switch (message.type) {\n case 'system':\n if (message.subtype === 'init' && !options.resume) {\n // Set the session ID to resume the conversation in future queries\n options.resume = message.session_id;\n }\n break;\n default:\n if (message.type === 'result' && message.subtype !== 'success') {\n throw new Error(\n `Claude encountered an error when initialising: ${message.errors.join('\\n')}`,\n );\n }\n }\n }\n\n // eslint-disable-next-line no-param-reassign\n task.title = 'Successfully configured and initialised Claude\\n';\n },\n },\n ]);\n },\n },\n ]);\n\n // Set manager to run tasks concurrently, once initialisation steps are done\n await manager.runAll().finally(() => {\n // eslint-disable-next-line no-param-reassign\n manager.options = {\n concurrent: DIRECTORY_CONCURRENCY_LIMIT,\n };\n });\n\n return options;\n}\n\n/**\n * Queries Claude with the given path and handles logging of success/error messages\n */\nexport async function queryClaude(\n directory: string,\n filePath: string,\n options: Options,\n task: ListrTaskWrapper<never, typeof DefaultRenderer, typeof SimpleRenderer>,\n isDebug = false,\n) {\n const startTime = Date.now();\n const result = query({\n options,\n prompt: filePath,\n });\n\n for await (const message of result) {\n switch (message.type) {\n case 'result':\n if (message.subtype === 'success' && isDebug) {\n // eslint-disable-next-line no-param-reassign\n task.title = `\\x1b[2m${formatPathOutput(directory, filePath)}\\x1b[0m]`;\n // eslint-disable-next-line no-param-reassign\n task.output = `\\x1b[2mMigrated in ${generateElapsedTime(startTime)}\\x1b[0m`;\n } else if (message.is_error) {\n // eslint-disable-next-line no-param-reassign\n task.title = `\\x1b[2m${formatPathOutput(directory, filePath)}\\x1b[0m]`;\n // eslint-disable-next-line no-param-reassign\n task.output = `${CONSOLE_ICONS.error} Claude encountered an error: ${JSON.stringify(message)}`;\n }\n break;\n default:\n }\n }\n}\n","import { execSync } from 'node:child_process';\nimport { readFileSync } from 'node:fs';\n\nimport { Manager } from '@listr2/manager';\nimport {\n type DefaultRenderer,\n Listr,\n type ListrTask,\n type ListrTaskWrapper,\n type SimpleRenderer,\n} from 'listr2';\n\nimport { FILE_CONCURRENCY_LIMIT } from '../../constants/claude';\nimport { formatPathOutput, generateElapsedTime } from '../common';\nimport { initiateClaudeSessionOptions, queryClaude } from './query';\n\ninterface ClaudeTransformerOptions {\n targetDirectories: string[];\n componentGrepPattern: RegExp;\n additionalPromptContext: string;\n isDebug?: boolean;\n}\n\n/**\n * Performs code migration using Claude for the specified target directories, for the provided deprecated components and migration guides.\n * @param targetDirectories - Array of directory paths to process\n * @param componentGrepPattern - RegExp pattern to identify files needing migration (e.g. via deprecated component imports)\n * @param isDebug - Whether to enable debug logging\n * @param additionalPromptContext - Additional context to include in the initial Claude prompt before processing files\n */\nexport const claudeTransformer = async ({\n targetDirectories,\n componentGrepPattern,\n additionalPromptContext,\n isDebug = false,\n}: ClaudeTransformerOptions) => {\n process.setMaxListeners(20); // Resolves potential memory issues with how Claude handles its own event listeners\n const startTime = Date.now();\n // Create manager for handling multiple listr instances\n const manager = new Manager({\n concurrent: true,\n });\n const queryOptions = await initiateClaudeSessionOptions(manager, additionalPromptContext);\n\n manager.add([\n {\n title: 'Processing target directories for migration...',\n task: async (ctx, task) => {\n return task.newListr(\n targetDirectories.map((directory) => {\n // Find all .tsx files in the directory\n const allTsxFiles = execSync(`find \"${directory}\" -name \"*.tsx\" -type f`, {\n encoding: 'utf-8',\n })\n .trim()\n .split('\\n')\n .filter(Boolean);\n\n // Filter files that match the pattern by reading and testing each file\n const matchingFilePaths = allTsxFiles.filter((filePath) => {\n const content = readFileSync(filePath, 'utf-8');\n return componentGrepPattern.test(content);\n });\n\n // No files to process in this directory, so we add a task that's immediately skipped\n if (matchingFilePaths.length === 0) {\n return {\n title: `\\x1b[2m${formatPathOutput(directory)} - No files need migration\\x1b[0m`,\n task: (subCtx: ListrTask): void => {\n // eslint-disable-next-line no-param-reassign\n subCtx.skip = true;\n },\n };\n }\n\n return {\n title: `${formatPathOutput(directory)} - Found \\x1b[32m${matchingFilePaths.length}\\x1b[0m file(s) needing migration`,\n task: async (subCtx, parentTask) => {\n // eslint-disable-next-line no-param-reassign\n parentTask.title = `${formatPathOutput(directory)} - Migrating \\x1b[32m${matchingFilePaths.length}\\x1b[0m file(s)...`;\n const completedFilesInDirectory = { count: 0 };\n return parentTask\n .newListr(\n matchingFilePaths.map((filePath) => ({\n title: '', // No title so it runs in the background without any console output\n task: async (\n fileCtx,\n fileTask: ListrTaskWrapper<\n never,\n typeof DefaultRenderer,\n typeof SimpleRenderer\n >,\n ) => {\n await queryClaude(\n directory,\n filePath,\n queryOptions,\n fileTask,\n isDebug,\n ).finally(() => {\n // Update parent task title with progress for each completed file migration\n completedFilesInDirectory.count += 1;\n const isDim =\n completedFilesInDirectory.count === matchingFilePaths.length;\n // eslint-disable-next-line no-param-reassign\n parentTask.title = `${isDim ? '\\x1b[2m' : ''}${formatPathOutput(directory)} - Migrated \\x1b[32m${completedFilesInDirectory.count}\\x1b[0m/\\x1b[32m${matchingFilePaths.length}\\x1b[0m files${isDim ? '\\x1b[0m' : ''}`;\n });\n },\n })),\n { concurrent: FILE_CONCURRENCY_LIMIT },\n )\n .run();\n },\n };\n }),\n { rendererOptions: { suffixSkips: false, collapseSubtasks: false } },\n );\n },\n },\n ]);\n\n // Run all directory tasks concurrently, with final follow up/summary task\n await manager.runAll().finally(async () => {\n await new Listr([\n {\n title: `Finished migrating - elapsed time: \\x1b[32m${generateElapsedTime(startTime)}\\x1b[0m `,\n task: async () => {\n // Task completes immediately\n },\n },\n ]).run();\n });\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"claude-D6IwFmoz.js","names":[],"sources":["../src/constants/claude.ts","../src/helpers/common/pathUtils.ts","../src/helpers/common/timerUtils.ts","../src/helpers/common/vpnUtils.ts","../src/helpers/claude/query.ts","../src/helpers/claude/transformer.ts"],"sourcesContent":["export const CLAUDE_SETTINGS_FILE = '.claude/settings.json';\nexport const VPN_COUNTDOWN_TIMEOUT = 5;\nexport const DIRECTORY_CONCURRENCY_LIMIT = 3;\nexport const FILE_CONCURRENCY_LIMIT = 10;\n\nexport const INITIAL_CLAUDE_PROMPT = `You are a code migration assistant that helps migrate TypeScript/JSX code of deprecated Wise Design System (WDS) components from '@transferwise/components', to their new replacement components as detailed in the relevant migration guide. The following is a list of rules that must always be followed throughout the migration process.\n\nRules:\n1. Only ever modify files via the Edit tool - do not use the Write tool\n2. When identifying what code to migrate within a file, explain how you identified it first.\n3. Migrate components per provided migration rules\n4. Maintain TypeScript type safety and update types to match new API\n5. Map props: handle renamed, deprecated, new required, and changed types\n6. Update imports to new WDS components and types\n7. Preserve code style, formatting, and calculated logic\n8. Handle conditional rendering, spread props, and complex expressions\n9. Note: New components may lack feature parity with legacy versions\n10. Only modify code requiring changes per migration rules, and any impacted surrounding code for context.\n11. Final result response should just be whether the migration was successful overall, or if any errors were encountered\n - Do not summarise or explain the changes made\n12. Explain your reasoning and justification before making changes, as you edit each file.\n - Keep it concise and succinct, as only bullet points\n13. After modifying the file, do not summarise the changes made.\n14. If you do not have permission to edit a file, still attempt to edit it and then move onto the next file.\n\nAdditionally you'll receive:\n- File paths to migrate in individual queries\n- Deprecated component names at the end of this prompt\n- Migration context and guide for each deprecated component`;\n","/** Split the path string to get the relative path after the directory, and wrap with ANSI color codes */\nexport function formatPathOutput(directory: string, path?: string, asDim?: boolean): string {\n const relativePath = path ? (path.split(directory.replace('.', ''))[1] ?? path) : directory;\n return asDim ? `\\x1b[2m${relativePath}\\x1b[0m` : `\\x1b[32m${relativePath}\\x1b[0m`;\n}\n","/** Generates a formatted string representing the total elapsed time since the given start time */\nexport function generateElapsedTime(startTime: number): string {\n const endTime = Date.now();\n const elapsedTime = Math.floor((endTime - startTime) / 1000);\n const hours = Math.floor(elapsedTime / 3600);\n const minutes = Math.floor((elapsedTime % 3600) / 60);\n const seconds = elapsedTime % 60;\n\n return `${hours ? `${hours}h ` : ''}${minutes ? `${minutes}m ` : ''}${seconds ? `${seconds}s` : ''}`;\n}\n","import https from 'node:https';\n\nimport type { DefaultRenderer, ListrTaskWrapper, SimpleRenderer } from 'listr2';\n\nimport { VPN_COUNTDOWN_TIMEOUT } from '../../constants/claude';\n\n/** Checks VPN connectivity by pinging the provided base URL's /health endpoint, with countdown retries */\nexport async function checkVPN(\n baseUrl: string | undefined,\n task: ListrTaskWrapper<never, typeof DefaultRenderer, typeof SimpleRenderer>,\n): Promise<void> {\n if (!baseUrl) return;\n\n const checkOnce = async (): Promise<boolean> =>\n new Promise<boolean>((resolveCheck) => {\n const url = new URL('/health', baseUrl);\n const req = https.get(url, { timeout: 2000, rejectUnauthorized: false }, (res) => {\n const ok = !!(res.statusCode && res.statusCode >= 200 && res.statusCode < 400);\n res.resume();\n resolveCheck(ok);\n });\n req.on('timeout', () => {\n req.destroy(new Error('timeout'));\n });\n req.on('error', () => resolveCheck(false));\n });\n\n while (true) {\n const ok = await checkOnce();\n if (ok) {\n // eslint-disable-next-line no-param-reassign\n task.title = 'Connected to VPN';\n break;\n }\n\n // Countdown from 5s\n for (let countdown = VPN_COUNTDOWN_TIMEOUT; countdown > 0; countdown -= 1) {\n // eslint-disable-next-line no-param-reassign\n task.output = `Please connect to VPN... retrying in ${countdown}s`;\n await new Promise<void>((response) => {\n setTimeout(response, 1000);\n });\n }\n }\n}\n","import { execSync } from 'node:child_process';\nimport { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport { type Options, query } from '@anthropic-ai/claude-agent-sdk';\nimport type { Manager } from '@listr2/manager';\nimport type { DefaultRenderer, ListrTaskWrapper, SimpleRenderer } from 'listr2';\n\nimport {\n CLAUDE_SETTINGS_FILE,\n DIRECTORY_CONCURRENCY_LIMIT,\n INITIAL_CLAUDE_PROMPT,\n} from '../../constants/claude';\nimport { CONSOLE_ICONS } from '../../constants/common';\nimport { checkVPN, formatPathOutput, generateElapsedTime } from '../common';\n\ninterface ClaudeSettings {\n apiKeyHelper?: string;\n env?: {\n ANTHROPIC_BASE_URL?: string;\n ANTHROPIC_CUSTOM_HEADERS?: string;\n ANTHROPIC_DEFAULT_SONNET_MODEL?: string;\n ANTHROPIC_DEFAULT_HAIKU_MODEL?: string;\n ANTHROPIC_DEFAULT_OPUS_MODEL?: string;\n API_TIMEOUT_MS?: string;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n}\n\ninterface QueryOptionProps {\n additionalPromptContext?: string;\n}\n\n/**\n * Creates reusable query options for Claude session, including authentication and system prompt\n */\nfunction getQueryOptions({ additionalPromptContext }: QueryOptionProps): Options {\n // Read settings from ~/.claude/settings.json to get headers and apiKeyHelper\n const claudeSettingsPath = resolve(process.env.HOME || '', CLAUDE_SETTINGS_FILE);\n const settings = JSON.parse(readFileSync(claudeSettingsPath, 'utf-8')) as ClaudeSettings;\n\n // Get API key by executing the apiKeyHelper script, for authenticating with Okta via LLM Gateway\n let apiKey;\n try {\n apiKey = execSync(`bash ${settings.apiKeyHelper}`, {\n encoding: 'utf-8',\n }).trim();\n } catch {}\n\n if (!apiKey || !settings.env?.ANTHROPIC_BASE_URL) {\n throw new Error(\n 'Failed to retrieve Anthropic API key or Base URL. Please check your Claude Code x LLM Gateway configuration - https://transferwise.atlassian.net/wiki/x/_YUe3Q',\n );\n }\n\n const { ANTHROPIC_CUSTOM_HEADERS, ...restEnvVars } = settings?.env ?? {};\n\n const envVars = {\n ANTHROPIC_AUTH_TOKEN: apiKey,\n ANTHROPIC_CUSTOM_HEADERS,\n ...restEnvVars,\n PATH: process.env.PATH, // Specifying PATH, as Claude Agent SDK seems to struggle consuming the actual environment PATH\n };\n\n return {\n env: envVars,\n permissionMode: 'acceptEdits',\n systemPrompt: {\n type: 'preset',\n preset: 'claude_code',\n append: `${INITIAL_CLAUDE_PROMPT}\\n${additionalPromptContext}`,\n },\n allowedTools: ['Grep', 'Read'],\n settingSources: ['local', 'project', 'user'],\n };\n}\n\n/**\n * Initiate a new Claude session/conversation and return reusable options\n */\nexport async function initiateClaudeSessionOptions(\n manager: Manager,\n additionalPromptContext: string,\n): Promise<Options> {\n let options: Options = {};\n\n manager.add([\n {\n title: 'Configuring Claude connection and instance...',\n task: async (ctx, task) => {\n options = getQueryOptions({ additionalPromptContext });\n\n return task.newListr([\n {\n title: 'Checking VPN connection...',\n task: async (\n subCtx,\n subtask: ListrTaskWrapper<never, typeof DefaultRenderer, typeof SimpleRenderer>,\n ) => checkVPN(options.env?.ANTHROPIC_BASE_URL, subtask),\n },\n {\n title: 'Initialising Claude session',\n task: async (\n subCtx,\n subtask: ListrTaskWrapper<never, typeof DefaultRenderer, typeof SimpleRenderer>,\n ) => {\n // eslint-disable-next-line no-param-reassign\n subtask.output = 'Your browser may open for Okta authentication if required';\n\n const result = query({\n options,\n prompt: `This is an initialisation query to start a new Claude code migration session. No text response is needed.`,\n });\n\n for await (const message of result) {\n switch (message.type) {\n case 'system':\n if (message.subtype === 'init' && !options.resume) {\n // Set the session ID to resume the conversation in future queries\n options.resume = message.session_id;\n }\n break;\n default:\n if (message.type === 'result' && message.subtype !== 'success') {\n throw new Error(\n `Claude encountered an error when initialising: ${message.errors.join('\\n')}`,\n );\n }\n }\n }\n\n // eslint-disable-next-line no-param-reassign\n task.title = 'Successfully configured and initialised Claude\\n';\n },\n },\n ]);\n },\n },\n ]);\n\n // Set manager to run tasks concurrently, once initialisation steps are done\n await manager.runAll().finally(() => {\n // eslint-disable-next-line no-param-reassign\n manager.options = {\n concurrent: DIRECTORY_CONCURRENCY_LIMIT,\n };\n });\n\n return options;\n}\n\n/**\n * Queries Claude with the given path and handles logging of success/error messages\n */\nexport async function queryClaude(\n directory: string,\n filePath: string,\n options: Options,\n task: ListrTaskWrapper<never, typeof DefaultRenderer, typeof SimpleRenderer>,\n isDebug = false,\n) {\n const startTime = Date.now();\n const result = query({\n options,\n prompt: filePath,\n });\n\n for await (const message of result) {\n switch (message.type) {\n case 'result':\n if (message.subtype === 'success' && isDebug) {\n // eslint-disable-next-line no-param-reassign\n task.title = `\\x1b[2m${formatPathOutput(directory, filePath)}\\x1b[0m]`;\n // eslint-disable-next-line no-param-reassign\n task.output = `\\x1b[2mMigrated in ${generateElapsedTime(startTime)}\\x1b[0m`;\n } else if (message.is_error) {\n // eslint-disable-next-line no-param-reassign\n task.title = `\\x1b[2m${formatPathOutput(directory, filePath)}\\x1b[0m]`;\n // eslint-disable-next-line no-param-reassign\n task.output = `${CONSOLE_ICONS.error} Claude encountered an error: ${JSON.stringify(message)}`;\n }\n break;\n default:\n }\n }\n}\n","import { execSync } from 'node:child_process';\nimport { readFileSync } from 'node:fs';\n\nimport { Manager } from '@listr2/manager';\nimport {\n type DefaultRenderer,\n Listr,\n type ListrTask,\n type ListrTaskWrapper,\n type SimpleRenderer,\n} from 'listr2';\n\nimport { FILE_CONCURRENCY_LIMIT } from '../../constants/claude';\nimport { formatPathOutput, generateElapsedTime } from '../common';\nimport { initiateClaudeSessionOptions, queryClaude } from './query';\n\ninterface ClaudeTransformerOptions {\n targetDirectories: string[];\n componentGrepPattern: RegExp;\n additionalPromptContext: string;\n isDebug?: boolean;\n}\n\n/**\n * Performs code migration using Claude for the specified target directories, for the provided deprecated components and migration guides.\n * @param targetDirectories - Array of directory paths to process\n * @param componentGrepPattern - RegExp pattern to identify files needing migration (e.g. via deprecated component imports)\n * @param isDebug - Whether to enable debug logging\n * @param additionalPromptContext - Additional context to include in the initial Claude prompt before processing files\n */\nexport const claudeTransformer = async ({\n targetDirectories,\n componentGrepPattern,\n additionalPromptContext,\n isDebug = false,\n}: ClaudeTransformerOptions) => {\n process.setMaxListeners(20); // Resolves potential memory issues with how Claude handles its own event listeners\n const startTime = Date.now();\n // Create manager for handling multiple listr instances\n const manager = new Manager({\n concurrent: true,\n });\n const queryOptions = await initiateClaudeSessionOptions(manager, additionalPromptContext);\n\n manager.add([\n {\n title: 'Processing target directories for migration...',\n task: async (ctx, task) => {\n return task.newListr(\n targetDirectories.map((directory) => {\n // Find all .tsx files in the directory\n const allTsxFiles = execSync(`find \"${directory}\" -name \"*.tsx\" -type f`, {\n encoding: 'utf-8',\n })\n .trim()\n .split('\\n')\n .filter(Boolean);\n\n // Filter files that match the pattern by reading and testing each file\n const matchingFilePaths = allTsxFiles.filter((filePath) => {\n const content = readFileSync(filePath, 'utf-8');\n return componentGrepPattern.test(content);\n });\n\n // No files to process in this directory, so we add a task that's immediately skipped\n if (matchingFilePaths.length === 0) {\n return {\n title: `\\x1b[2m${formatPathOutput(directory)} - No files need migration\\x1b[0m`,\n task: (subCtx: ListrTask): void => {\n // eslint-disable-next-line no-param-reassign\n subCtx.skip = true;\n },\n };\n }\n\n return {\n title: `${formatPathOutput(directory)} - Found \\x1b[32m${matchingFilePaths.length}\\x1b[0m file(s) needing migration`,\n task: async (subCtx, parentTask) => {\n // eslint-disable-next-line no-param-reassign\n parentTask.title = `${formatPathOutput(directory)} - Migrating \\x1b[32m${matchingFilePaths.length}\\x1b[0m file(s)...`;\n const completedFilesInDirectory = { count: 0 };\n return parentTask\n .newListr(\n matchingFilePaths.map((filePath) => ({\n title: '', // No title so it runs in the background without any console output\n task: async (\n fileCtx,\n fileTask: ListrTaskWrapper<\n never,\n typeof DefaultRenderer,\n typeof SimpleRenderer\n >,\n ) => {\n await queryClaude(\n directory,\n filePath,\n queryOptions,\n fileTask,\n isDebug,\n ).finally(() => {\n // Update parent task title with progress for each completed file migration\n completedFilesInDirectory.count += 1;\n const isDim =\n completedFilesInDirectory.count === matchingFilePaths.length;\n // eslint-disable-next-line no-param-reassign\n parentTask.title = `${isDim ? '\\x1b[2m' : ''}${formatPathOutput(directory)} - Migrated \\x1b[32m${completedFilesInDirectory.count}\\x1b[0m/\\x1b[32m${matchingFilePaths.length}\\x1b[0m files${isDim ? '\\x1b[0m' : ''}`;\n });\n },\n })),\n { concurrent: FILE_CONCURRENCY_LIMIT },\n )\n .run();\n },\n };\n }),\n { rendererOptions: { suffixSkips: false, collapseSubtasks: false } },\n );\n },\n },\n ]);\n\n // Run all directory tasks concurrently, with final follow up/summary task\n await manager.runAll().finally(async () => {\n await new Listr([\n {\n title: `Finished migrating - elapsed time: \\x1b[32m${generateElapsedTime(startTime)}\\x1b[0m `,\n task: async () => {\n // Task completes immediately\n },\n },\n ]).run();\n });\n};\n"],"mappings":"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"}
@@ -3,7 +3,7 @@ import { exec, execSync } from "node:child_process";
3
3
  import fs from "node:fs/promises";
4
4
  import path, { dirname, join, relative, resolve } from "node:path";
5
5
  import { checkbox, confirm, input, search, select } from "@inquirer/prompts";
6
- import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
6
+ import fs$1, { existsSync, promises, readFileSync, readdirSync, statSync } from "node:fs";
7
7
  import { promisify } from "node:util";
8
8
  //#region \0rolldown/runtime.js
9
9
  var __create = Object.create;
@@ -23,19 +23,21 @@ var __copyProps = (to, from, except, desc) => {
23
23
  }
24
24
  return to;
25
25
  };
26
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
26
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
27
27
  value: mod,
28
28
  enumerable: true
29
29
  }) : target, mod));
30
30
  //#endregion
31
31
  //#region node_modules/.pnpm/semver@7.8.5/node_modules/semver/internal/constants.js
32
32
  var require_constants = /* @__PURE__ */ __commonJSMin(((exports, module) => {
33
+ const SEMVER_SPEC_VERSION = "2.0.0";
34
+ const MAX_LENGTH = 256;
35
+ const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991;
33
36
  module.exports = {
34
- MAX_LENGTH: 256,
37
+ MAX_LENGTH,
35
38
  MAX_SAFE_COMPONENT_LENGTH: 16,
36
- MAX_SAFE_BUILD_LENGTH: 250,
37
- MAX_SAFE_INTEGER: Number.MAX_SAFE_INTEGER ||
38
- /* istanbul ignore next */ 9007199254740991,
39
+ MAX_SAFE_BUILD_LENGTH: MAX_LENGTH - 6,
40
+ MAX_SAFE_INTEGER,
39
41
  RELEASE_TYPES: [
40
42
  "major",
41
43
  "premajor",
@@ -45,7 +47,7 @@ var require_constants = /* @__PURE__ */ __commonJSMin(((exports, module) => {
45
47
  "prepatch",
46
48
  "prerelease"
47
49
  ],
48
- SEMVER_SPEC_VERSION: "2.0.0",
50
+ SEMVER_SPEC_VERSION,
49
51
  FLAG_INCLUDE_PRERELEASE: 1,
50
52
  FLAG_LOOSE: 2
51
53
  };
@@ -183,10 +185,9 @@ var require_semver$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
183
185
  module.exports = class SemVer {
184
186
  constructor(version, options) {
185
187
  options = parseOptions(options);
186
- if (version instanceof SemVer) {
187
- if (version.loose === !!options.loose && version.includePrerelease === !!options.includePrerelease) return version;
188
- else version = version.version;
189
- } else if (typeof version !== "string") throw new TypeError(`Invalid version. Must be a string. Got type "${typeof version}".`);
188
+ if (version instanceof SemVer) if (version.loose === !!options.loose && version.includePrerelease === !!options.includePrerelease) return version;
189
+ else version = version.version;
190
+ else if (typeof version !== "string") throw new TypeError(`Invalid version. Must be a string. Got type "${typeof version}".`);
190
191
  if (version.length > MAX_LENGTH) throw new TypeError(`version is longer than ${MAX_LENGTH} characters`);
191
192
  debug("SemVer", version, options);
192
193
  this.options = options;
@@ -639,7 +640,9 @@ var require_truncate = /* @__PURE__ */ __commonJSMin(((exports, module) => {
639
640
  version.minor = 0;
640
641
  version.patch = 0;
641
642
  break;
642
- case "minor": version.patch = 0;
643
+ case "minor":
644
+ version.patch = 0;
645
+ break;
643
646
  }
644
647
  return version.format();
645
648
  };
@@ -688,10 +691,8 @@ var require_range = /* @__PURE__ */ __commonJSMin(((exports, module) => {
688
691
  module.exports = class Range {
689
692
  constructor(range, options) {
690
693
  options = parseOptions(options);
691
- if (range instanceof Range) {
692
- if (range.loose === !!options.loose && range.includePrerelease === !!options.includePrerelease) return range;
693
- else return new Range(range.raw, options);
694
- }
694
+ if (range instanceof Range) if (range.loose === !!options.loose && range.includePrerelease === !!options.includePrerelease) return range;
695
+ else return new Range(range.raw, options);
695
696
  if (range instanceof Comparator) {
696
697
  this.raw = range.value;
697
698
  this.set = [[range]];
@@ -861,21 +862,18 @@ var require_range = /* @__PURE__ */ __commonJSMin(((exports, module) => {
861
862
  let ret;
862
863
  if (isX(M)) ret = "";
863
864
  else if (isX(m)) ret = `>=${M}.0.0${z} <${+M + 1}.0.0-0`;
864
- else if (isX(p)) {
865
- if (M === "0") ret = `>=${M}.${m}.0${z} <${M}.${+m + 1}.0-0`;
866
- else ret = `>=${M}.${m}.0${z} <${+M + 1}.0.0-0`;
867
- } else if (pr) {
865
+ else if (isX(p)) if (M === "0") ret = `>=${M}.${m}.0${z} <${M}.${+m + 1}.0-0`;
866
+ else ret = `>=${M}.${m}.0${z} <${+M + 1}.0.0-0`;
867
+ else if (pr) {
868
868
  debug("replaceCaret pr", pr);
869
- if (M === "0") {
870
- if (m === "0") ret = `>=${M}.${m}.${p}-${pr} <${M}.${m}.${+p + 1}-0`;
871
- else ret = `>=${M}.${m}.${p}-${pr} <${M}.${+m + 1}.0-0`;
872
- } else ret = `>=${M}.${m}.${p}-${pr} <${+M + 1}.0.0-0`;
869
+ if (M === "0") if (m === "0") ret = `>=${M}.${m}.${p}-${pr} <${M}.${m}.${+p + 1}-0`;
870
+ else ret = `>=${M}.${m}.${p}-${pr} <${M}.${+m + 1}.0-0`;
871
+ else ret = `>=${M}.${m}.${p}-${pr} <${+M + 1}.0.0-0`;
873
872
  } else {
874
873
  debug("no pr");
875
- if (M === "0") {
876
- if (m === "0") ret = `>=${M}.${m}.${p} <${M}.${m}.${+p + 1}-0`;
877
- else ret = `>=${M}.${m}.${p} <${M}.${+m + 1}.0-0`;
878
- } else ret = `>=${M}.${m}.${p} <${+M + 1}.0.0-0`;
874
+ if (M === "0") if (m === "0") ret = `>=${M}.${m}.${p} <${M}.${m}.${+p + 1}-0`;
875
+ else ret = `>=${M}.${m}.${p} <${M}.${+m + 1}.0-0`;
876
+ else ret = `>=${M}.${m}.${p} <${+M + 1}.0.0-0`;
879
877
  }
880
878
  debug("caret return", ret);
881
879
  return ret;
@@ -897,10 +895,9 @@ var require_range = /* @__PURE__ */ __commonJSMin(((exports, module) => {
897
895
  const anyX = xp;
898
896
  if (gtlt === "=" && anyX) gtlt = "";
899
897
  pr = options.includePrerelease ? "-0" : "";
900
- if (xM) {
901
- if (gtlt === ">" || gtlt === "<") ret = "<0.0.0-0";
902
- else ret = "*";
903
- } else if (gtlt && anyX) {
898
+ if (xM) if (gtlt === ">" || gtlt === "<") ret = "<0.0.0-0";
899
+ else ret = "*";
900
+ else if (gtlt && anyX) {
904
901
  if (xm) m = 0;
905
902
  p = 0;
906
903
  if (gtlt === ">") {
@@ -974,10 +971,8 @@ var require_comparator = /* @__PURE__ */ __commonJSMin(((exports, module) => {
974
971
  }
975
972
  constructor(comp, options) {
976
973
  options = parseOptions(options);
977
- if (comp instanceof Comparator) {
978
- if (comp.loose === !!options.loose) return comp;
979
- else comp = comp.value;
980
- }
974
+ if (comp instanceof Comparator) if (comp.loose === !!options.loose) return comp;
975
+ else comp = comp.value;
981
976
  comp = comp.trim().split(/\s+/).join(" ");
982
977
  debug("comparator", comp, options);
983
978
  this.options = options;
@@ -1297,15 +1292,11 @@ var require_subset = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1297
1292
  const minimumVersion = [new Comparator(">=0.0.0")];
1298
1293
  const simpleSubset = (sub, dom, options) => {
1299
1294
  if (sub === dom) return true;
1300
- if (sub.length === 1 && sub[0].semver === ANY) {
1301
- if (dom.length === 1 && dom[0].semver === ANY) return true;
1302
- else if (options.includePrerelease) sub = minimumVersionWithPreRelease;
1303
- else sub = minimumVersion;
1304
- }
1305
- if (dom.length === 1 && dom[0].semver === ANY) {
1306
- if (options.includePrerelease) return true;
1307
- else dom = minimumVersion;
1308
- }
1295
+ if (sub.length === 1 && sub[0].semver === ANY) if (dom.length === 1 && dom[0].semver === ANY) return true;
1296
+ else if (options.includePrerelease) sub = minimumVersionWithPreRelease;
1297
+ else sub = minimumVersion;
1298
+ if (dom.length === 1 && dom[0].semver === ANY) if (options.includePrerelease) return true;
1299
+ else dom = minimumVersion;
1309
1300
  const eqSet = /* @__PURE__ */ new Set();
1310
1301
  let gt, lt;
1311
1302
  for (const c of sub) if (c.operator === ">" || c.operator === ">=") gt = higherGT(gt, c, options);
@@ -1560,8 +1551,7 @@ function getVersionFromNodeModules(packagePath, packageName) {
1560
1551
  function findClosestPackageJson(currentPath) {
1561
1552
  const resolvedPath = resolve(currentPath);
1562
1553
  const projectRoot = findProjectRoot();
1563
- const packageJsonPath = join(resolvedPath, "package.json");
1564
- if (existsSync(packageJsonPath)) return resolvedPath;
1554
+ if (existsSync(join(resolvedPath, "package.json"))) return resolvedPath;
1565
1555
  if (projectRoot && resolvedPath === projectRoot) return null;
1566
1556
  const parentPath = dirname(resolvedPath);
1567
1557
  if (parentPath === resolvedPath) return null;
@@ -1640,13 +1630,12 @@ const assessPrerequisitesBatch = (targetPaths, codemodPath, spinnies) => {
1640
1630
  const uniquePackages = packageRootToTargets.size;
1641
1631
  const passedCount = Array.from(results.values()).filter(Boolean).length;
1642
1632
  const failedCount = uniquePackages - passedCount;
1643
- if (failedTargetsNoPackage.length || !passedCount) {
1644
- if (!passedCount) spinnies.fail("prerequisite-check", { text: "All targets failed prerequisite checks" });
1645
- else {
1646
- spinnies.fail("prerequisite-check", { text: `No package.json found for ${failedTargetsNoPackage.length} target${failedTargetsNoPackage.length !== 1 ? "s" : ""} (searching upwards to project root).` });
1647
- failedTargetsNoPackage.forEach((t) => console.error(`- ${t}`));
1648
- }
1649
- } else {
1633
+ if (failedTargetsNoPackage.length || !passedCount) if (!passedCount) spinnies.fail("prerequisite-check", { text: "All targets failed prerequisite checks" });
1634
+ else {
1635
+ spinnies.fail("prerequisite-check", { text: `No package.json found for ${failedTargetsNoPackage.length} target${failedTargetsNoPackage.length !== 1 ? "s" : ""} (searching upwards to project root).` });
1636
+ failedTargetsNoPackage.forEach((t) => console.error(`- ${t}`));
1637
+ }
1638
+ else {
1650
1639
  const failedPart = failedCount > 0 ? `, \x1b[31m${failedCount} failed\x1b[0m` : "";
1651
1640
  spinnies.succeed("prerequisite-check", { text: `\x1b[0mChecked ${totalTargets} target${totalTargets !== 1 ? "s" : ""} across ${uniquePackages} package${uniquePackages !== 1 ? "s" : ""}:\x1b[0m \x1b[32m${passedCount} passed\x1b[0m${failedPart}` });
1652
1641
  }
@@ -1881,6 +1870,24 @@ async function getOptions({ transformFiles, transformerType, packages, root, pre
1881
1870
  };
1882
1871
  }
1883
1872
  //#endregion
1873
+ //#region src/controller/helpers/loadTransformModules.ts
1874
+ async function loadTransformModules(transformsDir) {
1875
+ let transformModules = {};
1876
+ const transformers = await promises.readdir(transformsDir);
1877
+ const transformFiles = await Promise.all(transformers.map(async (name) => {
1878
+ const transformModule = await import(path.join(transformsDir, name, "transformer.js"));
1879
+ transformModules = {
1880
+ ...transformModules,
1881
+ [name]: transformModule.default
1882
+ };
1883
+ return name;
1884
+ }));
1885
+ return {
1886
+ transformModules,
1887
+ transformFiles
1888
+ };
1889
+ }
1890
+ //#endregion
1884
1891
  //#region src/controller/helpers/reportManualReview.ts
1885
1892
  const REPORT_PATH = path.resolve(process.cwd(), "codemod-report.txt");
1886
1893
  const reportManualReview = async (filePath, message) => {
@@ -1939,6 +1946,13 @@ async function runTransformPrompts(codemodPath) {
1939
1946
  return answers;
1940
1947
  }
1941
1948
  //#endregion
1942
- export { assessPrerequisites as a, findProjectRoot as c, logToInquirer as i, getCodemodConfig as l, reportManualReview as n, assessPrerequisitesBatch as o, getOptions as r, findPackages as s, runTransformPrompts as t };
1949
+ //#region src/controller/helpers/validateClaudeConfig.ts
1950
+ const CLAUDE_SETTINGS_FILE = ".claude/settings.json";
1951
+ function validateClaudeConfig() {
1952
+ const claudeSettingsPath = path.resolve(process.env.HOME || "", CLAUDE_SETTINGS_FILE);
1953
+ if (!fs$1.existsSync(claudeSettingsPath)) throw new Error(`${CONSOLE_ICONS.error} Claude Code is not configured. Expected settings file at: ${claudeSettingsPath}. Please follow the setup instructions:\n https://transferwise.atlassian.net/wiki/spaces/GenAIP/pages/3709765113/Configuring+Claude+Code\n`);
1954
+ }
1955
+ //#endregion
1956
+ export { getOptions as a, assessPrerequisitesBatch as c, getCodemodConfig as d, loadTransformModules as i, findPackages as l, runTransformPrompts as n, logToInquirer as o, reportManualReview as r, assessPrerequisites as s, validateClaudeConfig as t, findProjectRoot as u };
1943
1957
 
1944
- //# sourceMappingURL=helpers-DzRXaYkW.js.map
1958
+ //# sourceMappingURL=helpers-9lRR7FPz.js.map