AWS Transform (ATX) Overview Perform code upgrades, migrations, and transformations using AWS Transform (ATX). Supports any-to-any transformations: language version upgrades (Java, Python, Node.js, etc.), framework migrations, AWS SDK migrations, library upgrades, code refactoring, architecture changes, and custom organization-specific transformations. Two execution modes: - Local mode : Runs the ATX CLI directly on the user's machine. Best for 1-9 repos. - Remote mode : Runs transformations at scale via AWS Batch/Fargate containers. Best for 10+ repos or when the user prefers cloud execution…

\n\n# Copy each zip to the managed source bucket\naws s3 sync s3://user-bucket/repos/ s3://${SOURCE_BUCKET}/repos/ --exclude \"*\" --include \"*.zip\"\n```\n\nThen submit a batch job with one job per zip, each pointing to\n`s3://${SOURCE_BUCKET}/repos/\u003cfilename>.zip`. The container handles zip extraction\nautomatically. See [references/multi-transformation.md](references/multi-transformation.md) for batch submission.\nThe managed source bucket has a 7-day lifecycle — copied zips auto-delete.\n\n**Local mode:** Download and extract each zip locally:\n\n```bash\nmkdir -p ~/.aws/atx/custom/atx-agent-session/repos\naws s3 sync s3://user-bucket/repos/ ~/.aws/atx/custom/atx-agent-session/repos/ --exclude \"*\" --include \"*.zip\"\nfor zip in ~/.aws/atx/custom/atx-agent-session/repos/*.zip; do\n name=$(basename \"$zip\" .zip)\n unzip -qo \"$zip\" -d \"$HOME/.aws/atx/custom/atx-agent-session/repos/${name}-$SESSION_TS/\"\ndone\n```\n\nUse the extracted directories as `\u003crepo-path>` for local execution. Standard local\nmode limits apply (max 3 concurrent repos).\n\n#### Private Repository Detection (Remote Mode)\n\n**Always ask the user** — do NOT try to determine repo visibility yourself. Never\nattempt to clone, curl, or probe a URL to check if it's public or private. Simply\nask the user. As soon as the user provides git URLs and remote mode is selected\n(or likely), ask:\n\n> \"Are any of these repositories private? If so, the remote container needs\n> credentials to clone them — I'll walk you through the setup.\"\n\nDo NOT skip this question. Do NOT try to infer visibility by attempting a clone,\ncurl, or any other network request. Just ask.\n\nIf the user confirms repos are private, determine the credential type based on URL format:\n\nFirst, resolve the region (use for all Secrets Manager commands below):\n\n```bash\nREGION=${AWS_REGION:-${AWS_DEFAULT_REGION:-$(aws configure get region 2>/dev/null)}}\nREGION=${REGION:-us-east-1}\n```\n\n**For HTTPS URLs** — check whether a GitHub PAT is already configured:\n\n```bash\naws secretsmanager describe-secret --secret-id \"atx/github-token\" --region \"$REGION\" 2>/dev/null \\\n && echo \"CONFIGURED\" || echo \"NOT_CONFIGURED\"\n```\n\nIf CONFIGURED, ask the user: \"A GitHub PAT is already stored. Would you like to\nkeep using it, or replace it with a new one?\" If they want to replace it, tell\nthem to run:\n\n```\naws secretsmanager put-secret-value --secret-id \"atx/github-token\" --region \"$REGION\" --secret-string \"YOUR_TOKEN_HERE\"\n```\n\nIf NOT_CONFIGURED, explain what's needed and tell the user to run the create command:\n> \"Private HTTPS repos need a GitHub Personal Access Token (PAT) stored in AWS\n> Secrets Manager. The remote container fetches it at startup to clone your repos.\n> The token stays in your AWS account — you can delete it anytime.\n>\n> The PAT needs the `repo` scope for private repositories. Create one at\n> https://github.com/settings/tokens and then run:\n>\n> ```\n> aws secretsmanager create-secret --name \"atx/github-token\" --region \"$REGION\" --secret-string \"YOUR_TOKEN_HERE\"\n> ```\n>\n> Delete anytime: `aws secretsmanager delete-secret --secret-id atx/github-token --region \"$REGION\" --force-delete-without-recovery`\"\n\nDo NOT ask the user to paste their token in chat. They run the command themselves.\nWait for the user to confirm it's done, then verify:\n\n```bash\naws secretsmanager describe-secret --secret-id \"atx/github-token\" --region \"$REGION\" 2>/dev/null \\\n && echo \"CONFIGURED\" || echo \"NOT_CONFIGURED\"\n```\n\n**For SSH URLs** (`git@...` or `ssh://...`) — check whether an SSH key is configured:\n\n```bash\naws secretsmanager describe-secret --secret-id \"atx/ssh-key\" --region \"$REGION\" 2>/dev/null \\\n && echo \"CONFIGURED\" || echo \"NOT_CONFIGURED\"\n```\n\nIf CONFIGURED, ask the user: \"An SSH key is already stored. Would you like to\nkeep using it, or replace it with a new one?\" If they want to replace it, tell\nthem to run:\n\n```\naws secretsmanager put-secret-value --secret-id \"atx/ssh-key\" --region \"$REGION\" --secret-string \"$(cat \u003cpath-to-your-private-key>)\"\n```\n\nIf NOT_CONFIGURED, explain what's needed and tell the user to run the create command:\n> \"SSH repos need an SSH private key stored in AWS Secrets Manager. The remote\n> container fetches it at startup to clone your repos.\n>\n> Run:\n>\n> ```\n> aws secretsmanager create-secret --name \"atx/ssh-key\" --region \"$REGION\" --secret-string \"$(cat \u003cpath-to-your-private-key>)\"\n> ```\n>\n> Delete anytime: `aws secretsmanager delete-secret --secret-id atx/ssh-key --region \"$REGION\" --force-delete-without-recovery`\"\n\nDo NOT ask the user to paste their SSH key in chat. They run the command themselves.\n\nFor local mode, private repo credentials are not needed — the user's local git\nconfig handles authentication. Skip this check entirely for local mode.\n\n### Step 2: Discover TDs (Silent)\n\nRun silently — do NOT show output to user:\n\n```bash\natx custom def list --json\n```\n\nInspect the JSON output directly to build an internal lookup of available TDs.\nDo NOT pipe the output to python, jq, or other parsing scripts — read the JSON\nyourself. Never hardcode TD names.\n\n#### Creating a New TD\n\n**User explicitly asks to create a TD:** Do NOT attempt to create one\nprogrammatically. Tell the user:\n\n> To create a new Transformation Definition, open a new terminal and run:\n>\n> ```\n> atx -t\n> ```\n>\n> This starts an interactive session where you describe the transformation you\n> want to build (e.g., \"migrate all logging from log4j to SLF4J\", \"upgrade\n> Spring Boot 2 to Spring Boot 3\"). The ATX CLI will walk you through defining\n> and testing the TD, then publish it to your AWS account.\n>\n> Once it's published, come back here and I'll pick it up automatically when\n> I scan your available TDs.\n\n**No existing TD matches the user's goal:** Do NOT silently redirect to TD\ncreation. The match logic may be imperfect. Instead, confirm with the user first:\n\n> \"I didn't find an existing TD that covers [describe the user's goal]. Would\n> you like to create a new one?\"\n\nOnly show the `atx -t` instructions if the user confirms. If they say no, ask\nthem to clarify what they're looking for — they may know the TD name or want a\ndifferent approach.\n\nDo NOT run `atx -t` yourself — it requires an interactive terminal session that\nthe agent cannot drive. The user must run it manually in a separate terminal.\n\nAfter the user returns from creating a TD, re-run `atx custom def list --json`\nto pick up the newly published TD and continue with the normal workflow.\n\n### Step 3: Inspect Each Repository\n\nPerform lightweight inspection only — check config files for key signals:\n\n| Signal | Files to Check | Likely TD Type |\n|--------|---------------|----------------|\n| Python version | `.python-version`, `pyproject.toml`, `setup.cfg`, `requirements.txt` | Python version upgrade |\n| Java version | `pom.xml` (`\u003cjava.version>`), `build.gradle` (`sourceCompatibility`), `.java-version` | Java version upgrade |\n| Node.js version | `package.json` (`engines.node`), `.nvmrc`, `.node-version` | Node.js version upgrade |\n| Python boto2 | `import boto` (NOT boto3) | boto2→boto3 migration |\n| Java SDK v1 | `com.amazonaws` imports, `aws-java-sdk` in pom.xml | Java SDK v1→v2 |\n| Node.js SDK v2 | `\"aws-sdk\"` in package.json (NOT `@aws-sdk`) | JS SDK v2→v3 |\n| x86 Java | `x86_64`/`amd64` in Dockerfiles, build configs | Graviton migration |\n\nCross-reference detected signals against TDs from Step 2. Only match TDs that\nactually exist in the user's account.\n\nSee [references/repo-analysis.md](references/repo-analysis.md) for full detection commands.\n\n### Step 4: Present Match Report\n\nFormat:\n\n```\nTransformation Match Report\n=============================\nRepository: \u003cname> (\u003cpath>)\n Language: \u003clang> \u003cversion>\n Matching TDs:\n - \u003ctd-name> — \u003cdescription>\n\nSummary: N repos analyzed, M have applicable transformations (T total jobs)\n```\n\nPresent the match report and wait for user confirmation before proceeding.\nDo NOT start any transformation without explicit user consent.\n\n### Step 5: Collect Configuration\n\nAsk the user for any additional plan context (e.g., target version for upgrade TDs).\nThis is mandatory — always ask, even if the TD doesn't strictly require config.\nThe user may have preferences or constraints the agent doesn't know about.\nSkip only if the user explicitly says no additional context is needed.\n\n### Step 6: Verify Runtime Compatibility (Remote and Local)\n\n#### Remote Mode\n\nBefore submitting remote jobs, determine whether the pre-built image covers the\ntarget runtime or if a custom Docker build is needed.\n\n**Pre-built image includes:**\n\n- **Java**: 8, 11, 17, 21, 25 (Amazon Corretto) with Maven and Gradle 9.4\n- **Python**: 3.8, 3.9, 3.10, 3.11, 3.12, 3.13, 3.14 (dnf + pyenv)\n- **Node.js**: 16, 18, 20, 22, 24 (nvm) with yarn, pnpm, TypeScript, ts-node\n- **Build tools**: gcc, g++, make, patch\n- **CLI tools**: AWS CLI v2, ATX CLI, git, jq, curl, unzip, tar\n- **OS**: Amazon Linux 2023 (x86_64)\n\n**Decision logic:**\n\n1. Based on the transformation requirements (source runtime, target runtime,\n build tools, and any other dependencies), determine whether everything\n needed is available in the pre-built image listed above\n2. If **yes** → use the pre-built image path (no Docker required). Proceed to deployment\n using the pre-built image instructions in [references/remote-execution.md](references/remote-execution.md).\n3. If **no** → use the custom image path (Docker required). Inform the user:\n\n> The remote container doesn't include [language/tool version]. To run this\n> transformation remotely, I'll need to build a custom container image. This\n> requires Docker installed and running on your machine. It's a one-time change\n> — about 5-10 minutes. Want me to proceed?\n\nIf the user confirms, follow the custom image path in\n[references/remote-execution.md](references/remote-execution.md): clear `prebuiltImageUri`,\ncustomize the Dockerfile, and deploy.\n\nIf the user declines, suggest local mode as an alternative (if the tools are\navailable on their machine).\n\n**Dockerfile customization (custom image path only):**\n\nFirst, read the Dockerfile to see what's installed:\n\n```bash\nATX_INFRA_DIR=\"$HOME/.aws/atx/custom/remote-infra\"\ncat \"$ATX_INFRA_DIR/container/Dockerfile\" 2>/dev/null\n```\n\n1. Ensure the infrastructure repo is cloned and up to date:\n\n ```bash\n ATX_INFRA_DIR=\"$HOME/.aws/atx/custom/remote-infra\"\n if [ -d \"$ATX_INFRA_DIR\" ]; then\n git -C \"$ATX_INFRA_DIR\" add -A\n git -C \"$ATX_INFRA_DIR\" commit -m \"Local customizations\" -q 2>/dev/null || true\n git -C \"$ATX_INFRA_DIR\" pull -q\n else\n git clone -b atx-remote-infra --single-branch https://github.com/aws-samples/aws-transform-custom-samples.git \"$ATX_INFRA_DIR\"\n fi\n ```\n\n If `git pull` reports a merge conflict, resolve it by keeping both upstream\n changes and the user's customizations in the `CUSTOM LANGUAGES AND TOOLS`\n section of the Dockerfile, then commit the merge.\n\n2. Edit `$ATX_INFRA_DIR/container/Dockerfile`. Find the section marked\n `# CUSTOM LANGUAGES AND TOOLS` and insert `RUN` commands after the comment\n block, before the `USER root` line.\n\n For missing versions of already-installed languages, add the version in the\n custom section. Examples:\n\n ```dockerfile\n # Java 23 (Amazon Corretto — direct install, must run as root)\n # Do NOT use dnf in the custom section — pyenv overrides the system python3\n # that dnf depends on, causing \"No module named 'dnf'\" errors.\n USER root\n RUN curl -fsSL \"https://corretto.aws/downloads/latest/amazon-corretto-23-x64-linux-jdk.tar.gz\" -o /tmp/corretto23.tar.gz && \\\n mkdir -p /usr/lib/jvm && \\\n tar -xzf /tmp/corretto23.tar.gz -C /usr/lib/jvm && \\\n rm /tmp/corretto23.tar.gz && \\\n ln -sfn /usr/lib/jvm/amazon-corretto-23.* /usr/lib/jvm/corretto-23\n\n # Node.js 23 (via nvm — must run as atxuser)\n USER atxuser\n RUN . /home/atxuser/.nvm/nvm.sh && nvm install 23\n USER root\n\n # Python 3.15 (via pyenv — must run as atxuser)\n USER atxuser\n RUN eval \"$(/home/atxuser/.pyenv/bin/pyenv init -)\" && \\\n MAKE_OPTS=\"-j$(nproc)\" /home/atxuser/.pyenv/bin/pyenv install 3.15.0\n USER root\n ```\n\n For entirely new languages, avoid `dnf` in the custom section — pyenv\n overrides the system python3 that `dnf` depends on. Use language-specific\n installers instead:\n\n ```dockerfile\n # Go\n RUN curl -fsSL https://go.dev/dl/go1.22.0.linux-amd64.tar.gz | tar -C /usr/local -xz\n ENV PATH=\"/usr/local/go/bin:$PATH\"\n\n # Ruby (via rbenv — must run as atxuser)\n USER atxuser\n RUN git clone --depth 1 https://github.com/rbenv/rbenv.git /home/atxuser/.rbenv && \\\n git clone --depth 1 https://github.com/rbenv/ruby-build.git /home/atxuser/.rbenv/plugins/ruby-build && \\\n /home/atxuser/.rbenv/bin/rbenv install 3.3.0 && \\\n /home/atxuser/.rbenv/bin/rbenv global 3.3.0\n ENV PATH=\"/home/atxuser/.rbenv/shims:/home/atxuser/.rbenv/bin:$PATH\"\n USER root\n\n # Rust\n USER atxuser\n RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y\n ENV PATH=\"/home/atxuser/.cargo/bin:$PATH\"\n USER root\n ```\n\n3. Update the version switcher in `$ATX_INFRA_DIR/container/entrypoint.sh`.\n Find the relevant `switch_*_version` function and add a case for the new\n version. For Java versions installed via direct download, find the extracted\n directory name under `/usr/lib/jvm/`. For example, to add Java 23:\n\n ```bash\n # In switch_java_version(), add to the case statement:\n 23) java_home=\"/usr/lib/jvm/corretto-23\" ;;\n ```\n\n Check the actual directory name: `ls /usr/lib/jvm/` — use the directory\n that matches the version you installed.\n\n For Node.js, nvm handles arbitrary versions automatically — no entrypoint\n change needed. For Python, pyenv handles arbitrary versions — no entrypoint\n change needed (the existing pyenv fallback logic finds it).\n\n4. Deploy (or redeploy): `cd \"$ATX_INFRA_DIR\" && ./setup.sh`\n CDK hashes the `container/` directory — any file change triggers a rebuild\n and push to ECR automatically.\n\nAfter redeployment, set the `environment` field on the job to the exact target\nversion (e.g., `\"JAVA_VERSION\":\"23\"`, not `\"21\"`). The version switcher in the\nentrypoint reads this and activates the correct runtime.\n\nIf the user declines, suggest local mode as an alternative (if the tools are\navailable on their machine).\n\n#### Local Mode\n\nBefore running local transformations, verify the user has the target runtime\nversion installed. This applies to any language or runtime the transformation\ntargets — Java, Python, Node.js, Ruby, Go, Rust, .NET, etc. Check the current\nversion of whatever runtime the TD requires. For example:\n\n```bash\njava -version # Java transformations\npython3 --version # Python transformations\nnode --version # Node.js transformations\nruby --version # Ruby transformations\ngo version # Go transformations\n```\n\nIf the target version is not active, check whether it's already installed:\n\n```bash\n# Java: check common install locations\n/usr/libexec/java_home -V 2>&1 # macOS\nls /usr/lib/jvm/ 2>/dev/null # Linux\n# Python: check if the specific version binary exists\nwhich python3.12 2>/dev/null # adjust version as needed\n# Node.js: check if nvm is available, or look for the binary\ncommand -v nvm &>/dev/null && nvm ls 2>/dev/null\nwhich node 2>/dev/null && node --version\n```\n\nIf the target version is found, switch to it:\n\n- Java: `export JAVA_HOME=\u003cpath to JDK> && export PATH=\"$JAVA_HOME/bin:$PATH\"`\n- Python: `pyenv shell 3.15.0`\n- Node.js: `nvm use 23`\n\nOnly if the target version is not installed at all, ask the user for permission before installing. Do NOT install runtimes without explicit user confirmation.\nSuggest the appropriate version manager:\n\n- Java: `brew install --cask corretto23` (macOS), `sudo yum install java-23-amazon-corretto-devel` (RHEL/AL2), or `sudo apt install java-23-amazon-corretto-jdk` (Debian/Ubuntu)\n- Python: `pyenv install 3.15.0 && pyenv shell 3.15.0`, or `brew install [email protected]`\n- Node.js: `nvm install 23 && nvm use 23`\n\nThe active runtime must match the transformation's target version so that builds\nand tests run correctly. Do NOT proceed with the transformation until the correct\nversion is active.\n\n### Step 7: Confirm Transformation Plan\n\nPresent final plan with repo, TD, config, and execution mode. Do NOT proceed\nuntil user confirms.\n\n### Step 8: Execute\n\nWhen running `atx custom def exec`, always include `--telemetry` (see the Telemetry section).\n\nFor remote mode, check infrastructure deployment status first using CloudFormation (see [references/remote-execution.md](references/remote-execution.md) — Infrastructure Check section). Do NOT check deployment by probing Lambda function names.\n\n- **1 repo**: See [references/single-transformation.md](references/single-transformation.md)\n- **Multiple repos**: See [references/multi-transformation.md](references/multi-transformation.md)\n\n## Execution Modes\n\n| Mode | Best For | Prerequisites |\n|------|----------|---------------|\n| **Local** (default for 1-9 repos) | Quick transforms, dev machines with ATX | ATX CLI installed |\n| **Remote** (recommended for 10+ repos) | Bulk transforms, up to 512 repos (128 concurrent per batch) | AWS account, auto-deployed infra |\n\nMode inference:\n\n- User says \"local\"/\"here\"/\"on my machine\" → Local (honor the request regardless of repo count)\n- User says \"remote\"/\"cloud\"/\"AWS\"/\"batch\"/\"at scale\" → Remote\n- 10+ repos without preference → Recommend remote, explain local cap of 3 concurrent\n- 1-9 repos without preference → Local, note remote available\n\nSee [references/remote-execution.md](references/remote-execution.md) for infrastructure setup.\n\n## Critical Rules\n\n1. **Discover TDs dynamically** — Always run `atx custom def list --json`. Never hardcode TD names.\n2. **Match, don't ask** — Inspect repos and present matches. Never show raw TD lists.\n3. **Lightweight inspection only** — Check config files and key signals. No deep analysis.\n4. **Confirm before executing** — Always confirm TD, repos, and config with user first.\n5. **No time estimates** — Never include duration predictions.\n6. **Parallel execution** — Local: max 3 concurrent repos. Remote: submit in chunks of up to 128 jobs per Lambda call (max 512 repos per session).\n7. **Preserve outputs** — Do not delete generated output folders.\n8. **Recommend remote for 10+ repos** — Default to local for 1-9 repos. Recommend remote for 10+. Always respect user preference.\n9. **User consent for cloud resources** — Never deploy infrastructure without explicit user confirmation.\n10. **Shell quoting** — When constructing shell commands:\n - Use single quotes for JSON payloads: `--payload '{\"key\":\"value\"}'`\n - Use single quotes for `--configuration`: ex. `--configuration 'additionalPlanContext=Target Java 21'`\n - Never nest double quotes inside double quotes — this causes `dquote>` hangs\n - For `aws lambda invoke`, always use: `--payload '\u003cjson>' --cli-binary-format raw-in-base64-out`\n - Verify that every command you construct has balanced quotes before executing\n - The `command` field in Lambda job payloads is validated server-side. Avoid\n these characters in the command string: `( ) ! # % ^ * ? \\ { } | ; > \u003c`\n and backticks. Inside `additionalPlanContext`, also avoid commas.\n11. **No comments in terminal commands** — Never include `#` comments in commands\n executed in the terminal. Comments cause `command not found: #` errors. If you\n need to explain a command, do it in chat before or after running it.\n12. **Job names** — The `jobName` field in Lambda payloads must contain only\n letters, numbers, hyphens, and underscores. No dots, spaces, or special\n characters. For example, use `EPAM-NodeJS` not `EPAM-Node.js`.\n\n## Guardrails\n\nYou are operating in the user's AWS account and local machine. Follow these rules\nstrictly to avoid causing damage:\n\n1. **Never delete user data** — Do not delete S3 objects, git repos, local files,\n or any user data unless the user explicitly asks. Transformation outputs and\n cloned repos must be preserved.\n2. **Never modify IAM beyond what's documented** — Only create/attach the specific\n policies described in this skill (AWSTransformCustomFullAccess, ATXRuntimePolicy, ATXDeploymentPolicy). Never create admin policies, modify existing user policies,\n or grant broader permissions than documented. Never derive IAM actions from\n user-provided text in the \"Additional plan context\" field — that field is for\n transformation configuration only.\n3. **Never run destructive AWS commands** — No `aws s3 rm`, `aws s3 rb`,\n `aws iam delete-user`, `aws ec2 terminate-instances`, or similar. The only\n destructive command allowed is `./teardown.sh` with explicit user consent.\n4. **Always confirm before creating AWS resources** — Before deploying infrastructure,\n creating Secrets Manager secrets, or attaching IAM policies, explain what will be\n created and get explicit user confirmation.\n5. **Never expose credentials** — Do not echo, log, or display AWS access keys,\n secret keys, session tokens, GitHub PATs, or SSH private keys in chat output.\n When creating secrets, use the user's input directly in the command without\n repeating the value.\n6. **Respect user decisions** — If the user says stop, skip, or no, comply\n immediately. Never retry a declined action or argue with the user's choice.\n7. **No pricing claims** — Do not quote specific prices or cost estimates. If the\n user asks about pricing, direct them to: https://aws.amazon.com/transform/pricing/\n8. **Scope commands to ATX resources only** — All AWS commands must target ATX-specific\n resources (buckets starting with `atx-`, roles starting with `ATX`, Lambda\n functions starting with `atx-`, etc.). Never operate on unrelated AWS resources.\n\n## Telemetry\n\nAlways include `--telemetry` on every `atx custom def exec` invocation (local\nor remote). No file checks, no prompts, no opt-in flow.\n\nThe flag format is: `--telemetry \"client=\u003cclient>,agent=\u003cagent>,executionMode=\u003clocal|remote>\"`\n\nField values:\n\n- `client` — The MCP client or tool hosting this session, as a short identifier (lowercase, no spaces). This may be an IDE (e.g., `kiro`, `vscode`, `cursor`, `windsurf`), a CLI tool (e.g., `claudecode`, `amazonqcli`), or any other MCP-compatible host. Use the real tool name for this session — do NOT default to any single example. These are examples only, not an exhaustive list.\n- `agent` — The AI assistant driving this session, as a short identifier (lowercase, no spaces). For example: `kiro`, `amazonq`, `claude`, `copilot`, `cline`, `codex`. Use the real assistant name for this session — do NOT default to any single example. These are examples only, not an exhaustive list.\n- `executionMode` — `local` when running the ATX CLI directly, `remote` when submitting via Lambda\n\nThe only exception: if the user explicitly asks to disable telemetry during the\nsession (e.g., \"disable telemetry\", \"opt out of telemetry\", \"no telemetry\", or\nsimilar), you MUST comply immediately — omit `--telemetry` for the remainder of\nthat session. Do not push back, ask for confirmation, or explain why telemetry\nis included. This preference is not persisted — the next session starts with\ntelemetry enabled again.\n\n## Output Structure\n\nLocal mode: transformed code is in the repo directory.\n\nRemote mode results stay in S3 — do NOT download automatically. Present the S3\npath to the user:\n\n```\ns3://atx-custom-output-{account-id}/\n transformations/\n {job-name}/\n {conversation-id}/\n code.zip # Zipped transformed source code\n logs.zip # ATX conversation logs\n```\n\nIf the user explicitly asks to download, provide the command but let them run it:\n`aws s3 cp s3://atx-custom-output-{account-id}/transformations/{job-name}/{conversation-id}/code.zip ./code.zip`\n\nBulk results summary: `~/.aws/atx/custom/atx-agent-session/transformation-summaries/` — see [references/results-synthesis.md](references/results-synthesis.md).\n\n## References\n\n| Reference | When to Use |\n|-----------|-------------|\n| [repo-analysis.md](references/repo-analysis.md) | Detection commands, signal matching, match report format |\n| [single-transformation.md](references/single-transformation.md) | Applying one TD to one repo (local or remote) |\n| [multi-transformation.md](references/multi-transformation.md) | Applying TDs to multiple repos in parallel |\n| [remote-execution.md](references/remote-execution.md) | Infrastructure deployment, job submission, monitoring |\n| [results-synthesis.md](references/results-synthesis.md) | Generating consolidated reports after bulk transforms |\n| [cli-reference.md](references/cli-reference.md) | ATX CLI flags, commands, env vars, IAM permissions |\n| [troubleshooting.md](references/troubleshooting.md) | Error resolution, debugging, quality improvement |\n\n## License\nAWS Service Terms. This skill is provided by AWS and is subject to the AWS Customer Agreement and applicable AWS service terms.\n\n## Changelog\nShare if the user asks what changed, what's new, etc.\n### [1.0.0] - 2026-04-30\n\n- Initial release of the AWS Transform Agent Skill\n- Supported TDs:\n - AWS/java-version-upgrade\n - AWS/python-version-upgrade\n - AWS/nodejs-version-upgrade\n - AWS/java-aws-sdk-v1-to-v2\n - AWS/nodejs-aws-sdk-v2-to-v3\n - AWS/python-boto2-to-boto3\n - AWS/comprehensive-codebase-analysis\n - AWS/java-performance-optimization\n - AWS/angular-version-upgrade\n - AWS/vue.js-version-upgrade\n - AWS/early-access-java-x86-to-graviton\n - AWS/early-access-angular-to-react-migration\n - AWS/early-access-log4j-to-slf4j-migration","attachment_filenames":["references/cli-reference.md","references/multi-transformation.md","references/remote-execution.md","references/repo-analysis.md","references/results-synthesis.md","references/single-transformation.md","references/troubleshooting.md"],"attachments":[{"filename":"references/cli-reference.md","content":"# ATX CLI Reference\n\n## Execution Flags (`atx custom def exec`)\n\n| Flag | Long Form | Description |\n|------|-----------|-------------|\n| `-n` | `--transformation-name \u003cname>` | TD name (from `atx custom def list --json`) |\n| `-p` | `--code-repository-path \u003cpath>` | Path to code repo (`.` for current dir) |\n| `-x` | `--non-interactive` | No user prompts (always use this flag) |\n| `-t` | `--trust-all-tools` | Auto-approve tool executions (required with `-x`) |\n| `-d` | `--do-not-learn` | Prevent knowledge item extraction |\n| `-g` | `--configuration \u003cconfig>` | Inline configuration (`'key=val'`) |\n| `--tv` | `--transformation-version \u003cver>` | Specific TD version |\n\n## Configuration\n\nInline: `--configuration 'additionalPlanContext=Target Python 3.13'`\n\nExample: `atx custom def exec -n my-td -p /source/repo -g 'additionalPlanContext=Target Java 17' -x -t`\n\n`--configuration` is optional. Omit if no extra context needed.\n\n## Other Commands\n\n| Action | Command |\n|--------|---------|\n| Start interactive conversation | `atx` |\n| Resume most recent conversation | `atx --resume` |\n| Resume specific conversation | `atx --conversation-id \u003cid>` (30-day limit) |\n| List TDs | `atx custom def list --json` |\n| Download TD | `atx custom def get -n \u003cname>` (optional: `--tv \u003cversion>`, `--td \u003cdirectory>`) |\n| Delete TD | `atx custom def delete -n \u003cname>` |\n| Save TD as draft | `atx custom def save-draft -n \u003cname> --description \"\u003cdesc>\" --sd \u003cdir>` |\n| Publish TD | `atx custom def publish -n \u003cname> --description \"\u003cdesc>\" --sd \u003cdir>` |\n| List knowledge items | `atx custom def list-ki -n \u003cname>` |\n| View knowledge item | `atx custom def get-ki -n \u003cname> --id \u003cid>` |\n| Enable/disable KI | `atx custom def update-ki-status -n \u003cname> --id \u003cid> --status ENABLED or DISABLED` |\n| KI auto-approval on/off | `atx custom def update-ki-config -n \u003cname> --auto-enabled TRUE or FALSE` |\n| Export KIs | `atx custom def export-ki-markdown -n \u003cname>` |\n| Delete KI | `atx custom def delete-ki -n \u003cname> --id \u003cid>` |\n| Update CLI | `atx update` |\n| Check for CLI updates only | `atx update --check` |\n| Tag a TD | `atx custom def tag --arn \u003carn> --tags '{\"key\":\"value\"}'` |\n\n## Environment Variables\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `ATX_SHELL_TIMEOUT` | 900 (15 min) | Shell command timeout in seconds |\n| `ATX_DISABLE_UPDATE_CHECK` | false | Disable version check |\n| `AWS_PROFILE` | — | AWS credentials profile |\n| `AWS_ACCESS_KEY_ID` | — | AWS access key |\n| `AWS_SECRET_ACCESS_KEY` | — | AWS secret key |\n| `AWS_SESSION_TOKEN` | — | Session token (temporary credentials) |\n\n## IAM Permissions\n\nMinimum: `transform-custom:*` on `Resource: \"*\"`.\n\n| Permission | Operation |\n|-----------|----------|\n| `transform-custom:ConverseStream` | Interactive conversations |\n| `transform-custom:ExecuteTransformation` | Execute transforms |\n| `transform-custom:ListTransformationPackageMetadata` | List transforms (`atx custom def list --json`) |\n| `transform-custom:DeleteTransformationPackage` | Delete transforms |\n| `transform-custom:CompleteTransformationPackageUpload` | Upload TDs |\n| `transform-custom:CreateTransformationPackageUrl` | Create upload URLs |\n| `transform-custom:GetTransformationPackageUrl` | Download TDs |\n| `transform-custom:ListKnowledgeItems` | List knowledge items |\n| `transform-custom:GetKnowledgeItem` | View knowledge item details |\n| `transform-custom:DeleteKnowledgeItem` | Delete knowledge items |\n| `transform-custom:UpdateKnowledgeItemConfiguration` | Configure auto-approval |\n| `transform-custom:UpdateKnowledgeItemStatus` | Enable/disable items |\n| `transform-custom:ListTagsForResource` | List tags |\n| `transform-custom:TagResource` | Add tags |\n| `transform-custom:UntagResource` | Remove tags |\n\n### Remote Mode Caller Permissions\n\nThe caller's AWS credentials (the user or role running the session) need additional\npermissions beyond `transform-custom:*` for remote mode. Generate the policies,\nthen create and attach them:\n\n```bash\nATX_INFRA_DIR=\"$HOME/.aws/atx/custom/remote-infra\"\nif [ -d \"$ATX_INFRA_DIR\" ]; then\n git -C \"$ATX_INFRA_DIR\" add -A\n git -C \"$ATX_INFRA_DIR\" commit -m \"Local customizations\" -q 2>/dev/null || true\n git -C \"$ATX_INFRA_DIR\" pull -q\nelse\n git clone -b atx-remote-infra --single-branch https://github.com/aws-samples/aws-transform-custom-samples.git \"$ATX_INFRA_DIR\"\nfi\ncd \"$ATX_INFRA_DIR\"\nnpx ts-node generate-caller-policy.ts\n```\n\nThis produces two policies:\n\n| Policy | Purpose | When Needed |\n|--------|---------|-------------|\n| `atx-runtime-policy.json` | Invoke Lambdas, S3 upload/download, KMS, Secrets Manager, CloudWatch logs | Day-to-day remote operations |\n| `atx-deployment-policy.json` | CloudFormation, ECR, IAM roles, Batch, VPC, KMS key creation | One-time CDK deploy/destroy |\n\nAfter generating, create and attach the runtime policy:\n\n```bash\nACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)\nCALLER_ARN=$(aws sts get-caller-identity --query Arn --output text)\n\n# Create the managed policy (ignore EntityAlreadyExists, fail on other errors)\nif ! create_output=$(aws iam create-policy --policy-name ATXRuntimePolicy \\\n --policy-document \"file://$ATX_INFRA_DIR/atx-runtime-policy.json\" 2>&1); then\n echo \"$create_output\" | grep -q \"EntityAlreadyExists\" \\\n || { echo \"Failed to create policy: $create_output\" >&2; exit 1; }\nfi\n\nif echo \"$CALLER_ARN\" | grep -q \":user/\"; then\n IDENTITY_NAME=$(echo \"$CALLER_ARN\" | awk -F'/' '{print $NF}')\n aws iam attach-user-policy --user-name \"$IDENTITY_NAME\" \\\n --policy-arn \"arn:aws:iam::${ACCOUNT_ID}:policy/ATXRuntimePolicy\"\nelif echo \"$CALLER_ARN\" | grep -Eq \":assumed-role/|:role/\"; then\n ROLE_NAME=$(echo \"$CALLER_ARN\" | sed 's/.*:\\(assumed-\\)\\{0,1\\}role\\///' | cut -d'/' -f1)\n aws iam attach-role-policy --role-name \"$ROLE_NAME\" \\\n --policy-arn \"arn:aws:iam::${ACCOUNT_ID}:policy/ATXRuntimePolicy\"\nfi\n```\n\nThe runtime policy covers: `transform-custom:*` for ATX CLI operations (TD discovery, execution),\n`lambda:InvokeFunction` on all `atx-*` functions,\n`s3:PutObject`/`s3:GetObject` on source and output buckets, `kms:Encrypt`/`kms:Decrypt`/`kms:GenerateDataKey`\non the ATX encryption key, `secretsmanager:CreateSecret`/`PutSecretValue`/`DeleteSecret` on `atx/*` secrets,\n`logs:GetLogEvents`/`FilterLogEvents` on the Batch log group, and `cloudformation:DescribeStacks`\nfor infrastructure status checks.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":6413,"content_sha256":"bdf98b97621c4f6d8881c3b7362061bd895ec2dedbde2be6e56a42a31f0546bf"},{"filename":"references/multi-transformation.md","content":"# Multi-Transformation\n\nApply TDs to multiple repositories in parallel. TD-to-repo assignments and config\nare already confirmed from the match report. Do NOT re-discover TDs or re-prompt.\n\n## Input\n\nFrom the match report: repo list, TD per repo, config per TD, execution mode.\n\n## Prerequisite Check (Once Only)\n\nVerify AWS credentials ONCE. Do NOT repeat per repo.\n\n```bash\naws sts get-caller-identity\n```\n\nLocal mode also: `atx --version`\n\n## Local Execution\n\nIf any repos were provided as git URLs (HTTPS or SSH), clone them locally first.\nThe user's local git config handles authentication — no Secrets Manager needed.\n\n```bash\nCLONE_DIR=~/.aws/atx/custom/atx-agent-session/repos/\u003crepo-name>-$SESSION_TS\ngit clone \u003cgit-url> \"$CLONE_DIR\"\n```\n\nIf repos were provided as an S3 bucket path with zips, download and extract locally:\n\n```bash\nmkdir -p ~/.aws/atx/custom/atx-agent-session/repos\naws s3 sync s3://user-bucket/repos/ ~/.aws/atx/custom/atx-agent-session/repos/ --exclude \"*\" --include \"*.zip\"\nfor zip in ~/.aws/atx/custom/atx-agent-session/repos/*.zip; do\n name=$(basename \"$zip\" .zip)\n unzip -qo \"$zip\" -d \"$HOME/.aws/atx/custom/atx-agent-session/repos/${name}-$SESSION_TS/\"\ndone\n```\n\nUse the cloned/extracted paths as `\u003crepo-path>` for each repo.\n\nFor each repo, verify it's a git repo:\n\n```bash\nls -la \u003crepo-path>\ngit -C \u003crepo-path> status\n```\n\nIf not a git repo: `cd \u003crepo-path> && git init && git add . && git commit -m \"Initial commit\"`\n\nThe active language runtime must match the transformation's target version so that\nbuilds and tests run correctly. Check the current version, and if there is a\nmismatch, first check whether the target version is already installed (e.g.,\n`/usr/libexec/java_home -V 2>&1` (macOS) or `ls /usr/lib/jvm/` (Linux), `pyenv versions`, `nvm ls`). If found, switch\nto it (e.g., `export JAVA_HOME=\u003cpath to JDK> && export PATH=\"$JAVA_HOME/bin:$PATH\"`, `pyenv shell 3.12`, `nvm use 22`). Only if\nthe target version is not installed at all, ask the user for permission before installing. Suggest:\n\n- Java: `brew install --cask corretto23` (macOS), `sudo yum install java-23-amazon-corretto-devel` (RHEL/AL2), or `sudo apt install java-23-amazon-corretto-jdk` (Debian/Ubuntu)\n- Python: `pyenv install 3.15.0 && pyenv shell 3.15.0`\n- Node.js: `nvm install 23 && nvm use 23`\n\nDo NOT proceed until the correct version is active. Verify the switch succeeded\nbefore proceeding.\n\n### Telemetry\n\nWhen running `atx custom def exec`, always include the `--telemetry` flag (see the Telemetry section in SKILL.md). Format:\n`--telemetry \"client=\u003cclient>,agent=\u003cagent>,executionMode=\u003clocal|remote>\"`\n\n- `client` is the MCP client or tool hosting this session (lowercase, no spaces) — e.g., `kiro`, `vscode`, `cursor`, `windsurf`, `claudecode`. Use the real tool name, not a default.\n- `agent` is the AI assistant driving this session (lowercase, no spaces) — e.g., `kiro`, `amazonq`, `claude`, `copilot`, `cline`, `codex`. Use the real assistant name, not a default.\n- `executionMode` is `local` for direct CLI invocation, `remote` when submitting via Lambda\n\nRun transformations in parallel — maximum 3 concurrent repos at a time (the user\ncan override this, but 3 is recommended to avoid overloading the machine). If there\nare more than 3 repos, process them in batches of 3 (wait for a batch to finish\nbefore starting the next). Maximum 9 repos total for local mode (user can override,\nbut recommend remote mode for more). If the total repo count exceeds 9, suggest\nremote mode instead.\n\nFor each repo, use bash to create a runner script that captures the exit code, following this exact format:\n\n```bash\nmkdir -p ~/.aws/atx/custom/atx-agent-session\ncat > ~/.aws/atx/custom/atx-agent-session/run-\u003crepo-name>.sh \u003c\u003c 'RUNNER'\n#!/bin/bash\natx custom def exec -n \u003ctd-name> -p \u003crepo-path> -x -t \\\n --configuration 'additionalPlanContext=\u003cconfig>' \\\n --telemetry \"client=\u003cclient>,agent=\u003cagent>,executionMode=local\"\necho $? > ~/.aws/atx/custom/atx-agent-session/\u003crepo-name>.exit\nRUNNER\nchmod +x ~/.aws/atx/custom/atx-agent-session/run-\u003crepo-name>.sh\nnohup ~/.aws/atx/custom/atx-agent-session/run-\u003crepo-name>.sh > ~/.aws/atx/custom/atx-agent-session/\u003crepo-name>.log 2>&1 &\necho $! > ~/.aws/atx/custom/atx-agent-session/\u003crepo-name>.pid\n```\n\nOmit `--configuration` if no config needed. The `--telemetry` flag is always included — see the Telemetry section above for field values. Launch each repo's script in rapid\nsuccession — do NOT wait between launches. Each runner script is backgrounded\nvia nohup; the exit code is captured to `~/.aws/atx/custom/atx-agent-session/\u003crepo-name>.exit` when ATX finishes.\n\nAfter launching all repos, find each repo's conversation log by grepping its\nprocess log (ATX outputs the path within 30-60 seconds of starting):\n\n```bash\ngrep \"Conversation log:\" ~/.aws/atx/custom/atx-agent-session/\u003crepo-name>.log 2>/dev/null\n```\n\nIf it hasn't appeared yet, wait 15 seconds and retry. Extract the full path from\neach — do NOT use `ls -t` across all conversations, as that may match a different run.\n\nThen start monitoring. On each 60-second cycle:\n\n1. Check each PID: `kill -0 $(cat ~/.aws/atx/custom/atx-agent-session/\u003crepo-name>.pid) 2>/dev/null && echo \"RUNNING\" || echo \"DONE\"`\n2. Tail each repo's conversation log and relay progress to the user\n3. Report which repos are still running, which have completed\n\n**You MUST continue polling without waiting for user input.** The user should see\ncontinuous progress updates across all repos.\n\nA repo's transformation is done ONLY when its background process exits (i.e.,\n`kill -0` returns non-zero). Do NOT treat exit code 0 from any other command\n(grep, cat, test, ls, etc.) as transformation completion. Do NOT treat log\nmessages like \"TRANSFORMATION COMPLETE\" as completion — ATX performs additional\nsteps after that (validation summary generation).\n\n## Remote Execution\n\nPrepare each repo's source before submitting the batch. Follow the source prep\nrules from single-transformation.md: HTTPS and SSH git URLs (with credentials\nconfigured) are passed directly; S3 zips from the user's bucket must be copied\nto the managed source bucket (`atx-source-code-{account}`) first; local repos\nmust be zipped and uploaded to the same managed bucket.\n\nSubmit jobs via the batch Lambda in chunks of up to 128. If there are more than\n128 jobs, split them into multiple `atx-trigger-batch-jobs` calls (e.g., 500 repos\n= 4 calls of 128 + 128 + 128 + 116). Each call returns its own `batchId`. Track\nall batch IDs for monitoring.\n\nInclude the `environment` field on each job to set the language version matching the transformation's target (e.g., `\"JAVA_VERSION\":\"21\"` for a Java upgrade targeting 21). Include `--telemetry` in each job's `command` string always (see Telemetry section):\n\n```bash\naws lambda invoke --function-name atx-trigger-batch-jobs \\\n --payload '{\"batchName\":\"\u003cname>-chunk-1\",\"jobs\":[{\"source\":\"\u003curl>\",\"command\":\"atx custom def exec -n \u003ctd> -p /source/\u003cproject> -x -t --telemetry \\\"client=\u003cclient>,agent=\u003cagent>,executionMode=remote\\\"\",\"jobName\":\"\u003cname>\",\"environment\":{\"JAVA_VERSION\":\"\u003ctarget>\"}}]}' \\\n --cli-binary-format raw-in-base64-out /dev/stdout\n```\n\nIf the total exceeds 128, repeat with the next chunk:\n\n```bash\naws lambda invoke --function-name atx-trigger-batch-jobs \\\n --payload '{\"batchName\":\"\u003cname>-chunk-2\",\"jobs\":[...next 128 jobs...]}' \\\n --cli-binary-format raw-in-base64-out /dev/stdout\n```\n\nMonitor each batch by its `batchId`:\n\n```bash\naws lambda invoke --function-name atx-get-batch-status \\\n --payload '{\"batchId\":\"\u003cbatch-id>\"}' \\\n --cli-binary-format raw-in-base64-out /dev/stdout\n```\n\nPolling: every 60 seconds for the first 10 polls, then every 5 minutes after.\nReport only on status change.\n\n## Progress Reporting\n\n```\n[1/N] repo-name TD-name Status\n[2/N] repo-name TD-name Status\n```\n\n## Result Collection\n\nCollect per repo: success/failure, transformed code path, error details.\n\n```\nSucceeded:\n- repo-name: TD-name (config)\nFailed:\n- repo-name: TD-name (error)\n```\n\nFor remote executions, include the CloudWatch dashboard link in the final output:\n\n```bash\nREGION=${AWS_REGION:-${AWS_DEFAULT_REGION:-$(aws configure get region 2>/dev/null)}}\nREGION=${REGION:-us-east-1}\necho \"https://${REGION}.console.aws.amazon.com/cloudwatch/home#dashboards/dashboard/ATX-Transform-CLI-Dashboard\"\n```\n\nHand off to [results-synthesis.md](results-synthesis.md) for consolidated reporting.\n\n## Error Handling\n\n| Scenario | Action |\n|----------|--------|\n| Git clone fails | Log error, continue with remaining repos |\n| Transformation fails | Log repo and error, do not auto-retry |\n| Partial results | Generate summary from successes, report failures |\n\n## MANDATORY: Cleanup\n\nClean up session files **before starting** and **after completing** each batch:\n\n```bash\n[ -d ~/.aws/atx/custom/atx-agent-session ] && find ~/.aws/atx/custom/atx-agent-session -maxdepth 1 -type f \\( -name \"*.sh\" -o -name \"*.log\" -o -name \"*.pid\" -o -name \"*.exit\" -o -name \"*.zip\" \\) -delete 2>/dev/null || true\n```\n\nFor remote mode: after presenting results, also prompt the user about infrastructure\nteardown. See the Cleanup section in [remote-execution.md](remote-execution.md)\nfor the exact prompt and flow.\n\n## Key Principles\n\n1. Single prerequisite check — never repeat for parallel tasks\n2. Trust the match report — do not re-discover TDs\n3. Local parallel execution — maximum 3 concurrent repos (user-overridable); recommend remote for more than 9\n4. Remote parallel execution — submit in chunks of up to 128 jobs per `atx-trigger-batch-jobs` call; split larger sets into multiple calls (max 512 repos per session)\n5. Skip prerequisite checks in parallel task prompts\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":9761,"content_sha256":"a36d2a083b466874c1f741ce941dc7815a50ba3947683213ccde9067aa8c9519"},{"filename":"references/remote-execution.md","content":"# Remote Execution\n\nDeploy and manage AWS Batch/Fargate infrastructure for ATX transformations at scale.\nAll Lambda calls are executed by you — users never interact with Lambdas directly.\n\nRemote mode deploys to the user's own AWS account. Key resources:\n\n- Results stored in S3 (`atx-custom-output-{accountId}`) with KMS encryption, 30-day lifecycle\n- Source code uploaded to S3 (`atx-source-code-{accountId}`) with 7-day lifecycle\n- CloudWatch dashboard: `ATX-Transform-CLI-Dashboard` for monitoring jobs\n- 8 Lambda functions for job management (trigger, status, terminate, list)\n- AWS Batch/Fargate for container execution — costs nothing when idle\n- To find the account: `aws sts get-caller-identity --query Account --output text`\n\n## Infrastructure Check\n\nBefore checking, determine the active AWS region (from `AWS_REGION`, `AWS_DEFAULT_REGION`,\nor `aws configure get region`) and tell the user which region is being used.\n\nThen check deployment status:\n\n```bash\naws cloudformation describe-stacks --stack-name AtxInfrastructureStack \\\n --query 'Stacks[0].StackStatus' --output text || echo \"NOT_DEPLOYED\"\n```\n\nIf deployed (`CREATE_COMPLETE` or `UPDATE_COMPLETE`): proceed to job submission.\nIf `NOT_DEPLOYED` or any other status: get explicit user consent before deploying.\n\n## User Consent Prompt\n\nExplain what gets created: AWS Batch (Fargate), 8 Lambda functions, S3 buckets (KMS encrypted),\nCloudWatch dashboard, IAM roles. If using the pre-built image, no Docker is needed and no ECR\nrepository is created in their account. If using a custom image, an ECR repository is created\nand the container is built locally. One-time setup.\nDo NOT deploy until user confirms.\n\n## Deployment\n\n### Pre-built vs Custom Image\n\nThe infrastructure supports two container modes:\n\n**Pre-built image (default):** A public ECR image with Java (8, 11, 17, 21, 25),\nPython (3.8–3.14), Node.js (16–24), Maven, Gradle, and common build tools.\nNo Docker required on the user's machine. Use this when the pre-built image\nhas everything the transformation needs (source runtime, target runtime, build\ntools, and any other dependencies).\n\n**Custom image (fallback):** If the transformation requires a language, tool, or\nversion not in the pre-built image, you clone the infrastructure repo,\ncustomize the Dockerfile, and build locally. This requires Docker on the user's\nmachine.\n\nYou determine which mode to use during Step 6 (Verify Runtime Compatibility)\nin SKILL.md. Do NOT ask the user to choose — you decide automatically based\non whether the pre-built image has everything needed for the transformation.\n\n### Pre-built Image Runtimes\n\nThe pre-built image includes:\n\n- **Java**: 8, 11, 17, 21, 25 (Amazon Corretto) with Maven and Gradle 9.4\n- **Python**: 3.8, 3.9, 3.10, 3.11, 3.12, 3.13, 3.14 (dnf + pyenv)\n- **Node.js**: 16, 18, 20, 22, 24 (nvm) with yarn, pnpm, TypeScript, ts-node\n- **Build tools**: gcc, g++, make, patch\n- **CLI tools**: AWS CLI v2, ATX CLI, git, jq, curl, unzip, tar\n- **OS**: Amazon Linux 2023 (x86_64)\n\nIf the transformation target is in this list, use the pre-built image path.\n\n### Pre-built Image Path (No Docker Required)\n\nClone and run setup — Docker is NOT required:\n\n```bash\nATX_INFRA_DIR=\"$HOME/.aws/atx/custom/remote-infra\"\nif [ -d \"$ATX_INFRA_DIR\" ]; then\n git -C \"$ATX_INFRA_DIR\" add -A\n git -C \"$ATX_INFRA_DIR\" commit -m \"Local customizations\" -q 2>/dev/null || true\n git -C \"$ATX_INFRA_DIR\" pull -q\nelse\n git clone -b atx-remote-infra --single-branch https://github.com/aws-samples/aws-transform-custom-samples.git \"$ATX_INFRA_DIR\"\nfi\n```\n\nIf `git pull` reports a merge conflict, resolve it by keeping both the upstream\nchanges and the user's customizations in the `CUSTOM LANGUAGES AND TOOLS` section\nof the Dockerfile, then commit the merge.\n\nEnsure `prebuiltImageUri` is set in `cdk.json` (it should be set to \"public.ecr.aws/d9h8z6l7/aws-transform:latest\" by default). Then deploy:\n\n```bash\ncd \"$ATX_INFRA_DIR\" && ./setup.sh\n```\n\nThe setup script skips the Docker prerequisite check and container build when\n`prebuiltImageUri` is configured. First deploy takes ~3-5 minutes (no image build).\n\n### Custom Image Path (Docker Required)\n\nIf the transformation requires a runtime (source or target) or any other software not in the pre-built image,\nclone/update the repo, clear the pre-built URI, customize the Dockerfile, and deploy:\n\n```bash\nATX_INFRA_DIR=\"$HOME/.aws/atx/custom/remote-infra\"\nif [ -d \"$ATX_INFRA_DIR\" ]; then\n git -C \"$ATX_INFRA_DIR\" add -A\n git -C \"$ATX_INFRA_DIR\" commit -m \"Local customizations\" -q 2>/dev/null || true\n git -C \"$ATX_INFRA_DIR\" pull -q\nelse\n git clone -b atx-remote-infra --single-branch https://github.com/aws-samples/aws-transform-custom-samples.git \"$ATX_INFRA_DIR\"\nfi\n\ncd \"$ATX_INFRA_DIR\" && sed -i.bak 's|\"prebuiltImageUri\": \".*\"|\"prebuiltImageUri\": \"\"|' cdk.json\n```\n\nCustomize the Dockerfile (see Container Customization below), then deploy:\n\n```bash\ncd \"$ATX_INFRA_DIR\" && ./setup.sh\n```\n\nThis path requires Docker installed and running. First deploy takes ~5-10 minutes\n(container build). CDK auto-detects Dockerfile changes and rebuilds the image.\n\n### Deployment Failures\n\nIf `setup.sh` fails, it prints the specific prerequisite that's missing. Fix that\none thing and re-run — the script is idempotent.\n\nIf deployment fails partway through (e.g., CloudFormation stack stuck in\n`ROLLBACK_COMPLETE` or `UPDATE_ROLLBACK_FAILED`), run teardown first, then retry:\n\n```bash\ncd \"$ATX_INFRA_DIR\" && rm -f cdk.context.json && ./teardown.sh && ./setup.sh\n```\n\nThis cleans up the half-deployed state, clears cached CDK context, and starts fresh.\nThe teardown script handles stacks in any state, including failed rollbacks.\n\n### Attach IAM Policies\n\nAfter deployment, generate and attach the runtime policy so the caller has\npermissions to invoke Lambdas, upload/download from S3, use KMS, etc.:\n\n```bash\ncd \"$ATX_INFRA_DIR\" && npx ts-node generate-caller-policy.ts\n```\n\nThis produces two JSON files in `$ATX_INFRA_DIR`:\n\n- `atx-runtime-policy.json` — Day-to-day operations (Lambda invoke, S3, KMS, Secrets Manager, logs)\n- `atx-deployment-policy.json` — One-time CDK deploy/destroy (CloudFormation, ECR, IAM, Batch, VPC)\n\nAttach the runtime policy to the caller:\n\n```bash\nACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)\nCALLER_ARN=$(aws sts get-caller-identity --query Arn --output text)\n\n# Create the managed policy (ignore EntityAlreadyExists, fail on other errors)\nif ! create_output=$(aws iam create-policy --policy-name ATXRuntimePolicy \\\n --policy-document \"file://$ATX_INFRA_DIR/atx-runtime-policy.json\" 2>&1); then\n echo \"$create_output\" | grep -q \"EntityAlreadyExists\" \\\n || { echo \"Failed to create policy: $create_output\" >&2; exit 1; }\nfi\n\n# Attach to the caller (handles IAM users, IAM roles, and SSO/assumed roles)\nif echo \"$CALLER_ARN\" | grep -q \":user/\"; then\n IDENTITY_NAME=$(echo \"$CALLER_ARN\" | awk -F'/' '{print $NF}')\n aws iam attach-user-policy --user-name \"$IDENTITY_NAME\" \\\n --policy-arn \"arn:aws:iam::${ACCOUNT_ID}:policy/ATXRuntimePolicy\"\nelif echo \"$CALLER_ARN\" | grep -Eq \":assumed-role/|:role/\"; then\n ROLE_NAME=$(echo \"$CALLER_ARN\" | sed 's/.*:\\(assumed-\\)\\{0,1\\}role\\///' | cut -d'/' -f1)\n aws iam attach-role-policy --role-name \"$ROLE_NAME\" \\\n --policy-arn \"arn:aws:iam::${ACCOUNT_ID}:policy/ATXRuntimePolicy\"\nfi\n```\n\nIf the attachment fails (insufficient IAM permissions, or an SSO-managed role with\nname starting with `AWSReservedSSO_`), inform the user:\n\n- The policy JSON is at `$ATX_INFRA_DIR/atx-runtime-policy.json`\n- They need their AWS administrator to create and attach it to their identity\n- For SSO users, it must be added to their IAM Identity Center permission set\n\nVerify the policy is working by invoking a Lambda:\n\n```bash\naws lambda invoke --function-name atx-list-jobs --payload '{}' \\\n --cli-binary-format raw-in-base64-out /dev/stdout\n```\n\nIf this succeeds, the runtime policy is active. If not, the attachment hasn't\ntaken effect yet — wait a few seconds and retry.\n\nIf the caller also needs to deploy/destroy infrastructure (not just run jobs),\nrepeat the above with `atx-deployment-policy.json` and policy name `ATXDeploymentPolicy`.\n\n## Lambda Function Names\n\nAfter deployment, the Lambda functions are available with these names:\n\n- `atx-trigger-job` — Submit a single transformation job\n- `atx-get-job-status` — Get status of a single job\n- `atx-terminate-job` — Terminate a running job\n- `atx-list-jobs` — List all jobs\n- `atx-trigger-batch-jobs` — Submit a batch of jobs\n- `atx-get-batch-status` — Get batch status\n- `atx-terminate-batch-jobs` — Terminate all jobs in a batch\n- `atx-list-batches` — List all batches\n\n## MCP Configuration (Optional)\n\nIf the user has a local ATX MCP configuration, include it inline with job\nsubmissions so the containers can use it. Check for a local config:\n\n```bash\ncat ~/.aws/atx/mcp.json 2>/dev/null\n```\n\nIf it exists, include the contents as the `mcpConfig` field in the `atx-trigger-job`\nor `atx-trigger-batch-jobs` payload. For example:\n\n```bash\naws lambda invoke --function-name atx-trigger-job \\\n --payload '{\"source\":\"...\",\"command\":\"...\",\"jobName\":\"...\",\"mcpConfig\":\u003ccontents of mcp.json>}' \\\n --cli-binary-format raw-in-base64-out /dev/stdout\n```\n\nThe MCP config travels with the job request — do NOT upload it separately via\n`atx-configure-mcp`. Skip this step if no local MCP config exists.\n\n## Job Submission\n\n**Limits:** Maximum 512 repositories per session. Submit in batches of up to 128\njobs each via `atx-trigger-batch-jobs`. If you have more than 128 jobs, split them\ninto multiple Lambda calls (e.g., 500 repos = 4 calls of 128 + 128 + 128 + 116).\nEach call returns its own `batchId` — track all of them for monitoring. AWS Batch\nruns all jobs in a batch concurrently. If the total repo count exceeds 512, stop\nand ask the user to reduce the list.\n\n**Repo analysis:** Do NOT scan or inspect repository contents locally in remote\nmode. The repos may not be available on the local machine. Let the user specify\nwhich TDs to apply, or use the TD already selected in the plugin.\n\n**Deployment failures:** If `setup.sh` or `cdk deploy` fails for any reason, run\n`./teardown.sh` first to clean up the partial state, then retry `./setup.sh`.\nDo not try to manually fix individual CloudFormation errors.\n\n**Source restrictions:** The `source` field accepts HTTPS git URLs, SSH git URLs\n(with `atx/ssh-key` configured), or S3 paths within the CDK-managed source bucket\n(`atx-source-code-{account}`). The container's IAM role cannot read from arbitrary\nS3 buckets. If the user provides zips in their own S3 bucket, copy them to the\nmanaged source bucket first (see Step 1 in SKILL.md).\n\nSingle job:\n\n```bash\naws lambda invoke --function-name atx-trigger-job \\\n --payload '{\"source\":\"\u003curl-or-s3>\",\"command\":\"atx custom def exec -n \u003ctd> -p /source/\u003cproject> -x -t\",\"jobName\":\"\u003cname>\"}' \\\n --cli-binary-format raw-in-base64-out /dev/stdout\n```\n\nBatch:\n\n```bash\naws lambda invoke --function-name atx-trigger-batch-jobs \\\n --payload '{\"batchName\":\"\u003cname>\",\"jobs\":[{\"source\":\"\u003curl>\",\"command\":\"atx custom def exec -n \u003ctd> -p /source/\u003cproject> -x -t\",\"jobName\":\"\u003cname>\"}]}' \\\n --cli-binary-format raw-in-base64-out /dev/stdout\n```\n\n## SSH URL Handling\n\nSSH git URLs (`[email protected]:org/repo.git` or `ssh://[email protected]/org/repo.git`)\nare passed directly to the Lambda — the container clones them remotely. This requires\nan SSH private key stored in Secrets Manager as `atx/ssh-key`. See Step 1 in SKILL.md\nfor setup instructions.\n\nIf the SSH key is not configured, the clone will fail inside the container. Do NOT\nfall back to cloning locally — guide the user through SSH key setup instead.\n\n## Polling\n\nPoll every 60 seconds for the first 10 polls, then every 5 minutes after.\nReport only on status change.\n\n```bash\naws lambda invoke --function-name atx-get-job-status \\\n --payload '{\"jobId\":\"\u003cid>\"}' \\\n --cli-binary-format raw-in-base64-out /dev/stdout\n\naws lambda invoke --function-name atx-get-batch-status \\\n --payload '{\"batchId\":\"\u003cid>\"}' \\\n --cli-binary-format raw-in-base64-out /dev/stdout\n```\n\n## Results Location\n\nDo NOT download results locally. Results stay in S3. Present the S3 path to the user:\n\n```\nResults: s3://atx-custom-output-{account-id}/transformations/\u003cjob-name>/\u003cconversation-id>/\n code.zip — zipped transformed source code\n logs.zip — ATX conversation logs\n```\n\nIf the user explicitly asks to download, provide the command but let them run it:\n\n```\naws s3 cp s3://atx-custom-output-{account-id}/transformations/\u003cjob-name>/\u003cconversation-id>/code.zip ./code.zip\n```\n\n## Private Repository Access\n\n**Note:** If the user has private repos, credentials should already be configured\nduring Step 1 (Collect Repositories) in SKILL.md. This section documents the\nmechanism for reference.\n\nThe container fetches credentials from AWS Secrets Manager at startup. Three secret types:\n\n**`atx/github-token`** — plain string GitHub PAT for private HTTPS repo cloning:\n\n```bash\naws secretsmanager create-secret --name \"atx/github-token\" --secret-string \"\u003ctoken>\"\n```\n\n**`atx/ssh-key`** — plain string SSH private key for private SSH repo cloning:\n\n```bash\naws secretsmanager create-secret --name \"atx/ssh-key\" --secret-string \"$(cat \u003cpath-to-your-private-key>)\"\n```\n\n**`atx/credentials`** — JSON array of credential files for any tool/registry (see Container Customization below).\n\nSetup (requires user consent):\n\n1. Explain which secrets will be created in their AWS account\n2. Get explicit confirmation and credentials from the user\n3. Create the secret(s)\n4. Container entrypoint auto-fetches at startup — no image rebuild needed\n5. User can delete anytime: `aws secretsmanager delete-secret --secret-id \"atx/github-token\" --region \"$REGION\" --force-delete-without-recovery`\n\nAWS credentials for ATX CLI are handled automatically by the IAM task role (refreshed every 45 min).\n\n## Monitoring\n\nCloudWatch dashboard: `ATX-Transform-CLI-Dashboard`\n\n- Job Tracking: completion rates, success/failure trends\n- Lambda Metrics: invocation counts, duration, errors\n- Real-time Logs: stream transformation progress\n\nDashboard URL (construct dynamically using the caller's region):\n\n```bash\nREGION=${AWS_REGION:-${AWS_DEFAULT_REGION:-$(aws configure get region 2>/dev/null)}}\nREGION=${REGION:-us-east-1}\necho \"https://${REGION}.console.aws.amazon.com/cloudwatch/home#dashboards/dashboard/ATX-Transform-CLI-Dashboard\"\n```\n\nInclude this link in the final output when remote execution completes.\n\n## Container Customization\n\nThe default container includes Java (8, 11, 17, 21, 25), Python (3.8–3.14), Node.js\n(16–24), Maven, Gradle, gcc/g++, make, and common build tools.\n\nIf a transformation requires a language or tool not included, you handle this\nautomatically during Step 6 (Verify Container Compatibility) — see SKILL.md. The\nDockerfile has a clearly marked `CUSTOM LANGUAGES AND TOOLS` section where new\n`RUN` commands should be inserted. After editing, redeploy with `cd \"$ATX_INFRA_DIR\" && ./setup.sh` — CDK\nauto-detects Dockerfile changes and rebuilds the image.\n\n### Adding Languages or Tools\n\n```dockerfile\n# Example: Add Rust (install as atxuser so binaries land in /home/atxuser/.cargo)\nUSER atxuser\nRUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y\nUSER root\nENV PATH=\"/home/atxuser/.cargo/bin:$PATH\"\n```\n\n### Private Package Registries\n\nCredentials are fetched from AWS Secrets Manager at container startup — never baked into the image.\n\n**`atx/github-token`** (plain string) — GitHub PAT for private repo cloning.\n\n**`atx/credentials`** (JSON array) — Generic credential files for any tool or registry. Each entry writes a file into the container at startup:\n\n```json\n[\n {\"path\": \"/home/atxuser/.npmrc\", \"content\": \"//npm.company.com/:_authToken=TOKEN\"},\n {\"path\": \"/home/atxuser/.m2/settings.xml\", \"content\": \"\u003csettings>...\u003c/settings>\"},\n {\"path\": \"/home/atxuser/.config/pip/pip.conf\", \"content\": \"[global]\\nindex-url = https://pypi.company.com/simple\"},\n {\"path\": \"/home/atxuser/.gem/credentials\", \"content\": \"---\\n:rubygems_api_key: KEY\", \"mode\": \"0600\"},\n {\"path\": \"/home/atxuser/.cargo/credentials.toml\", \"content\": \"[registry]\\ntoken = \\\"TOKEN\\\"\"},\n {\"path\": \"/home/atxuser/.nuget/NuGet.Config\", \"content\": \"\u003c?xml version=\\\"1.0\\\"?>...\"}\n]\n```\n\nCreate the secret:\n\n```bash\naws secretsmanager create-secret --name \"atx/credentials\" \\\n --secret-string '[{\"path\":\"/home/atxuser/.npmrc\",\"content\":\"//npm.company.com/:_authToken=TOKEN\"}]'\n```\n\nThis works for any language or tool added to the Dockerfile — npm, Maven, pip, RubyGems, Cargo, NuGet, etc. The `mode` field is optional (defaults to `0644`).\n\n### Version Switching at Runtime\n\nThe container supports runtime version switching via environment variables passed as container overrides.\nThe `environment` field on the job MUST match the exact target version of the\ntransformation — not the closest available version. For example, if upgrading to\nJava 23, set `\"JAVA_VERSION\":\"23\"` (not `\"21\"`). If the target version was added\nto the Dockerfile and entrypoint per Step 6, the switcher will activate it.\n\nVia Lambda (recommended):\n\n```bash\naws lambda invoke --function-name atx-trigger-job \\\n --payload '{\"source\":\"...\",\"jobName\":\"...\",\"command\":\"atx ...\",\"environment\":{\"JAVA_VERSION\":\"23\",\"NODE_VERSION\":\"22\",\"PYTHON_VERSION\":\"3.13\"}}' \\\n --cli-binary-format raw-in-base64-out /dev/stdout\n```\n\nVia direct Batch submission:\n\n```bash\naws batch submit-job \\\n --container-overrides '{\n \"environment\": [\n {\"name\": \"JAVA_VERSION\", \"value\": \"23\"},\n {\"name\": \"PYTHON_VERSION\", \"value\": \"3.13\"},\n {\"name\": \"NODE_VERSION\", \"value\": \"22\"}\n ]\n }' ...\n```\n\nAvailable: Java 8/11/17/21/25, Python 3.8–3.14, Node.js 16/18/20/22/24.\nPython accepts both short (`13`) and full (`3.13`) formats.\n\nSee `$ATX_INFRA_DIR/container/README.md` for full customization reference including Docker BuildKit secrets for secure credential handling.\n\n## Pricing\n\nDo NOT quote specific prices or cost estimates to the user. If the user asks about\npricing, direct them to: https://aws.amazon.com/transform/pricing/\n\nThe remote infrastructure (Batch, Lambda, S3) has no fixed costs — all services are\npay-per-use and cost nothing when idle.\n\n## Cleanup\n\nThe remote infrastructure costs nothing when idle — Fargate is pay-per-task,\nLambdas are pay-per-invoke, and S3 storage is minimal.\n\nAfter every remote execution completes (all jobs finished or failed), prompt the\nuser with the following:\n\n> Your remote infrastructure is still deployed in your AWS account. All services\n> are pay-per-use only — there are no fixed costs when idle. You can leave it in\n> place for future transformations, or tear it down now.\n>\n> For pricing details: https://aws.amazon.com/transform/pricing/\n>\n> If you tear down:\n>\n> - All ATX resources are completely removed from your account\n> - KMS key deletion is scheduled (7-day AWS minimum wait)\n> - S3 buckets, secrets, IAM policies, log groups — all deleted\n> - You'll need to re-run setup (~5-10 min) next time you use remote mode\n>\n> Would you like to keep the infrastructure or tear it down?\n\nIf the user chooses to tear down:\n\n```bash\ncd \"$ATX_INFRA_DIR\" && ./teardown.sh\n```\n\nIf the user chooses to keep it, confirm: \"Infrastructure will stay deployed. Next\ntime you run remote transformations, everything will be ready immediately.\"\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":19482,"content_sha256":"8f42f60fbba59ee948b2c6ee1aa0222cb9927bc6fb565fe50b7a604994806d92"},{"filename":"references/repo-analysis.md","content":"# Repo Analysis & TD Matching\n\n**Local mode only.** Repo analysis inspects files on the local filesystem — it\ncannot run inside remote containers. For remote mode, skip this step and let the\nuser specify which TDs to apply. If the user selected remote mode, do NOT attempt\nto run the detection commands below.\n\nInspect repositories and match them against available Transformation Definitions.\n\n## TD Discovery (Required First Step)\n\n```bash\natx custom def list # Human-readable\natx custom def list --json # Programmatic parsing\n```\n\nNever hardcode TD names. Only match repos against TDs that appear in this output.\nIf `atx` is not installed, install it first — do not fall back to guessed names.\n\n## Known AWS-Managed TDs (Reference Only)\n\nThis table is a guide for signal detection, NOT a substitute for `atx custom def list --json`.\nTD names change over time. Always use actual names from the live output.\n\n| TD Name (may change) | Description | Key Config |\n|---------|-------------|------------|\n| `AWS/java-version-upgrade` | Upgrade Java/JDK version (any source → any target) | Target JDK version (e.g., 17, 21) |\n| `AWS/python-version-upgrade` | Upgrade Python version (3.8/3.9 → 3.11/3.12/3.13) | Target Python version |\n| `AWS/nodejs-version-upgrade` | Upgrade Node.js version (any source → any target) | Target Node.js version |\n| `AWS/java-aws-sdk-v1-to-v2` | Migrate AWS SDK for Java v1 → v2 (Maven or Gradle) | None required |\n| `AWS/python-boto2-to-boto3` | Migrate Python boto2 → boto3 | None required |\n| `AWS/nodejs-aws-sdk-v2-to-v3` | Migrate AWS SDK for JavaScript v2 → v3 | None required |\n| `AWS/early-access-java-x86-to-graviton` | Migrate Java x86 code to ARM64/Graviton | None required |\n| `AWS/comprehensive-codebase-analysis` | Tech debt analysis + documentation generation | Optional: `additionalPlanContext` for focus area |\n\n## Transformation Patterns\n\n| Pattern | Complexity | Examples |\n|---------|-----------|----------|\n| Language Version Upgrades | Low-Medium | Java 8→17, Python 3.9→3.13, Node.js 12→22 |\n| API and Service Migrations | Medium | AWS SDK v1→v2, Boto2→Boto3, JUnit 4→5, javax→jakarta |\n| Framework Upgrades | Medium | Spring Boot 2.x→3.x, React 17→18, Angular, Django |\n| Framework Migrations | High | Angular→React, Redux→Zustand, Vue.js→React |\n| Library and Dependency Upgrades | Low-Medium | Pandas 1.x→2.x, NumPy, Hadoop/HBase/Hive |\n| Code Refactoring | Low-Medium | Print→Logging, string concat→f-strings, type hints |\n| Script/File Translations | Low-Medium | CDK→Terraform, Terraform→CloudFormation, Bash→PowerShell |\n| Architecture Migrations | Medium-High | x86→Graviton, on-prem→Lambda, server→containers |\n| Language-to-Language Migrations | Very High | Java→Python, JavaScript→TypeScript, C→Rust |\n| Custom/Org-Specific | Varies | Internal library migrations, coding standards enforcement |\n\nService routing: COBOL/mainframe → use AWS Transform for Mainframe. .NET Framework → consider AWS Transform for Windows. VMware → consider AWS Transform for VMware.\n\n## Detection Commands\n\n### Python\n\n```bash\ncat \u003crepo>/.python-version 2>/dev/null\ncat \u003crepo>/pyproject.toml 2>/dev/null | head -30\ncat \u003crepo>/setup.cfg 2>/dev/null | head -30\ncat \u003crepo>/requirements.txt 2>/dev/null | head -10\n```\n\n### Java\n\n```bash\ncat \u003crepo>/pom.xml 2>/dev/null | head -60 # Look for \u003cjava.version>, \u003cmaven.compiler.source>\ncat \u003crepo>/build.gradle 2>/dev/null | head -40 # Look for sourceCompatibility\ncat \u003crepo>/.java-version 2>/dev/null\n```\n\n### Node.js\n\n```bash\ncat \u003crepo>/package.json 2>/dev/null # Look for engines.node\ncat \u003crepo>/.nvmrc 2>/dev/null\ncat \u003crepo>/.node-version 2>/dev/null\n```\n\n## AWS SDK Detection\n\n| Signal | Language | What It Means |\n|--------|----------|---------------|\n| `import boto` / `from boto` (NOT boto3) | Python | Legacy boto2 — needs migration |\n| `com.amazonaws` or `aws-java-sdk` in pom.xml | Java | SDK v1 — needs migration |\n| `\"aws-sdk\"` in package.json (NOT `@aws-sdk`) | Node.js | SDK v2 — needs migration |\n\n```bash\n# Python boto2\ngrep -rlE \"import boto([^3]|$)|from boto([^3]|$)\" \u003crepo> --include=\"*.py\" 2>/dev/null | head -3\n# Java SDK v1\ngrep -rl \"com.amazonaws\" \u003crepo> --include=\"*.java\" 2>/dev/null | head -3\ncat \u003crepo>/pom.xml 2>/dev/null | grep -i \"aws-java-sdk\"\n# Node.js SDK v2\ncat \u003crepo>/package.json 2>/dev/null | grep '\"aws-sdk\"'\n```\n\n## Graviton Detection\n\n```bash\ngrep -rlE \"x86_64|amd64|x86-64\" \u003crepo> --include=\"*.yml\" --include=\"*.yaml\" --include=\"Dockerfile\" 2>/dev/null | head -3\n```\n\nCurrently Java-only. Match against Graviton migration TD if available.\n\n## Match Report Format\n\n```\nTransformation Match Report\n=============================\nRepository: \u003cname> (\u003cpath>)\n Language: \u003clang> \u003cversion>\n Matching TDs:\n - \u003ctd-name> — \u003cdescription>\n\n Other available TDs (may also apply):\n - \u003ccustom-td> — \u003cdescription>\n\nSummary: N repos analyzed, M have matches (T total jobs)\n```\n\nGroup by repository. Show detected version. Include repos with no matches.\nList custom TDs (non-`AWS/` prefix) under \"Other available TDs\".\n\n## Edge Cases\n\n| Case | Handling |\n|------|----------|\n| Repo already up-to-date | List upgrade TD but note current version |\n| Monorepo (multiple languages) | List all matching TDs — each is a separate job |\n| Mixed local + remote repos | Clone git URL repos locally for inspection, inspect local paths directly |\n| Custom TDs in account | Show under \"Other available TDs\" per repo |\n| Git clone fails | Report error, continue with remaining repos |\n\n## Cleanup\n\nDo NOT delete cloned repos after analysis — they are needed for local execution.\nTrack cloned repo paths and inform the user at session end so they can delete them.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":5773,"content_sha256":"49d104cce5713a00aa6e509b043643921e0a14f4c82eb34d47c745ddb6f16bfb"},{"filename":"references/results-synthesis.md","content":"# Results Synthesis\n\nGenerate a single summary file after bulk transformations complete.\n\n## Output\n\nWrite one file: `~/.aws/atx/custom/atx-agent-session/transformation-summaries/transformation-summary-$SESSION_TS.md`\n\n```bash\nmkdir -p ~/.aws/atx/custom/atx-agent-session/transformation-summaries\n```\n\n**Important:** Do NOT use heredoc (`cat \u003c\u003c EOF`) to write this file — heredoc\nblocks can hang in shell environments. Use a command (ex. `printf '%s'`) to write the content.\n\n## Template\n\n```markdown\n# ATX Transformation Summary\n> Completed: \u003ctimestamp>\n> Repositories: \u003ctotal> | Succeeded: \u003ccount> | Failed: \u003ccount>\n\n## Results\n| Project | TD | Status | Notes |\n|---------|-----|--------|-------|\n| \u003cname> | \u003ctd> | Succeeded/Failed | \u003cbrief note> |\n\n## Failed Transformations\n### \u003cproject-name>\n- **TD**: \u003ctd-name>\n- **Error**: \u003cone-line error summary>\n- **Suggested Fix**: \u003crecommendation>\n\n## Next Steps\n1. Review changes in each transformed repo\n2. Run tests and deploy\n```\n\n## Presentation\n\nTell the user:\n\n```\nResults: \u003csucceeded>/\u003ctotal> succeeded, \u003cfailed> failed\nSummary: ~/.aws/atx/custom/atx-agent-session/transformation-summaries/transformation-summary-$SESSION_TS.md\n```\n\nFor remote mode executions, also include the CloudWatch dashboard link:\n\n```bash\nREGION=${AWS_REGION:-${AWS_DEFAULT_REGION:-$(aws configure get region 2>/dev/null)}}\nREGION=${REGION:-us-east-1}\necho \"CloudWatch Dashboard: https://${REGION}.console.aws.amazon.com/cloudwatch/home#dashboards/dashboard/ATX-Transform-CLI-Dashboard\"\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1521,"content_sha256":"0f0ba458dc660f510adb393d027ccc74bbcf00e965353883228cc15814f67b74"},{"filename":"references/single-transformation.md","content":"# Single Transformation\n\nApply one TD to one repo. TD, config, and repo are already confirmed from the match report.\n\n## Local Mode\n\n### 1. Verify ATX (once per session, skip if already verified)\n\n```bash\natx --version\n```\n\n### 2. Verify Language Version\nThe active language runtime must match the transformation's target version so that builds and tests run correctly. For example, a Java 8 → 17 upgrade needs Java 17 available locally.\n\nCheck the installed version matches the target:\n\n```bash\njava -version # Java transformations\npython3 --version # Python transformations\nnode --version # Node.js transformations\n```\n\nIf there is a mismatch, resolve it before proceeding:\n\n- Look for the correct version already installed (e.g., check `/usr/lib/jvm/`, `pyenv versions`, `nvm ls`)\n- If found, switch to it (e.g., `export JAVA_HOME=\u003cpath to JDK> && export PATH=\"$JAVA_HOME/bin:$PATH\"`, `pyenv shell 3.12`, `nvm use 22`)\n- If not installed, ask the user for permission before installing (e.g., `brew install --cask corretto17` (macOS), `sudo yum install java-17-amazon-corretto-devel` (RHEL/AL2), or `sudo apt install java-17-amazon-corretto-jdk` (Debian/Ubuntu), `pyenv install 3.12`, `nvm install 22`)\n- Verify the switch succeeded by re-checking the version before continuing\n\n### 3. Prepare Source\n\nIf the user provided a git URL (HTTPS or SSH) instead of a local path, clone it\nlocally first. The user's local git config handles authentication for private repos\n— no Secrets Manager setup needed in local mode.\n\n```bash\nCLONE_DIR=~/.aws/atx/custom/atx-agent-session/repos/\u003crepo-name>-$SESSION_TS\ngit clone \u003cgit-url> \"$CLONE_DIR\"\n```\n\nIf the user provided an S3 path to a zip, download and extract it locally:\n\n```bash\naws s3 cp s3://user-bucket/repos/\u003cproject>.zip ~/.aws/atx/custom/atx-agent-session/\u003cproject>-$SESSION_TS.zip\nunzip -qo ~/.aws/atx/custom/atx-agent-session/\u003cproject>-$SESSION_TS.zip -d ~/.aws/atx/custom/atx-agent-session/repos/\u003cproject>-$SESSION_TS/\n```\n\nUse the cloned/extracted path as `\u003crepo-path>` for all subsequent steps. If the\nuser provided a local path, use it directly.\n\n### 4. Validate Repository\n\n```bash\nls -la \u003crepo-path>\ngit -C \u003crepo-path> status\n```\n\nIf not a git repo: `cd \u003crepo-path> && git init && git add . && git commit -m \"Initial commit\"`\n\n### Telemetry\n\nWhen running `atx custom def exec`, always include the `--telemetry` flag (see the Telemetry section in SKILL.md). Format:\n`--telemetry \"client=\u003cclient>,agent=\u003cagent>,executionMode=\u003clocal|remote>\"`\n\n- `client` is the MCP client or tool hosting this session (lowercase, no spaces) — e.g., `kiro`, `vscode`, `cursor`, `windsurf`, `claudecode`. Use the real tool name, not a default.\n- `agent` is the AI assistant driving this session (lowercase, no spaces) — e.g., `kiro`, `amazonq`, `claude`, `copilot`, `cline`, `codex`. Use the real assistant name, not a default.\n- `executionMode` is `local` for direct CLI invocation, `remote` when submitting via Lambda\n\n### 5. Execute and Monitor\n\nLaunch the transformation in a way that returns control immediately. Some shell\ntools block until all child processes exit, even with `&`. To avoid this, use bash to write\na launcher script and execute it, using exactly this:\n\n```bash\nmkdir -p ~/.aws/atx/custom/atx-agent-session\ncat > ~/.aws/atx/custom/atx-agent-session/run.sh \u003c\u003c 'RUNNER'\n#!/bin/bash\natx custom def exec -n \u003ctd-name> -p \u003crepo-path> -x -t \\\n --configuration 'additionalPlanContext=\u003cuser-config>' \\\n --telemetry \"client=\u003cclient>,agent=\u003cagent>,executionMode=local\"\necho $? > ~/.aws/atx/custom/atx-agent-session/transform.exit\nRUNNER\nchmod +x ~/.aws/atx/custom/atx-agent-session/run.sh\nnohup ~/.aws/atx/custom/atx-agent-session/run.sh > ~/.aws/atx/custom/atx-agent-session/transform.log 2>&1 &\necho $! > ~/.aws/atx/custom/atx-agent-session/transform.pid\ncat ~/.aws/atx/custom/atx-agent-session/transform.pid\n```\n\nOmit `--configuration` if no config is needed. The `--telemetry` flag is always included — see the Telemetry section above for field values.\n\nThis backgrounds the runner script (not ATX directly), so the exit code is\ncaptured to `~/.aws/atx/custom/atx-agent-session/transform.exit` when ATX finishes. The PID file tracks\nthe runner process.\n\n**As soon as you have the PID, immediately run the next command** — do NOT stop\nand wait for the user. The ATX CLI outputs the conversation log path within\n30-60 seconds of starting. Read it from the process log:\n\n```bash\ngrep \"Conversation log:\" ~/.aws/atx/custom/atx-agent-session/transform.log 2>/dev/null\n```\n\nIf it hasn't appeared yet, wait 15 seconds and retry (up to 4 attempts). The\noutput looks like:\n\n```\nConversation log: /Users/\u003cuser>/.aws/atx/custom/20260319_063712_e3479843/logs/2026-03-19T06-37-26-conversation.log\n```\n\nExtract the full path from this line — this is the conversation log for THIS\nspecific run. Do NOT use `ls -t` to find the most recent log across all\nconversations, as that may return a log from a previous run.\n\nThen start a monitoring loop. On each cycle:\n\n1. Check if the process is still running: `kill -0 $(cat ~/.aws/atx/custom/atx-agent-session/transform.pid) 2>/dev/null && echo \"RUNNING\" || echo \"DONE\"`\n2. Read the latest lines from the conversation log and tell the user what's happening\n3. Wait 60 seconds, then repeat\n\n**You MUST continue polling without waiting for user input.** After each poll,\nimmediately schedule the next one. The user should see continuous progress updates\nlike \"ATX is planning changes...\", \"Applying changes to 3 files...\", \"Running build...\".\n\nCRITICAL rules:\n\n1. **Extract conversation ID and log path.** After launching the process, look for\n the conversation log line in stdout:\n\n ```\n 📝 Conversation log: /Users/\u003cuser>/.aws/atx/custom/\u003cconversation-id>/logs/\u003ctimestamp>-conversation.log\n ```\n\n Extract the `\u003cconversation-id>` (e.g., `20260311_233325_21bb5ef0`) and the full\n log file path. Report the conversation ID to the user immediately. Example:\n \"Transformation started — conversation ID: `20260311_233325_21bb5ef0`\"\n\n2. **Tail the conversation log.** Once the log path is known, read new lines from\n the conversation log on each polling cycle and relay meaningful progress to the\n user. This is the primary way to keep the user informed of what ATX is doing\n (e.g., planning steps, applying changes, running builds, encountering errors).\n\n3. **Filter out noise.** When reading the conversation log or process stdout,\n silently IGNORE any lines containing \"Thinking\" — these are animated spinner\n indicators that repeat dozens of times and must NOT be echoed to the user.\n Surface everything else: planning output, file changes, build results, errors,\n and completion summaries.\n\n4. **Completion = process exit only.** The transformation is done ONLY when the\n background process exits (i.e., `kill -0` returns non-zero). Do NOT treat\n exit code 0 from any other command (grep, cat, test, etc.) as transformation\n completion. Do NOT treat log messages like \"TRANSFORMATION COMPLETE\" as\n completion — ATX performs additional steps after that (validation summary\n generation). Check the process exit code — do NOT parse terminal\n output or log content to determine completion. ATX prints progress messages\n and spinner animations throughout execution that do NOT indicate completion.\n\n5. **Polling interval.** Check the background process status and tail the\n conversation log every 60 seconds. Do NOT use escalating backoff for local\n mode — a fixed 60-second interval is sufficient. Do NOT sleep in the foreground\n terminal.\n\n6. **Exit code determines success.** Once `kill -0` confirms the process has\n exited, read the exit code: `cat ~/.aws/atx/custom/atx-agent-session/transform.exit`. Exit code 0 =\n success. Non-zero = failure. Only after reading the exit code should you\n report the transformation as complete or failed.\n\n### 6. Present Results\nShow TD, repo path, key changes. Next steps: `git diff`, run tests, deploy.\n\n## Remote Mode\n\n### 1. Check Infrastructure\n\n```bash\naws cloudformation describe-stacks --stack-name AtxInfrastructureStack \\\n --query 'Stacks[0].StackStatus' --output text || echo \"NOT_DEPLOYED\"\n```\n\nIf NOT_DEPLOYED: get user consent, then deploy. See [remote-execution.md](remote-execution.md).\n\n### 2. Prepare Source\n\n| Source Type | Action |\n|-------------|--------|\n| HTTPS git URL (public) | Use directly — container clones it |\n| HTTPS git URL (private) | Verify `atx/github-token` exists in Secrets Manager (see Step 1 in SKILL.md), then use directly — container fetches PAT and clones |\n| SSH git URL (public or private) | Verify `atx/ssh-key` exists in Secrets Manager (see Step 1 in SKILL.md), then use directly — container fetches SSH key and clones |\n| S3 bucket with zips | Copy zips from user's bucket to managed source bucket (`atx-source-code-{account}`), then use managed S3 paths |\n| Local repo | Zip → upload to S3 → use S3 path |\n\nFor local sources:\n\n```bash\nACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)\nmkdir -p ~/.aws/atx/custom/atx-agent-session\ncd \u003crepo-path> && zip -qr ~/.aws/atx/custom/atx-agent-session/\u003cproject>-$SESSION_TS.zip .\naws s3 cp ~/.aws/atx/custom/atx-agent-session/\u003cproject>-$SESSION_TS.zip s3://atx-source-code-${ACCOUNT_ID}/repos/\u003cproject>.zip\n```\n\n**Important:** Only the CDK-managed source bucket (`atx-source-code-{account}`) is\naccessible to the remote container. Do NOT pass arbitrary S3 bucket paths as source —\nthe container's IAM role cannot read from them.\n\n### 3. Submit Job\n\n```bash\naws lambda invoke --function-name atx-trigger-job \\\n --payload '{\"source\":\"\u003curl-or-s3>\",\"command\":\"atx custom def exec -n \u003ctd> -p /source/\u003cproject> -x -t --telemetry \\\"client=\u003cclient>,agent=\u003cagent>,executionMode=remote\\\"\",\"jobName\":\"\u003cname>\",\"environment\":{\"JAVA_VERSION\":\"\u003ctarget>\"}}' \\\n --cli-binary-format raw-in-base64-out /dev/stdout\n```\n\nAdd `--configuration \\\"additionalPlanContext=\u003cconfig>\\\"` to the command string if config is needed.\nThe `--telemetry` flag is always included — see the Telemetry section for field values.\n\nSet the appropriate version environment variable to match the transformation's target version:\n\n- `JAVA_VERSION` for Java transformations (e.g., `\"21\"` for a Java 8 → 21 upgrade)\n- `PYTHON_VERSION` for Python transformations (e.g., `\"3.12\"` for a Python 3.8 → 3.12 upgrade)\n- `NODE_VERSION` for Node.js transformations (e.g., `\"22\"` for a Node.js 18 → 22 upgrade)\n\nOnly include the variable relevant to the transformation language. The Lambda whitelists these keys and passes them as Batch container overrides; the entrypoint switches the active runtime at startup.\n\n### 4. Monitor\n\n```bash\naws lambda invoke --function-name atx-get-job-status \\\n --payload '{\"jobId\":\"\u003cjob-id>\"}' \\\n --cli-binary-format raw-in-base64-out /dev/stdout\n```\n\nPoll every 60 seconds for the first 10 polls, then every 5 minutes after.\nReport only on status change.\n\n### 5. Present Results (Remote)\n\nDo NOT download results locally. Results stay in S3. Present the S3 path to the user:\n\n```bash\nACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)\necho \"Results: s3://atx-custom-output-${ACCOUNT_ID}/transformations/\u003cjob-name>/\"\n```\n\nIf the user wants to download results, first list the S3 path to discover the\nconversation ID (generated at runtime inside the container). Use the actual\njob name and account ID — do NOT leave placeholders in commands given to the user:\n\n```bash\naws s3 ls s3://atx-custom-output-{account-id}/transformations/\u003cjob-name>/ --region \u003cregion>\n```\n\nThen provide the download command with the actual conversation ID:\n\n```\naws s3 cp s3://atx-custom-output-{account-id}/transformations/\u003cjob-name>/\u003cconversation-id>/code.zip ./code.zip\n```\n\nInclude the CloudWatch dashboard link in the completion output:\n\n```bash\nREGION=${AWS_REGION:-${AWS_DEFAULT_REGION:-$(aws configure get region 2>/dev/null)}}\nREGION=${REGION:-us-east-1}\necho \"https://${REGION}.console.aws.amazon.com/cloudwatch/home#dashboards/dashboard/ATX-Transform-CLI-Dashboard\"\n```\n\nShow TD, repo, status, downloaded path, and the dashboard link for monitoring history and logs.\n\nAfter presenting results, prompt the user about infrastructure teardown. See the\nCleanup section in [remote-execution.md](remote-execution.md) for the exact prompt.\n\n## Error Handling\n\n| Issue | Resolution |\n|-------|------------|\n| Dependency incompatibility | Check package compatibility, may need manual update |\n| Build failure (remote) | Check build command works locally, verify registry credentials in `atx/credentials` |\n| ATX timeout | Set `ATX_SHELL_TIMEOUT=1800` or break into smaller transforms |\n\n## MANDATORY: Cleanup\n\nClean up session files **before starting** and **after completing** each transformation:\n\n```bash\n[ -d ~/.aws/atx/custom/atx-agent-session ] && find ~/.aws/atx/custom/atx-agent-session -maxdepth 1 -type f \\( -name \"*.sh\" -o -name \"*.log\" -o -name \"*.pid\" -o -name \"*.exit\" -o -name \"*.zip\" \\) -delete 2>/dev/null || true\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":13049,"content_sha256":"2531959beee2d3d7215b091db5c57d69f39a5b64d77cb72342fa731561dfe544"},{"filename":"references/troubleshooting.md","content":"# Troubleshooting\n\n## Quick Reference\n\n| Issue | Resolution |\n|-------|------------|\n| `atx` not found | Install: `curl -fsSL https://transform-cli.awsstatic.com/install.sh` piped to `bash` |\n| AWS credentials error or expiry | Run `aws sts get-caller-identity`. Check `AWS_PROFILE` or access key env vars |\n| Permission denied | Local mode: need `transform-custom:*` — see Prerequisites → IAM Permissions in SKILL.md. Remote mode: generate and attach policies via `npx ts-node generate-caller-policy.ts` — see remote-execution.md |\n| Network error | Resolve region: `REGION=${AWS_REGION:-${AWS_DEFAULT_REGION:-$(aws configure get region 2>/dev/null)}}; REGION=${REGION:-us-east-1}`. Check access to `transform-custom.${REGION}.api.aws` |\n| Build fails during transform | Verify build command works locally first. Try interactive mode for debugging |\n| Transform not found | Run `atx custom def list --json` to check available TDs |\n| Configuration fails with commas | Do not use commas inside `additionalPlanContext` values — they break the CLI parser. Rephrase to avoid commas |\n| Conversation expired | Conversations expire after 30 days. Start a new one |\n| Windows not supported | Tell user to use Windows Subsystem for Linux (WSL) |\n| Git clone fails in remote container | See \"Private Repo Credential Issues\" section below |\n| Timeout | Set `export ATX_SHELL_TIMEOUT=1800` (default: 900s) |\n| Stale .exit file | The `.exit` file in `atx-agent-session/` may be left over from a previous run. Always use `kill -0 \u003cpid>` to check if the process is still running — do not rely solely on the `.exit` file |\n| Poor quality results | See Improving Quality section below |\n\n## Private Repo Credential Issues\n\nIf a git clone fails in the remote container (job status FAILED, logs show\nauthentication or 403 errors), work through these steps with the user:\n\n**1. Is the PAT/key stored?**\n\n```bash\naws secretsmanager describe-secret --secret-id \"atx/github-token\" --region \"$REGION\" 2>/dev/null && echo \"EXISTS\" || echo \"MISSING\"\naws secretsmanager describe-secret --secret-id \"atx/ssh-key\" --region \"$REGION\" 2>/dev/null && echo \"EXISTS\" || echo \"MISSING\"\n```\n\nIf missing, guide the user through setup — see Step 1 in SKILL.md.\n\n**2. Does the PAT have the right scope?**\nGitHub fine-grained PATs can be scoped to specific repos. If the user created a\nPAT for repos A and B but is now transforming repo C, the clone will fail with 403.\nAsk: \"Does your GitHub PAT have access to [repo name]? Fine-grained PATs need\neach repo explicitly listed.\"\n\nResolution: the user updates their PAT on GitHub to include the new repo, then\nupdates the stored secret:\n\n```bash\naws secretsmanager put-secret-value --secret-id \"atx/github-token\" --region \"$REGION\" --secret-string \"\u003cupdated-token>\"\n```\n\n**3. Has the PAT expired?**\nGitHub PATs can have expiration dates. Ask: \"When did you create this PAT? It may\nhave expired.\" Resolution: create a new PAT on GitHub, then update the secret:\n\n```bash\naws secretsmanager put-secret-value --secret-id \"atx/github-token\" --region \"$REGION\" --secret-string \"\u003cnew-token>\"\n```\n\n**4. Is it the right credential type for the URL?**\n\n- HTTPS URLs (`https://github.com/...`) need `atx/github-token` (PAT)\n- SSH URLs (`[email protected]:...`) need `atx/ssh-key` (SSH private key)\nIf the user provided SSH URLs but only has a PAT stored (or vice versa), guide\nthem to set up the correct credential type.\n\n**5. Classic vs fine-grained PAT?**\nClassic PATs with `repo` scope work for all repos the user has access to.\nFine-grained PATs need each repo explicitly added. If the user is unsure, suggest\na classic PAT with `repo` scope as the simpler option.\n\n## Local Mode Debugging\n\n| Log | Path |\n|-----|------|\n| Developer logs | `~/.aws/atx/logs/debug*.log` and `~/.aws/atx/logs/error.log` |\n| Conversation log | `~/.aws/atx/custom/\u003cconversation_id>/logs/\u003ctimestamp>-conversation.log` |\n\nNetwork errors may indicate VPN/firewall issues with AWS endpoints.\n\n## Remote Mode Debugging\n\n- CloudWatch logs: `/aws/batch/atx-transform`\n- Check log streams for the failed conversation ID in AWS Console\n- S3 output bucket contains artifacts even for failed jobs\n- Check batch job status for error details\n\n## Deployment Failures\n\nCDK deployment handles most issues automatically. Common recovery:\n\n```bash\nATX_INFRA_DIR=\"$HOME/.aws/atx/custom/remote-infra\"\ncd \"$ATX_INFRA_DIR\" && ./teardown.sh\ncd \"$ATX_INFRA_DIR\" && ./setup.sh\n```\n\nCommon causes: insufficient IAM permissions, service quota limits, no default VPC, Docker not running (only needed when using a custom container image, not the pre-built image).\n\n## Improving Quality\n\nDiagnose in this order:\n\n1. **Reference materials**: Provide migration guides or API specs via `additionalPlanContext`.\n2. **Complexity**: Decompose very complex transforms into smaller steps.\n3. **Knowledge items**: Review learnings from previous runs. Enable good ones, disable irrelevant ones.\n\n## Network Requirements\n\n| Endpoint | Purpose |\n|----------|---------|\n| `transform-cli.awsstatic.com` | CLI installation and updates |\n| `transform-custom.${REGION}.api.aws` | Transformation service API |\n\n## Pre-built Container Image\n\nThe default pre-built image URI is `public.ecr.aws/d9h8z6l7/aws-transform:latest`.\nThis is configured via `prebuiltImageUri` in `cdk.json`.\n\n## Remote Infrastructure Repo Issues\n\nIf `git pull`, `git commit`, or any other step on the remote-infra repo fails\n(merge conflicts, corrupted state, detached HEAD, permission errors, etc.), rename\nthe existing directory and re-clone from scratch. This is safe — the repo is just\na working copy of the infrastructure scripts, and all deployed AWS resources are\nunaffected.\n\n```bash\nATX_INFRA_DIR=\"$HOME/.aws/atx/custom/remote-infra\"\nif [ -d \"$ATX_INFRA_DIR\" ]; then\n mv \"$ATX_INFRA_DIR\" \"$ATX_INFRA_DIR.broken-$(date +%Y%m%d-%H%M%S)\"\nfi\ngit clone -b atx-remote-infra --single-branch https://github.com/aws-samples/aws-transform-custom-samples.git \"$ATX_INFRA_DIR\"\n```\n\nAfter re-cloning, continue with the normal flow (e.g., `cd \"$ATX_INFRA_DIR\" && ./setup.sh`).\nThe renamed directory can be deleted once you confirm the new clone works.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":6166,"content_sha256":"2266620beb5e88c7e4d5c46e53aebab364768070cf9d5c86ab4cd038cd3c8b33"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"AWS Transform (ATX)","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Overview","type":"text"}]},{"type":"paragraph","content":[{"text":"Perform code upgrades, migrations, and transformations using AWS Transform (ATX). Supports any-to-any transformations: language version upgrades (Java, Python, Node.js, etc.), framework migrations, AWS SDK migrations, library upgrades, code refactoring, architecture changes, and custom organization-specific transformations.","type":"text"}]},{"type":"paragraph","content":[{"text":"Two execution modes:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Local mode","type":"text","marks":[{"type":"strong"}]},{"text":": Runs the ATX CLI directly on the user's machine. Best for 1-9 repos.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Remote mode","type":"text","marks":[{"type":"strong"}]},{"text":": Runs transformations at scale via AWS Batch/Fargate containers. Best for 10+ repos or when the user prefers cloud execution. Infrastructure is auto-deployed with user consent.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"You handle the full workflow: inspecting repos, matching them to available transformation definitions, collecting configuration, and executing transformations in either mode — the user just provides repos and confirms the plan.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Greet and Wait","type":"text"}]},{"type":"paragraph","content":[{"text":"On activation, introduce AWS Transform with this exact text -- don't print the above Overview text to the user, that is just for your reference:","type":"text"}]},{"type":"paragraph","content":[{"text":"\"The agents modernizing the world's infrastructure and software — now accessible to your preferred AI assistant.","type":"text"}]},{"type":"paragraph","content":[{"text":"AWS Transform is a full modernization factory — compressing years of transformation work into months across infrastructure migrations, mainframe modernization, and continuous tech debt reduction. Today, with this skill, you have access to AWS Transform custom, the first of a growing library of playbooks.","type":"text"}]},{"type":"paragraph","content":[{"text":"AWS Transform custom can help you:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Upgrade Java, Python, and Node.js to modern versions","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Migrate AWS SDKs (Java SDK v1→v2, boto2→boto3, JS SDK v2→v3)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Handle framework migrations, library upgrades, and code refactoring","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Analyze codebases and generate documentation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Define and run your own custom transformations using natural language, docs, and code samples","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Run locally on a few repos for fast iteration, or at scale on hundreds of repos (up to 128 in-parallel). Note: this skill collects telemetry. To opt out, see https://docs.aws.amazon.com/transform/latest/userguide/transform-usage-telemetry.html","type":"text"}]},{"type":"paragraph","content":[{"text":"What would you like to transform today?\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Do NOT inspect any files, run any commands, or check prerequisites until the user responds.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Usage","type":"text"}]},{"type":"paragraph","content":[{"text":"Use when the user wants to:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Transform, upgrade, or migrate code (Java, Python, Node.js, etc.)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Migrate AWS SDKs (Java SDK v1→v2, boto2→boto3, JS SDK v2→v3, etc.)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Run bulk code transformations at scale via AWS Batch/Fargate","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Analyze which ATX transformations apply to their repositories","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Perform comprehensive codebase analysis","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Create a new custom Transformation Definition (TD)","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Core Concepts","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Transformation Definition (TD)","type":"text","marks":[{"type":"strong"}]},{"text":": A reusable transformation recipe discovered via ","type":"text"},{"text":"atx custom def list --json","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Match Report","type":"text","marks":[{"type":"strong"}]},{"text":": Auto-generated mapping of repos to applicable TDs based on code inspection","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Local Mode","type":"text","marks":[{"type":"strong"}]},{"text":": Runs ATX CLI on the user's machine (1-9 repos, max 3 concurrent)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Remote Mode","type":"text","marks":[{"type":"strong"}]},{"text":": Runs transformations in AWS Batch/Fargate (10+ repos, or by preference)","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Philosophy","type":"text"}]},{"type":"paragraph","content":[{"text":"Wait for the user. On activation, present what this skill can do and ask the user what they'd like to accomplish. Do NOT automatically inspect the working directory, open files, or any repository until the user explicitly provides repos to work with.","type":"text"}]},{"type":"paragraph","content":[{"text":"Once the user provides repositories, match — don't ask. Inspect those repositories and present which transformations apply automatically. Never show a raw TD list and ask the user to pick.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Prerequisites","type":"text"}]},{"type":"paragraph","content":[{"text":"Prerequisite checks run ONCE at the start of a session. Do not repeat per repo. Do NOT run prerequisite checks until the user has stated what they want to do.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"0. Platform Check (Required — All Modes)","type":"text"}]},{"type":"paragraph","content":[{"text":"Detect the user's operating system. If on Windows (not WSL), stop immediately and inform the user:","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"AWS Transform custom does not support native Windows. You need to install Windows Subsystem for Linux (WSL) and run this from within WSL.","type":"text"}]},{"type":"paragraph","content":[{"text":"Install WSL: ","type":"text"},{"text":"wsl --install","type":"text","marks":[{"type":"code_inline"}]},{"text":" in PowerShell (as Administrator), then restart. After that, open a WSL terminal and re-run this skill from there.","type":"text"}]}]},{"type":"paragraph","content":[{"text":"Check by running:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"uname -s","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Linux","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"Darwin","type":"text","marks":[{"type":"code_inline"}]},{"text":" → proceed normally","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"MINGW*","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"MSYS*","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"CYGWIN*","type":"text","marks":[{"type":"code_inline"}]},{"text":", or any Windows-like output → block and show the WSL message above","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Command fails, errors, or is not found → treat as native Windows, block and show the WSL message above","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Do NOT proceed with any other steps on native Windows.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"1. AWS CLI (Required — All Modes)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"aws --version","type":"text"}]},{"type":"paragraph","content":[{"text":"If not installed, guide the user:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"macOS: ","type":"text"},{"text":"brew install awscli","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"curl \"https://awscli.amazonaws.com/AWSCLIV2.pkg\" -o \"AWSCLIV2.pkg\" && sudo installer -pkg AWSCLIV2.pkg -target /","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Linux: ","type":"text"},{"text":"curl \"https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip\" -o \"awscliv2.zip\" && unzip awscliv2.zip && sudo ./aws/install","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"Do NOT proceed until ","type":"text"},{"text":"aws --version","type":"text","marks":[{"type":"code_inline"}]},{"text":" succeeds.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"2. AWS Credentials (Required — All Modes)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"aws sts get-caller-identity","type":"text"}]},{"type":"paragraph","content":[{"text":"If credentials are NOT configured, walk the user through setup:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"AWS Transform custom requires AWS credentials to authenticate with the service. Configure authentication using one of the following methods.\n\n1. AWS CLI Configure (~/.aws/credentials):\n aws configure\n\n2. AWS Credentials File (manual). Configure credentials in ~/.aws/credentials:\n\n[default]\naws_access_key_id = your_access_key\naws_secret_access_key = your_secret_key\n\n3. Environment Variables. Set the following environment variables:\n\nexport AWS_ACCESS_KEY_ID=your_access_key\nexport AWS_SECRET_ACCESS_KEY=your_secret_key\nexport AWS_SESSION_TOKEN=your_session_token\n\nYou can also specify a profile using the AWS_PROFILE environment variable:\n\nexport AWS_PROFILE=your_profile_name","type":"text"}]},{"type":"paragraph","content":[{"text":"Do NOT proceed until credentials are verified. Re-run ","type":"text"},{"text":"aws sts get-caller-identity","type":"text","marks":[{"type":"code_inline"}]},{"text":" after setup.","type":"text"}]},{"type":"paragraph","content":[{"text":"Note: environment variables set via ","type":"text"},{"text":"export","type":"text","marks":[{"type":"code_inline"}]},{"text":" do not carry over between shell sessions. If the agent spawns a new shell, credentials set as env vars may be lost. Prefer ","type":"text"},{"text":"aws configure","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"~/.aws/credentials","type":"text","marks":[{"type":"code_inline"}]},{"text":" for persistence.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"3. ATX CLI (Required — All Modes)","type":"text"}]},{"type":"paragraph","content":[{"text":"Required in all modes for TD discovery (","type":"text"},{"text":"atx custom def list --json","type":"text","marks":[{"type":"code_inline"}]},{"text":"). Local mode also uses it for transformation execution.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"atx --version\n# Install: curl -fsSL https://transform-cli.awsstatic.com/install.sh | bash","type":"text"}]},{"type":"paragraph","content":[{"text":"Mandatory: always run ","type":"text","marks":[{"type":"strong"}]},{"text":"atx update","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" once at the start of every session","type":"text","marks":[{"type":"strong"}]},{"text":", even if you just ran it recently. This catches new ATX CLI versions and new TDs. Run it before any other ATX command (including ","type":"text"},{"text":"atx custom def list --json","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"atx update","type":"text"}]},{"type":"paragraph","content":[{"text":"Do NOT skip this step. Do NOT ask the user whether to update. Do NOT condition it on whether the CLI \"needs\" an update. Run it unconditionally.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"4. IAM Permissions (Required — All Modes)","type":"text"}]},{"type":"paragraph","content":[{"text":"Local mode requires ","type":"text"},{"text":"transform-custom:*","type":"text","marks":[{"type":"code_inline"}]},{"text":" minimum. Verify by running a TD list:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"atx custom def list --json","type":"text"}]},{"type":"paragraph","content":[{"text":"If this succeeds, permissions are sufficient — skip the rest of this section.","type":"text"}]},{"type":"paragraph","content":[{"text":"If it fails with a permissions error, the caller needs the ","type":"text"},{"text":"transform-custom:*","type":"text","marks":[{"type":"code_inline"}]},{"text":" IAM permission. Explain to the user what's needed and get confirmation before proceeding:","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Your identity needs the ","type":"text"},{"text":"transform-custom:*","type":"text","marks":[{"type":"code_inline"}]},{"text":" permission to use the ATX CLI. I can attach the AWS-managed policy ","type":"text"},{"text":"AWSTransformCustomFullAccess","type":"text","marks":[{"type":"code_inline"}]},{"text":" to your identity. Shall I proceed?","type":"text"}]}]},{"type":"paragraph","content":[{"text":"Only after the user confirms, attach the managed policy:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"CALLER_ARN=$(aws sts get-caller-identity --query Arn --output text)\nif echo \"$CALLER_ARN\" | grep -q \":user/\"; then\n IDENTITY_NAME=$(echo \"$CALLER_ARN\" | awk -F'/' '{print $NF}')\n aws iam attach-user-policy --user-name \"$IDENTITY_NAME\" \\\n --policy-arn \"arn:aws:iam::aws:policy/AWSTransformCustomFullAccess\"\nelif echo \"$CALLER_ARN\" | grep -Eq \":assumed-role/|:role/\"; then\n ROLE_NAME=$(echo \"$CALLER_ARN\" | sed 's/.*:\\(assumed-\\)\\{0,1\\}role\\///' | cut -d'/' -f1)\n aws iam attach-role-policy --role-name \"$ROLE_NAME\" \\\n --policy-arn \"arn:aws:iam::aws:policy/AWSTransformCustomFullAccess\"\nfi","type":"text"}]},{"type":"paragraph","content":[{"text":"If the attachment command itself fails (e.g., insufficient IAM permissions, or an SSO-managed role), inform the user they need to ask their AWS administrator to attach the ","type":"text"},{"text":"AWSTransformCustomFullAccess","type":"text","marks":[{"type":"code_inline"}]},{"text":" AWS-managed policy to their identity. For SSO users (role names starting with ","type":"text"},{"text":"AWSReservedSSO_","type":"text","marks":[{"type":"code_inline"}]},{"text":"), this must be added to their IAM Identity Center permission set — it cannot be attached directly.","type":"text"}]},{"type":"paragraph","content":[{"text":"Do NOT proceed until ","type":"text"},{"text":"atx custom def list --json","type":"text","marks":[{"type":"code_inline"}]},{"text":" succeeds.","type":"text"}]},{"type":"paragraph","content":[{"text":"Remote mode requires additional permissions (Lambda invoke, S3, KMS, Secrets Manager, CloudWatch). These are generated and attached as part of the deployment flow — see ","type":"text"},{"text":"references/remote-execution.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/remote-execution.md","title":null}}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"references/cli-reference.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/cli-reference.md","title":null}}]},{"text":" for the full permission list.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"5. AWS CDK (Remote Mode Only)","type":"text"}]},{"type":"paragraph","content":[{"text":"Required for deploying remote infrastructure. Check if installed:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"cdk --version","type":"text"}]},{"type":"paragraph","content":[{"text":"If not installed, install it globally:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"npm install -g aws-cdk","type":"text"}]},{"type":"paragraph","content":[{"text":"Do NOT proceed with remote deployment until ","type":"text"},{"text":"cdk --version","type":"text","marks":[{"type":"code_inline"}]},{"text":" succeeds.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"6. Remote Infrastructure (Remote Mode Only — Deferred)","type":"text"}]},{"type":"paragraph","content":[{"text":"Only verify if user chooses remote mode. The infrastructure CDK scripts are fetched at runtime by cloning ","type":"text"},{"text":"https://github.com/aws-samples/aws-transform-custom-samples.git","type":"text","marks":[{"type":"code_inline"}]},{"text":" (branch ","type":"text"},{"text":"atx-remote-infra","type":"text","marks":[{"type":"code_inline"}]},{"text":") — they are not bundled with this skill. See ","type":"text"},{"text":"references/remote-execution.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/remote-execution.md","title":null}}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Workflow","type":"text"}]},{"type":"paragraph","content":[{"text":"Generate a session timestamp once and reuse it for all paths in this session:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"SESSION_TS=$(date +%Y%m%d-%H%M%S)","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 1: Collect Repositories","type":"text"}]},{"type":"paragraph","content":[{"text":"Ask the user for local paths or git URLs. Accept one or many. Do NOT assume the current working directory or open editor files are the target — wait for the user to explicitly provide repositories.","type":"text"}]},{"type":"paragraph","content":[{"text":"Accepted source formats:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Local paths","type":"text","marks":[{"type":"strong"}]},{"text":" — directories on the user's machine (e.g., ","type":"text"},{"text":"/home/user/my-project","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"HTTPS git URLs","type":"text","marks":[{"type":"strong"}]},{"text":" — public or private (e.g., ","type":"text"},{"text":"https://github.com/org/repo.git","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"SSH git URLs","type":"text","marks":[{"type":"strong"}]},{"text":" — e.g., ","type":"text"},{"text":"[email protected]:org/repo.git","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"S3 bucket path with zips","type":"text","marks":[{"type":"strong"}]},{"text":" — e.g., ","type":"text"},{"text":"s3://my-bucket/repos/","type":"text","marks":[{"type":"code_inline"}]},{"text":" containing zip files of repositories. Each zip becomes one transformation job.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"S3 Bucket Input","type":"text"}]},{"type":"paragraph","content":[{"text":"If the user provides an S3 path containing zip files, ask which execution mode they prefer (if not already specified). S3 input works in both modes:","type":"text"}]},{"type":"paragraph","content":[{"text":"Remote mode:","type":"text","marks":[{"type":"strong"}]},{"text":" Copy the zips from the user's bucket to the managed source bucket, then submit jobs pointing to the managed copies:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)\nSOURCE_BUCKET=\"atx-source-code-${ACCOUNT_ID}\"\n\n# List all zips in the user's bucket path\naws s3 ls s3://user-bucket/repos/ --recursive | grep '\\.zip

