Read this when creating a new codemod package.
When drafting your plan, define:
- [MIGRATION TASK DESCRIPTION]: What the migration does (e.g. "Replace deprecated API X with new API Y", "Convert class components to function components", "Migrate from library A to library B").
- [OPTIONAL: SPECIFIC TRICKY CASES]: Patterns that need human intuition, domain knowledge, or deep codebase context — list these for the AI step. If the AST codemod can handle everything, omit.
codemods/<slug>/
├── codemod.yaml
├── workflow.yaml
├── SKILL.md
├── package.json
├── README.md
└── scripts/
└── codemod.ts
Use @jssg/utils for common transformations such as import manipulations.
Supported languages (ast-grep): TypeScript (ts, tsx), JavaScript (js, jsx), Angular (angular), Less (less), Python (py), Go (go), Rust (rs), Java (java), Ruby (rb), Kotlin (kotlin), Swift (swift), C/C++ (c, cpp), C# (cs), PHP (php), Scala (scala), Lua (lua), Elixir (ex), Haskell (hs), Bash (bash), HTML (html), CSS (css), JSON (json), YAML (yml), and more.
schema_version: "1.0"
name: "my-awesome-codemod"
version: "0.1.0"
description: "Transform legacy patterns to modern syntax"
author: "Your Name <you@example.com>"
license: "MIT"
workflow: "workflow.yaml"
category: "migration"
repository: "<URL to codemod source code, e.g. GitHub repo>"
targets:
languages: ["typescript"]
# Transformation type keywords/tags — pick one primary per codemod (avoid mixing breaking-change and feature-adoption):
# upgrade - upgrade code to newer versions (encompasses breaking changes and feature adoption)
# breaking-change - adapt to framework/library breaking API changes
# feature-adoption - adopt new optional or incremental features
# security - address known vulnerabilities or unsafe patterns
# cross-migration - replace one library/framework with another
# i18n - internationalization migrations or improvements
# a11y - accessibility improvements and compliance
# standardization - unify patterns, conventions, or APIs across a codebase
# code-mining - detect-only: identify, flag, or extract patterns without transforming
keywords: ["upgrade", "breaking-change", "react", "v17-to-v18"]
registry:
access: "public"
visibility: "public"IMPORTANT (agents): New codemods MUST implement the full node structure below:
apply-transforms(js-ast-grep and optional Commit steps),ai-tricky-cases(when the migration has tricky cases), andpublish. Include theparamsblock. Codemods run on the current branch.
Schema comment (first line):
# yaml-language-server: $schema=https://raw.githubusercontent.com/codemod/codemod/refs/heads/main/schemas/workflow.jsonParams schema — each param must have name, description, type, and default where applicable:
| Param | Type | Default | Notes |
|---|---|---|---|
| commit_per_step | boolean | false | Commit after each change-producing step with a meaningful message |
| run_ai_step | boolean | false | Run AI step for tricky cases — only add if AI step is needed |
| publish_pr | boolean | false | Push branch and create PR (when true; agents must not push by default) |
| main_branch | string | "main" | Target branch for PR |
| api_token | string | — | secret: true, for GitHub API when gh CLI unavailable |
| pr_title | string | — | Optional |
| pr_body | string | — | Optional, multi_line: true |
Node structure: Implement all three nodes below at minimum. Omit only the AI step (inside ai-tricky-cases) when all patterns can be handled by the AST codemod; keep the ai-tricky-cases node with depends_on. Include all steps (Commit, Push, Create PR) — they are gated by params. Do not push to remote by default — the Push step must use if: params.publish_pr. Commit steps use if: params.commit_per_step and must have task-specific, meaningful messages (e.g. fix: migrate X to Y, fix: AI-assisted resolution of [case]). If the migration can be divided into multiple shippable PRs, add a dedicated node for each part.
- Step 1: "[Task-specific name]" —
js-ast-grep:js_file: scripts/codemod.tslanguage: "<target_lang>"— use ast-grep alias (e.g.tsx,python,go,rust)semantic_analysis: workspace— required when the codemod must verify symbol definitions, trace imports, or update references across files. Without it,node.definition()andnode.references()return no-op. Use Codemod MCPget_jssg_instructionsfor Part 4: Semantic Analysis.include: globs for target files (e.g.**/*.tsx,**/*.py,**/*.go,**/*.rs)exclude:**/node_modules/**,**/vendor/**,**/*.test.*,**/*.spec.*,**/__pycache__/**(adapt to language)
- Step 2: "Commit AST transformations" —
if: params.commit_per_step, run:git add -A [ -n "$(git status --porcelain)" ] && git commit -m "fix: [task-specific meaningful message]" || true
- Step 1: AI step —
if: params.run_ai_step, with:- prompt: Opening "You are performing a [domain] code migration. Modify files directly." + Goal, Handle these tricky cases (1–6 from [SPECIFIC TRICKY CASES]), The fix, Constraints (preserve behavior, keep formatting, add TODO if unsure)
- system_prompt: Domain expert persona + migration-specific guidance
- Step 2: "Commit AI fixes" —
if: params.commit_per_step, run:git add -A [ -n "$(git status --porcelain)" ] && git commit -m "fix: AI-assisted resolution of [tricky-case summary]" || true
- Step 1: "Push branch to remote" —
if: params.publish_pr, run:BRANCH=$(git branch --show-current) git push -u origin "$BRANCH"
- Step 2: "Create PR to main branch" —
if: params.publish_pr, run:BRANCH=$(git branch --show-current) BASE="${PARAM_MAIN_BRANCH:-main}" TITLE="${PARAM_PR_TITLE:-fix: [task-specific default title]}" BODY="${PARAM_PR_BODY:-Automated codemod: [task-specific default body].}" [ -n "$PARAM_API_TOKEN" ] && export GITHUB_TOKEN="$PARAM_API_TOKEN" gh pr create --base "$BASE" --head "$BRANCH" --title "$TITLE" --body "$BODY"
- Use
codemod:ast-greptypes:Edit,SgNode,Transform, and the language module (e.g.codemod:ast-grep/langs/tsx,codemod:ast-grep/langs/python,codemod:ast-grep/langs/go) - Import
useMetricAtomfromcodemod:metrics - Metrics: Prefer one metric with cardinalities. Choose a descriptive name for the migration.
- Cardinality definitions (at top of script):
// Cardinalities: // change-type: "deterministic" | "human-or-ai" // change-difficulty: "simple" | "hard"
- When to use cardinalities (only if they make sense):
- change-type (
deterministic|human-or-ai): When workflow has AST + optional AI step. Omit if all changes are deterministic. - change-difficulty (
simple|hard): When there's a mix of straightforward vs complex transforms. Omit if all are similar. - file:
file: root.filename()for matched pattern - You can add domain-specific cardinalities beyond these.
- change-type (
- Symbol verification: When the codemod must distinguish symbols by origin (e.g., only transform
useStatefrom React, not local variables) or update definitions and references across files, usenode.definition()andnode.references(). Enablesemantic_analysis: workspacein the workflow. Get full instructions via Codemod MCPget_jssg_instructions. - Export default
transformfunction.
{
"name": "<slug>",
"version": "0.1.0",
"description": "[one-line description]",
"type": "module",
"scripts": {
"test": "npx codemod@latest jssg test -l <target_lang> ./scripts/codemod.ts",
"check-types": "tsc --noEmit"
},
"devDependencies": {
"@codemod.com/jssg-types": "latest",
"@jssg/utils": "latest",
"typescript": "latest"
}
}Use -l <target_lang> with ast-grep alias: tsx, typescript, python, go, rust, java, etc.
Include: Problem, Solution, Usage (npx codemod workflow run -w codemods/<slug>/workflow.yaml -t /path/to/project), Params, Metrics, Tricky Cases (when to use run_ai_step=true), What Gets Fixed (automated vs human/AI).
- include: Match by extension (e.g.
**/*.tsx,**/*.py,**/*.go,**/*.rs,**/*.java,**/*.rb) - exclude:
**/node_modules/**,**/vendor/**,**/__pycache__/**,**/*.test.*,**/*.spec.*(adapt to language)
Run npx codemod workflow validate codemods/<slug>/workflow.yaml before committing.
Create SKILL.md at the package root to make the codemod Agent Skill compatible. SKILL.md provides operational instructions for AI agents; it does not describe the migration itself (that lives in workflow steps and scripts).
Frontmatter: The YAML frontmatter at the top must be an exact copy of codemod.yaml. No invented fields, no deviations.
Body requirements:
- Read the workflow graph: Parse
workflow.yamlnodes anddepends_onto understand execution order (e.g. apply-transforms → ai-tricky-cases → publish). - Minimal adaptation: How to adjust
include/excludeglobs orscripts/codemod.tslogic based on user code patterns. - Step-by-step execution: Run steps in topological order (respect
depends_on). After each step: validate outputs (npm test,npm run check-types,npx codemod workflow validate workflow.yaml), review diffs, then proceed. - AI steps: Treat the AI step prompt as context for edge cases that AST transforms cannot handle. Apply AI steps only where indicated by the workflow (
run_ai_step=true). Keep changes localized and reviewable. - Explicit exclusions: Do not include a high-level "description of changes" or migration overview. Focus on: understand, tweak, run, adapt.
Acceptance criteria:
- SKILL.md exists in package root
- Frontmatter faithfully reflects
codemod.yaml - Body explains workflow graph reading, minimal adaptation, safe execution, and AI step usage
- No redundant "description of changes" section