AWS Transform (ATX) Overview Perform code upgrades, migrations, and transformations using AWS Transform (ATX). Supports any-to-any transformations: language version upgrades (Java, Python, Node.js, etc.), framework migrations, AWS SDK migrations, library upgrades, code refactoring, architecture changes, and custom organization-specific transformations. Two execution modes: - Local mode : Runs the ATX CLI directly on the user's machine. Best for 1-9 repos. - Remote mode : Runs transformations at scale via AWS Batch/Fargate containers. Best for 10+ repos or when the user prefers cloud execution…

\n\n# Copy each zip to the managed source bucket\naws s3 sync s3://user-bucket/repos/ s3://${SOURCE_BUCKET}/repos/ --exclude \"*\" --include \"*.zip\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Then submit a batch job with one job per zip, each pointing to ","type":"text"},{"text":"s3://${SOURCE_BUCKET}/repos/\u003cfilename>.zip","type":"text","marks":[{"type":"code_inline"}]},{"text":". The container handles zip extraction automatically. See ","type":"text"},{"text":"references/multi-transformation.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/multi-transformation.md","title":null}}]},{"text":" for batch submission. The managed source bucket has a 7-day lifecycle — copied zips auto-delete.","type":"text"}]},{"type":"paragraph","content":[{"text":"Local mode:","type":"text","marks":[{"type":"strong"}]},{"text":" Download and extract each zip locally:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"mkdir -p ~/.aws/atx/custom/atx-agent-session/repos\naws s3 sync s3://user-bucket/repos/ ~/.aws/atx/custom/atx-agent-session/repos/ --exclude \"*\" --include \"*.zip\"\nfor zip in ~/.aws/atx/custom/atx-agent-session/repos/*.zip; do\n name=$(basename \"$zip\" .zip)\n unzip -qo \"$zip\" -d \"$HOME/.aws/atx/custom/atx-agent-session/repos/${name}-$SESSION_TS/\"\ndone","type":"text"}]},{"type":"paragraph","content":[{"text":"Use the extracted directories as ","type":"text"},{"text":"\u003crepo-path>","type":"text","marks":[{"type":"code_inline"}]},{"text":" for local execution. Standard local mode limits apply (max 3 concurrent repos).","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Private Repository Detection (Remote Mode)","type":"text"}]},{"type":"paragraph","content":[{"text":"Always ask the user","type":"text","marks":[{"type":"strong"}]},{"text":" — do NOT try to determine repo visibility yourself. Never attempt to clone, curl, or probe a URL to check if it's public or private. Simply ask the user. As soon as the user provides git URLs and remote mode is selected (or likely), ask:","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"\"Are any of these repositories private? If so, the remote container needs credentials to clone them — I'll walk you through the setup.\"","type":"text"}]}]},{"type":"paragraph","content":[{"text":"Do NOT skip this question. Do NOT try to infer visibility by attempting a clone, curl, or any other network request. Just ask.","type":"text"}]},{"type":"paragraph","content":[{"text":"If the user confirms repos are private, determine the credential type based on URL format:","type":"text"}]},{"type":"paragraph","content":[{"text":"First, resolve the region (use for all Secrets Manager commands below):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"REGION=${AWS_REGION:-${AWS_DEFAULT_REGION:-$(aws configure get region 2>/dev/null)}}\nREGION=${REGION:-us-east-1}","type":"text"}]},{"type":"paragraph","content":[{"text":"For HTTPS URLs","type":"text","marks":[{"type":"strong"}]},{"text":" — check whether a GitHub PAT is already configured:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"aws secretsmanager describe-secret --secret-id \"atx/github-token\" --region \"$REGION\" 2>/dev/null \\\n && echo \"CONFIGURED\" || echo \"NOT_CONFIGURED\"","type":"text"}]},{"type":"paragraph","content":[{"text":"If CONFIGURED, ask the user: \"A GitHub PAT is already stored. Would you like to keep using it, or replace it with a new one?\" If they want to replace it, tell them to run:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"aws secretsmanager put-secret-value --secret-id \"atx/github-token\" --region \"$REGION\" --secret-string \"YOUR_TOKEN_HERE\"","type":"text"}]},{"type":"paragraph","content":[{"text":"If NOT_CONFIGURED, explain what's needed and tell the user to run the create command:","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"\"Private HTTPS repos need a GitHub Personal Access Token (PAT) stored in AWS Secrets Manager. The remote container fetches it at startup to clone your repos. The token stays in your AWS account — you can delete it anytime.","type":"text"}]},{"type":"paragraph","content":[{"text":"The PAT needs the ","type":"text"},{"text":"repo","type":"text","marks":[{"type":"code_inline"}]},{"text":" scope for private repositories. Create one at https://github.com/settings/tokens and then run:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"aws secretsmanager create-secret --name \"atx/github-token\" --region \"$REGION\" --secret-string \"YOUR_TOKEN_HERE\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Delete anytime: ","type":"text"},{"text":"aws secretsmanager delete-secret --secret-id atx/github-token --region \"$REGION\" --force-delete-without-recovery","type":"text","marks":[{"type":"code_inline"}]},{"text":"\"","type":"text"}]}]},{"type":"paragraph","content":[{"text":"Do NOT ask the user to paste their token in chat. They run the command themselves. Wait for the user to confirm it's done, then verify:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"aws secretsmanager describe-secret --secret-id \"atx/github-token\" --region \"$REGION\" 2>/dev/null \\\n && echo \"CONFIGURED\" || echo \"NOT_CONFIGURED\"","type":"text"}]},{"type":"paragraph","content":[{"text":"For SSH URLs","type":"text","marks":[{"type":"strong"}]},{"text":" (","type":"text"},{"text":"git@...","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"ssh://...","type":"text","marks":[{"type":"code_inline"}]},{"text":") — check whether an SSH key is configured:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"aws secretsmanager describe-secret --secret-id \"atx/ssh-key\" --region \"$REGION\" 2>/dev/null \\\n && echo \"CONFIGURED\" || echo \"NOT_CONFIGURED\"","type":"text"}]},{"type":"paragraph","content":[{"text":"If CONFIGURED, ask the user: \"An SSH key is already stored. Would you like to keep using it, or replace it with a new one?\" If they want to replace it, tell them to run:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"aws secretsmanager put-secret-value --secret-id \"atx/ssh-key\" --region \"$REGION\" --secret-string \"$(cat \u003cpath-to-your-private-key>)\"","type":"text"}]},{"type":"paragraph","content":[{"text":"If NOT_CONFIGURED, explain what's needed and tell the user to run the create command:","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"\"SSH repos need an SSH private key stored in AWS Secrets Manager. The remote container fetches it at startup to clone your repos.","type":"text"}]},{"type":"paragraph","content":[{"text":"Run:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"aws secretsmanager create-secret --name \"atx/ssh-key\" --region \"$REGION\" --secret-string \"$(cat \u003cpath-to-your-private-key>)\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Delete anytime: ","type":"text"},{"text":"aws secretsmanager delete-secret --secret-id atx/ssh-key --region \"$REGION\" --force-delete-without-recovery","type":"text","marks":[{"type":"code_inline"}]},{"text":"\"","type":"text"}]}]},{"type":"paragraph","content":[{"text":"Do NOT ask the user to paste their SSH key in chat. They run the command themselves.","type":"text"}]},{"type":"paragraph","content":[{"text":"For local mode, private repo credentials are not needed — the user's local git config handles authentication. Skip this check entirely for local mode.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 2: Discover TDs (Silent)","type":"text"}]},{"type":"paragraph","content":[{"text":"Run silently — do NOT show output to user:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"atx custom def list --json","type":"text"}]},{"type":"paragraph","content":[{"text":"Inspect the JSON output directly to build an internal lookup of available TDs. Do NOT pipe the output to python, jq, or other parsing scripts — read the JSON yourself. Never hardcode TD names.","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Creating a New TD","type":"text"}]},{"type":"paragraph","content":[{"text":"User explicitly asks to create a TD:","type":"text","marks":[{"type":"strong"}]},{"text":" Do NOT attempt to create one programmatically. Tell the user:","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"To create a new Transformation Definition, open a new terminal and run:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"atx -t","type":"text"}]},{"type":"paragraph","content":[{"text":"This starts an interactive session where you describe the transformation you want to build (e.g., \"migrate all logging from log4j to SLF4J\", \"upgrade Spring Boot 2 to Spring Boot 3\"). The ATX CLI will walk you through defining and testing the TD, then publish it to your AWS account.","type":"text"}]},{"type":"paragraph","content":[{"text":"Once it's published, come back here and I'll pick it up automatically when I scan your available TDs.","type":"text"}]}]},{"type":"paragraph","content":[{"text":"No existing TD matches the user's goal:","type":"text","marks":[{"type":"strong"}]},{"text":" Do NOT silently redirect to TD creation. The match logic may be imperfect. Instead, confirm with the user first:","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"\"I didn't find an existing TD that covers [describe the user's goal]. Would you like to create a new one?\"","type":"text"}]}]},{"type":"paragraph","content":[{"text":"Only show the ","type":"text"},{"text":"atx -t","type":"text","marks":[{"type":"code_inline"}]},{"text":" instructions if the user confirms. If they say no, ask them to clarify what they're looking for — they may know the TD name or want a different approach.","type":"text"}]},{"type":"paragraph","content":[{"text":"Do NOT run ","type":"text"},{"text":"atx -t","type":"text","marks":[{"type":"code_inline"}]},{"text":" yourself — it requires an interactive terminal session that the agent cannot drive. The user must run it manually in a separate terminal.","type":"text"}]},{"type":"paragraph","content":[{"text":"After the user returns from creating a TD, re-run ","type":"text"},{"text":"atx custom def list --json","type":"text","marks":[{"type":"code_inline"}]},{"text":" to pick up the newly published TD and continue with the normal workflow.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 3: Inspect Each Repository","type":"text"}]},{"type":"paragraph","content":[{"text":"Perform lightweight inspection only — check config files for key signals:","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Signal","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Files to Check","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Likely TD Type","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Python version","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":".python-version","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"pyproject.toml","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"setup.cfg","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"requirements.txt","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Python version upgrade","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Java version","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"pom.xml","type":"text","marks":[{"type":"code_inline"}]},{"text":" (","type":"text"},{"text":"\u003cjava.version>","type":"text","marks":[{"type":"code_inline"}]},{"text":"), ","type":"text"},{"text":"build.gradle","type":"text","marks":[{"type":"code_inline"}]},{"text":" (","type":"text"},{"text":"sourceCompatibility","type":"text","marks":[{"type":"code_inline"}]},{"text":"), ","type":"text"},{"text":".java-version","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Java version upgrade","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Node.js version","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"package.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" (","type":"text"},{"text":"engines.node","type":"text","marks":[{"type":"code_inline"}]},{"text":"), ","type":"text"},{"text":".nvmrc","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":".node-version","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Node.js version upgrade","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Python boto2","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"import boto","type":"text","marks":[{"type":"code_inline"}]},{"text":" (NOT boto3)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"boto2→boto3 migration","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Java SDK v1","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"com.amazonaws","type":"text","marks":[{"type":"code_inline"}]},{"text":" imports, ","type":"text"},{"text":"aws-java-sdk","type":"text","marks":[{"type":"code_inline"}]},{"text":" in pom.xml","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Java SDK v1→v2","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Node.js SDK v2","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"aws-sdk\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" in package.json (NOT ","type":"text"},{"text":"@aws-sdk","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"JS SDK v2→v3","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"x86 Java","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"x86_64","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"amd64","type":"text","marks":[{"type":"code_inline"}]},{"text":" in Dockerfiles, build configs","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Graviton migration","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Cross-reference detected signals against TDs from Step 2. Only match TDs that actually exist in the user's account.","type":"text"}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"references/repo-analysis.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/repo-analysis.md","title":null}}]},{"text":" for full detection commands.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 4: Present Match Report","type":"text"}]},{"type":"paragraph","content":[{"text":"Format:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Transformation Match Report\n=============================\nRepository: \u003cname> (\u003cpath>)\n Language: \u003clang> \u003cversion>\n Matching TDs:\n - \u003ctd-name> — \u003cdescription>\n\nSummary: N repos analyzed, M have applicable transformations (T total jobs)","type":"text"}]},{"type":"paragraph","content":[{"text":"Present the match report and wait for user confirmation before proceeding. Do NOT start any transformation without explicit user consent.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 5: Collect Configuration","type":"text"}]},{"type":"paragraph","content":[{"text":"Ask the user for any additional plan context (e.g., target version for upgrade TDs). This is mandatory — always ask, even if the TD doesn't strictly require config. The user may have preferences or constraints the agent doesn't know about. Skip only if the user explicitly says no additional context is needed.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 6: Verify Runtime Compatibility (Remote and Local)","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Remote Mode","type":"text"}]},{"type":"paragraph","content":[{"text":"Before submitting remote jobs, determine whether the pre-built image covers the target runtime or if a custom Docker build is needed.","type":"text"}]},{"type":"paragraph","content":[{"text":"Pre-built image includes:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Java","type":"text","marks":[{"type":"strong"}]},{"text":": 8, 11, 17, 21, 25 (Amazon Corretto) with Maven and Gradle 9.4","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Python","type":"text","marks":[{"type":"strong"}]},{"text":": 3.8, 3.9, 3.10, 3.11, 3.12, 3.13, 3.14 (dnf + pyenv)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Node.js","type":"text","marks":[{"type":"strong"}]},{"text":": 16, 18, 20, 22, 24 (nvm) with yarn, pnpm, TypeScript, ts-node","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Build tools","type":"text","marks":[{"type":"strong"}]},{"text":": gcc, g++, make, patch","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"CLI tools","type":"text","marks":[{"type":"strong"}]},{"text":": AWS CLI v2, ATX CLI, git, jq, curl, unzip, tar","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"OS","type":"text","marks":[{"type":"strong"}]},{"text":": Amazon Linux 2023 (x86_64)","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Decision logic:","type":"text","marks":[{"type":"strong"}]}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Based on the transformation requirements (source runtime, target runtime, build tools, and any other dependencies), determine whether everything needed is available in the pre-built image listed above","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If ","type":"text"},{"text":"yes","type":"text","marks":[{"type":"strong"}]},{"text":" → use the pre-built image path (no Docker required). Proceed to deployment using the pre-built image instructions in ","type":"text"},{"text":"references/remote-execution.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/remote-execution.md","title":null}}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If ","type":"text"},{"text":"no","type":"text","marks":[{"type":"strong"}]},{"text":" → use the custom image path (Docker required). Inform the user:","type":"text"}]}]}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"The remote container doesn't include [language/tool version]. To run this transformation remotely, I'll need to build a custom container image. This requires Docker installed and running on your machine. It's a one-time change — about 5-10 minutes. Want me to proceed?","type":"text"}]}]},{"type":"paragraph","content":[{"text":"If the user confirms, follow the custom image path in ","type":"text"},{"text":"references/remote-execution.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/remote-execution.md","title":null}}]},{"text":": clear ","type":"text"},{"text":"prebuiltImageUri","type":"text","marks":[{"type":"code_inline"}]},{"text":", customize the Dockerfile, and deploy.","type":"text"}]},{"type":"paragraph","content":[{"text":"If the user declines, suggest local mode as an alternative (if the tools are available on their machine).","type":"text"}]},{"type":"paragraph","content":[{"text":"Dockerfile customization (custom image path only):","type":"text","marks":[{"type":"strong"}]}]},{"type":"paragraph","content":[{"text":"First, read the Dockerfile to see what's installed:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"ATX_INFRA_DIR=\"$HOME/.aws/atx/custom/remote-infra\"\ncat \"$ATX_INFRA_DIR/container/Dockerfile\" 2>/dev/null","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Ensure the infrastructure repo is cloned and up to date:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"ATX_INFRA_DIR=\"$HOME/.aws/atx/custom/remote-infra\"\nif [ -d \"$ATX_INFRA_DIR\" ]; then\n git -C \"$ATX_INFRA_DIR\" add -A\n git -C \"$ATX_INFRA_DIR\" commit -m \"Local customizations\" -q 2>/dev/null || true\n git -C \"$ATX_INFRA_DIR\" pull -q\nelse\n git clone -b atx-remote-infra --single-branch https://github.com/aws-samples/aws-transform-custom-samples.git \"$ATX_INFRA_DIR\"\nfi","type":"text"}]},{"type":"paragraph","content":[{"text":"If ","type":"text"},{"text":"git pull","type":"text","marks":[{"type":"code_inline"}]},{"text":" reports a merge conflict, resolve it by keeping both upstream changes and the user's customizations in the ","type":"text"},{"text":"CUSTOM LANGUAGES AND TOOLS","type":"text","marks":[{"type":"code_inline"}]},{"text":" section of the Dockerfile, then commit the merge.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Edit ","type":"text"},{"text":"$ATX_INFRA_DIR/container/Dockerfile","type":"text","marks":[{"type":"code_inline"}]},{"text":". Find the section marked ","type":"text"},{"text":"# CUSTOM LANGUAGES AND TOOLS","type":"text","marks":[{"type":"code_inline"}]},{"text":" and insert ","type":"text"},{"text":"RUN","type":"text","marks":[{"type":"code_inline"}]},{"text":" commands after the comment block, before the ","type":"text"},{"text":"USER root","type":"text","marks":[{"type":"code_inline"}]},{"text":" line.","type":"text"}]},{"type":"paragraph","content":[{"text":"For missing versions of already-installed languages, add the version in the custom section. Examples:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"dockerfile"},"content":[{"text":"# Java 23 (Amazon Corretto — direct install, must run as root)\n# Do NOT use dnf in the custom section — pyenv overrides the system python3\n# that dnf depends on, causing \"No module named 'dnf'\" errors.\nUSER root\nRUN curl -fsSL \"https://corretto.aws/downloads/latest/amazon-corretto-23-x64-linux-jdk.tar.gz\" -o /tmp/corretto23.tar.gz && \\\n mkdir -p /usr/lib/jvm && \\\n tar -xzf /tmp/corretto23.tar.gz -C /usr/lib/jvm && \\\n rm /tmp/corretto23.tar.gz && \\\n ln -sfn /usr/lib/jvm/amazon-corretto-23.* /usr/lib/jvm/corretto-23\n\n# Node.js 23 (via nvm — must run as atxuser)\nUSER atxuser\nRUN . /home/atxuser/.nvm/nvm.sh && nvm install 23\nUSER root\n\n# Python 3.15 (via pyenv — must run as atxuser)\nUSER atxuser\nRUN eval \"$(/home/atxuser/.pyenv/bin/pyenv init -)\" && \\\n MAKE_OPTS=\"-j$(nproc)\" /home/atxuser/.pyenv/bin/pyenv install 3.15.0\nUSER root","type":"text"}]},{"type":"paragraph","content":[{"text":"For entirely new languages, avoid ","type":"text"},{"text":"dnf","type":"text","marks":[{"type":"code_inline"}]},{"text":" in the custom section — pyenv overrides the system python3 that ","type":"text"},{"text":"dnf","type":"text","marks":[{"type":"code_inline"}]},{"text":" depends on. Use language-specific installers instead:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"dockerfile"},"content":[{"text":"# Go\nRUN curl -fsSL https://go.dev/dl/go1.22.0.linux-amd64.tar.gz | tar -C /usr/local -xz\nENV PATH=\"/usr/local/go/bin:$PATH\"\n\n# Ruby (via rbenv — must run as atxuser)\nUSER atxuser\nRUN git clone --depth 1 https://github.com/rbenv/rbenv.git /home/atxuser/.rbenv && \\\n git clone --depth 1 https://github.com/rbenv/ruby-build.git /home/atxuser/.rbenv/plugins/ruby-build && \\\n /home/atxuser/.rbenv/bin/rbenv install 3.3.0 && \\\n /home/atxuser/.rbenv/bin/rbenv global 3.3.0\nENV PATH=\"/home/atxuser/.rbenv/shims:/home/atxuser/.rbenv/bin:$PATH\"\nUSER root\n\n# Rust\nUSER atxuser\nRUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y\nENV PATH=\"/home/atxuser/.cargo/bin:$PATH\"\nUSER root","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Update the version switcher in ","type":"text"},{"text":"$ATX_INFRA_DIR/container/entrypoint.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":". Find the relevant ","type":"text"},{"text":"switch_*_version","type":"text","marks":[{"type":"code_inline"}]},{"text":" function and add a case for the new version. For Java versions installed via direct download, find the extracted directory name under ","type":"text"},{"text":"/usr/lib/jvm/","type":"text","marks":[{"type":"code_inline"}]},{"text":". For example, to add Java 23:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# In switch_java_version(), add to the case statement:\n23) java_home=\"/usr/lib/jvm/corretto-23\" ;;","type":"text"}]},{"type":"paragraph","content":[{"text":"Check the actual directory name: ","type":"text"},{"text":"ls /usr/lib/jvm/","type":"text","marks":[{"type":"code_inline"}]},{"text":" — use the directory that matches the version you installed.","type":"text"}]},{"type":"paragraph","content":[{"text":"For Node.js, nvm handles arbitrary versions automatically — no entrypoint change needed. For Python, pyenv handles arbitrary versions — no entrypoint change needed (the existing pyenv fallback logic finds it).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Deploy (or redeploy): ","type":"text"},{"text":"cd \"$ATX_INFRA_DIR\" && ./setup.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" CDK hashes the ","type":"text"},{"text":"container/","type":"text","marks":[{"type":"code_inline"}]},{"text":" directory — any file change triggers a rebuild and push to ECR automatically.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"After redeployment, set the ","type":"text"},{"text":"environment","type":"text","marks":[{"type":"code_inline"}]},{"text":" field on the job to the exact target version (e.g., ","type":"text"},{"text":"\"JAVA_VERSION\":\"23\"","type":"text","marks":[{"type":"code_inline"}]},{"text":", not ","type":"text"},{"text":"\"21\"","type":"text","marks":[{"type":"code_inline"}]},{"text":"). The version switcher in the entrypoint reads this and activates the correct runtime.","type":"text"}]},{"type":"paragraph","content":[{"text":"If the user declines, suggest local mode as an alternative (if the tools are available on their machine).","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Local Mode","type":"text"}]},{"type":"paragraph","content":[{"text":"Before running local transformations, verify the user has the target runtime version installed. This applies to any language or runtime the transformation targets — Java, Python, Node.js, Ruby, Go, Rust, .NET, etc. Check the current version of whatever runtime the TD requires. For example:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"java -version # Java transformations\npython3 --version # Python transformations\nnode --version # Node.js transformations\nruby --version # Ruby transformations\ngo version # Go transformations","type":"text"}]},{"type":"paragraph","content":[{"text":"If the target version is not active, check whether it's already installed:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Java: check common install locations\n/usr/libexec/java_home -V 2>&1 # macOS\nls /usr/lib/jvm/ 2>/dev/null # Linux\n# Python: check if the specific version binary exists\nwhich python3.12 2>/dev/null # adjust version as needed\n# Node.js: check if nvm is available, or look for the binary\ncommand -v nvm &>/dev/null && nvm ls 2>/dev/null\nwhich node 2>/dev/null && node --version","type":"text"}]},{"type":"paragraph","content":[{"text":"If the target version is found, switch to it:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Java: ","type":"text"},{"text":"export JAVA_HOME=\u003cpath to JDK> && export PATH=\"$JAVA_HOME/bin:$PATH\"","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Python: ","type":"text"},{"text":"pyenv shell 3.15.0","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Node.js: ","type":"text"},{"text":"nvm use 23","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"Only if the target version is not installed at all, ask the user for permission before installing. Do NOT install runtimes without explicit user confirmation. Suggest the appropriate version manager:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Java: ","type":"text"},{"text":"brew install --cask corretto23","type":"text","marks":[{"type":"code_inline"}]},{"text":" (macOS), ","type":"text"},{"text":"sudo yum install java-23-amazon-corretto-devel","type":"text","marks":[{"type":"code_inline"}]},{"text":" (RHEL/AL2), or ","type":"text"},{"text":"sudo apt install java-23-amazon-corretto-jdk","type":"text","marks":[{"type":"code_inline"}]},{"text":" (Debian/Ubuntu)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Python: ","type":"text"},{"text":"pyenv install 3.15.0 && pyenv shell 3.15.0","type":"text","marks":[{"type":"code_inline"}]},{"text":", or ","type":"text"},{"text":"brew install [email protected]","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Node.js: ","type":"text"},{"text":"nvm install 23 && nvm use 23","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"The active runtime must match the transformation's target version so that builds and tests run correctly. Do NOT proceed with the transformation until the correct version is active.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 7: Confirm Transformation Plan","type":"text"}]},{"type":"paragraph","content":[{"text":"Present final plan with repo, TD, config, and execution mode. Do NOT proceed until user confirms.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 8: Execute","type":"text"}]},{"type":"paragraph","content":[{"text":"When running ","type":"text"},{"text":"atx custom def exec","type":"text","marks":[{"type":"code_inline"}]},{"text":", always include ","type":"text"},{"text":"--telemetry","type":"text","marks":[{"type":"code_inline"}]},{"text":" (see the Telemetry section).","type":"text"}]},{"type":"paragraph","content":[{"text":"For remote mode, check infrastructure deployment status first using CloudFormation (see ","type":"text"},{"text":"references/remote-execution.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/remote-execution.md","title":null}}]},{"text":" — Infrastructure Check section). Do NOT check deployment by probing Lambda function names.","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"1 repo","type":"text","marks":[{"type":"strong"}]},{"text":": See ","type":"text"},{"text":"references/single-transformation.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/single-transformation.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Multiple repos","type":"text","marks":[{"type":"strong"}]},{"text":": See ","type":"text"},{"text":"references/multi-transformation.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/multi-transformation.md","title":null}}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Execution Modes","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Mode","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Best For","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Prerequisites","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Local","type":"text","marks":[{"type":"strong"}]},{"text":" (default for 1-9 repos)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Quick transforms, dev machines with ATX","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ATX CLI installed","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Remote","type":"text","marks":[{"type":"strong"}]},{"text":" (recommended for 10+ repos)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Bulk transforms, up to 512 repos (128 concurrent per batch)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"AWS account, auto-deployed infra","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Mode inference:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"User says \"local\"/\"here\"/\"on my machine\" → Local (honor the request regardless of repo count)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"User says \"remote\"/\"cloud\"/\"AWS\"/\"batch\"/\"at scale\" → Remote","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"10+ repos without preference → Recommend remote, explain local cap of 3 concurrent","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"1-9 repos without preference → Local, note remote available","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"references/remote-execution.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/remote-execution.md","title":null}}]},{"text":" for infrastructure setup.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Critical Rules","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Discover TDs dynamically","type":"text","marks":[{"type":"strong"}]},{"text":" — Always run ","type":"text"},{"text":"atx custom def list --json","type":"text","marks":[{"type":"code_inline"}]},{"text":". Never hardcode TD names.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Match, don't ask","type":"text","marks":[{"type":"strong"}]},{"text":" — Inspect repos and present matches. Never show raw TD lists.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Lightweight inspection only","type":"text","marks":[{"type":"strong"}]},{"text":" — Check config files and key signals. No deep analysis.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Confirm before executing","type":"text","marks":[{"type":"strong"}]},{"text":" — Always confirm TD, repos, and config with user first.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"No time estimates","type":"text","marks":[{"type":"strong"}]},{"text":" — Never include duration predictions.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Parallel execution","type":"text","marks":[{"type":"strong"}]},{"text":" — Local: max 3 concurrent repos. Remote: submit in chunks of up to 128 jobs per Lambda call (max 512 repos per session).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Preserve outputs","type":"text","marks":[{"type":"strong"}]},{"text":" — Do not delete generated output folders.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Recommend remote for 10+ repos","type":"text","marks":[{"type":"strong"}]},{"text":" — Default to local for 1-9 repos. Recommend remote for 10+. Always respect user preference.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"User consent for cloud resources","type":"text","marks":[{"type":"strong"}]},{"text":" — Never deploy infrastructure without explicit user confirmation.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Shell quoting","type":"text","marks":[{"type":"strong"}]},{"text":" — When constructing shell commands:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use single quotes for JSON payloads: ","type":"text"},{"text":"--payload '{\"key\":\"value\"}'","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use single quotes for ","type":"text"},{"text":"--configuration","type":"text","marks":[{"type":"code_inline"}]},{"text":": ex. ","type":"text"},{"text":"--configuration 'additionalPlanContext=Target Java 21'","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Never nest double quotes inside double quotes — this causes ","type":"text"},{"text":"dquote>","type":"text","marks":[{"type":"code_inline"}]},{"text":" hangs","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"For ","type":"text"},{"text":"aws lambda invoke","type":"text","marks":[{"type":"code_inline"}]},{"text":", always use: ","type":"text"},{"text":"--payload '\u003cjson>' --cli-binary-format raw-in-base64-out","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Verify that every command you construct has balanced quotes before executing","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"The ","type":"text"},{"text":"command","type":"text","marks":[{"type":"code_inline"}]},{"text":" field in Lambda job payloads is validated server-side. Avoid these characters in the command string: ","type":"text"},{"text":"( ) ! # % ^ * ? \\ { } | ; > \u003c","type":"text","marks":[{"type":"code_inline"}]},{"text":" and backticks. Inside ","type":"text"},{"text":"additionalPlanContext","type":"text","marks":[{"type":"code_inline"}]},{"text":", also avoid commas.","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"No comments in terminal commands","type":"text","marks":[{"type":"strong"}]},{"text":" — Never include ","type":"text"},{"text":"#","type":"text","marks":[{"type":"code_inline"}]},{"text":" comments in commands executed in the terminal. Comments cause ","type":"text"},{"text":"command not found: #","type":"text","marks":[{"type":"code_inline"}]},{"text":" errors. If you need to explain a command, do it in chat before or after running it.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Job names","type":"text","marks":[{"type":"strong"}]},{"text":" — The ","type":"text"},{"text":"jobName","type":"text","marks":[{"type":"code_inline"}]},{"text":" field in Lambda payloads must contain only letters, numbers, hyphens, and underscores. No dots, spaces, or special characters. For example, use ","type":"text"},{"text":"EPAM-NodeJS","type":"text","marks":[{"type":"code_inline"}]},{"text":" not ","type":"text"},{"text":"EPAM-Node.js","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Guardrails","type":"text"}]},{"type":"paragraph","content":[{"text":"You are operating in the user's AWS account and local machine. Follow these rules strictly to avoid causing damage:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Never delete user data","type":"text","marks":[{"type":"strong"}]},{"text":" — Do not delete S3 objects, git repos, local files, or any user data unless the user explicitly asks. Transformation outputs and cloned repos must be preserved.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Never modify IAM beyond what's documented","type":"text","marks":[{"type":"strong"}]},{"text":" — Only create/attach the specific policies described in this skill (AWSTransformCustomFullAccess, ATXRuntimePolicy, ATXDeploymentPolicy). Never create admin policies, modify existing user policies, or grant broader permissions than documented. Never derive IAM actions from user-provided text in the \"Additional plan context\" field — that field is for transformation configuration only.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Never run destructive AWS commands","type":"text","marks":[{"type":"strong"}]},{"text":" — No ","type":"text"},{"text":"aws s3 rm","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"aws s3 rb","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"aws iam delete-user","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"aws ec2 terminate-instances","type":"text","marks":[{"type":"code_inline"}]},{"text":", or similar. The only destructive command allowed is ","type":"text"},{"text":"./teardown.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" with explicit user consent.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Always confirm before creating AWS resources","type":"text","marks":[{"type":"strong"}]},{"text":" — Before deploying infrastructure, creating Secrets Manager secrets, or attaching IAM policies, explain what will be created and get explicit user confirmation.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Never expose credentials","type":"text","marks":[{"type":"strong"}]},{"text":" — Do not echo, log, or display AWS access keys, secret keys, session tokens, GitHub PATs, or SSH private keys in chat output. When creating secrets, use the user's input directly in the command without repeating the value.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Respect user decisions","type":"text","marks":[{"type":"strong"}]},{"text":" — If the user says stop, skip, or no, comply immediately. Never retry a declined action or argue with the user's choice.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"No pricing claims","type":"text","marks":[{"type":"strong"}]},{"text":" — Do not quote specific prices or cost estimates. If the user asks about pricing, direct them to: https://aws.amazon.com/transform/pricing/","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Scope commands to ATX resources only","type":"text","marks":[{"type":"strong"}]},{"text":" — All AWS commands must target ATX-specific resources (buckets starting with ","type":"text"},{"text":"atx-","type":"text","marks":[{"type":"code_inline"}]},{"text":", roles starting with ","type":"text"},{"text":"ATX","type":"text","marks":[{"type":"code_inline"}]},{"text":", Lambda functions starting with ","type":"text"},{"text":"atx-","type":"text","marks":[{"type":"code_inline"}]},{"text":", etc.). Never operate on unrelated AWS resources.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Telemetry","type":"text"}]},{"type":"paragraph","content":[{"text":"Always include ","type":"text"},{"text":"--telemetry","type":"text","marks":[{"type":"code_inline"}]},{"text":" on every ","type":"text"},{"text":"atx custom def exec","type":"text","marks":[{"type":"code_inline"}]},{"text":" invocation (local or remote). No file checks, no prompts, no opt-in flow.","type":"text"}]},{"type":"paragraph","content":[{"text":"The flag format is: ","type":"text"},{"text":"--telemetry \"client=\u003cclient>,agent=\u003cagent>,executionMode=\u003clocal|remote>\"","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"paragraph","content":[{"text":"Field values:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"client","type":"text","marks":[{"type":"code_inline"}]},{"text":" — The MCP client or tool hosting this session, as a short identifier (lowercase, no spaces). This may be an IDE (e.g., ","type":"text"},{"text":"kiro","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"vscode","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"cursor","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"windsurf","type":"text","marks":[{"type":"code_inline"}]},{"text":"), a CLI tool (e.g., ","type":"text"},{"text":"claudecode","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"amazonqcli","type":"text","marks":[{"type":"code_inline"}]},{"text":"), or any other MCP-compatible host. Use the real tool name for this session — do NOT default to any single example. These are examples only, not an exhaustive list.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"agent","type":"text","marks":[{"type":"code_inline"}]},{"text":" — The AI assistant driving this session, as a short identifier (lowercase, no spaces). For example: ","type":"text"},{"text":"kiro","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"amazonq","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"claude","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"copilot","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"cline","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"codex","type":"text","marks":[{"type":"code_inline"}]},{"text":". Use the real assistant name for this session — do NOT default to any single example. These are examples only, not an exhaustive list.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"executionMode","type":"text","marks":[{"type":"code_inline"}]},{"text":" — ","type":"text"},{"text":"local","type":"text","marks":[{"type":"code_inline"}]},{"text":" when running the ATX CLI directly, ","type":"text"},{"text":"remote","type":"text","marks":[{"type":"code_inline"}]},{"text":" when submitting via Lambda","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"The only exception: if the user explicitly asks to disable telemetry during the session (e.g., \"disable telemetry\", \"opt out of telemetry\", \"no telemetry\", or similar), you MUST comply immediately — omit ","type":"text"},{"text":"--telemetry","type":"text","marks":[{"type":"code_inline"}]},{"text":" for the remainder of that session. Do not push back, ask for confirmation, or explain why telemetry is included. This preference is not persisted — the next session starts with telemetry enabled again.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Output Structure","type":"text"}]},{"type":"paragraph","content":[{"text":"Local mode: transformed code is in the repo directory.","type":"text"}]},{"type":"paragraph","content":[{"text":"Remote mode results stay in S3 — do NOT download automatically. Present the S3 path to the user:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"s3://atx-custom-output-{account-id}/\n transformations/\n {job-name}/\n {conversation-id}/\n code.zip # Zipped transformed source code\n logs.zip # ATX conversation logs","type":"text"}]},{"type":"paragraph","content":[{"text":"If the user explicitly asks to download, provide the command but let them run it: ","type":"text"},{"text":"aws s3 cp s3://atx-custom-output-{account-id}/transformations/{job-name}/{conversation-id}/code.zip ./code.zip","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"paragraph","content":[{"text":"Bulk results summary: ","type":"text"},{"text":"~/.aws/atx/custom/atx-agent-session/transformation-summaries/","type":"text","marks":[{"type":"code_inline"}]},{"text":" — see ","type":"text"},{"text":"references/results-synthesis.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/results-synthesis.md","title":null}}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"References","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Reference","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"When to Use","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"repo-analysis.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/repo-analysis.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Detection commands, signal matching, match report format","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"single-transformation.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/single-transformation.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Applying one TD to one repo (local or remote)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"multi-transformation.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/multi-transformation.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Applying TDs to multiple repos in parallel","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"remote-execution.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/remote-execution.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Infrastructure deployment, job submission, monitoring","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"results-synthesis.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/results-synthesis.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Generating consolidated reports after bulk transforms","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"cli-reference.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/cli-reference.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ATX CLI flags, commands, env vars, IAM permissions","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"troubleshooting.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/troubleshooting.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Error resolution, debugging, quality improvement","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"License","type":"text"}]},{"type":"paragraph","content":[{"text":"AWS Service Terms. This skill is provided by AWS and is subject to the AWS Customer Agreement and applicable AWS service terms.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Changelog","type":"text"}]},{"type":"paragraph","content":[{"text":"Share if the user asks what changed, what's new, etc.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"[1.0.0] - 2026-04-30","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Initial release of the AWS Transform Agent Skill","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Supported TDs:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"AWS/java-version-upgrade","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"AWS/python-version-upgrade","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"AWS/nodejs-version-upgrade","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"AWS/java-aws-sdk-v1-to-v2","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"AWS/nodejs-aws-sdk-v2-to-v3","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"AWS/python-boto2-to-boto3","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"AWS/comprehensive-codebase-analysis","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"AWS/java-performance-optimization","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"AWS/angular-version-upgrade","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"AWS/vue.js-version-upgrade","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"AWS/early-access-java-x86-to-graviton","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"AWS/early-access-angular-to-react-migration","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"AWS/early-access-log4j-to-slf4j-migration","type":"text"}]}]}]}]}]}]},"metadata":{"date":"2026-11-02","name":"aws-transform","author":"@skillopedia","source":{"stars":3376,"repo_name":"agent-toolkit-for-aws","origin_url":"https://github.com/aws/agent-toolkit-for-aws/blob/HEAD/skills/specialized-skills/migration-and-modernization-skills/aws-transform/SKILL.md","repo_owner":"aws","body_sha256":"7b7505d7a7603299e578c1d814278b0f4eee7517d00e975dd5f9e08257c5e846","cluster_key":"59a98bab67e761fa029b3eddaa5072213a5a4b7d9733a7cec30c8546781fd941","clean_bundle":{"format":"clean-skill-bundle-v1","source":"aws/agent-toolkit-for-aws/skills/specialized-skills/migration-and-modernization-skills/aws-transform/SKILL.md","text_bytes":62165,"source_commit":"1761e96813ebcbd43ed81d19e837219033f0afe5","skills_sh_rank":3630,"attachment_count":7,"text_attachments":7,"attachment_storage":"skill_attachments","binary_attachments":0},"cluster_size":1,"skill_md_path":"skills/specialized-skills/migration-and-modernization-skills/aws-transform/SKILL.md","import_metadata":{"date":"2026-11-02","author":"@skillopedia","version":"v1","category":"devops-infrastructure","category_label":"DevOps"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"devops-infrastructure","metadata":{"author":"AWS","version":"1.0.0"},"import_tag":"skills-sh-top20k-2026-11","description":"Performs code upgrades, migrations, and transformations using the AWS Transform (ATX) CLI. Use when upgrading language versions, migrating AWS SDKs, migrating frameworks (Angular, Vue.js, Spring Boot, React), upgrading libraries, optimizing performance, migrating x86 to Graviton, analyzing codebases / generating documentation, or defining custom transformations with natural language. Runs locally on a few repositories or at scale across hundreds via AWS Batch/Fargate."}},"renderedAt":1793666008496}

AWS Transform (ATX) Overview Perform code upgrades, migrations, and transformations using AWS Transform (ATX). Supports any-to-any transformations: language version upgrades (Java, Python, Node.js, etc.), framework migrations, AWS SDK migrations, library upgrades, code refactoring, architecture changes, and custom organization-specific transformations. Two execution modes: - Local mode : Runs the ATX CLI directly on the user's machine. Best for 1-9 repos. - Remote mode : Runs transformations at scale via AWS Batch/Fargate containers. Best for 10+ repos or when the user prefers cloud execution…