Codemod Workflows are self-hostable automations designed for running large-scale code transformation jobs.

Core Features

  • Single binary, no server — works anywhere you have a shell
  • Schema-validated shared state — tasks share one JSON document
  • Dynamic matrix fan-out — tasks appear/disappear as state arrays change
  • Manual gates — pause tasks until you trigger them
  • Durable & resumable — state survives crashes or reboots
  • Parallel scheduling — independent nodes run when dependencies allow
  • Host-shell execution — commands run directly on your machine (container runtimes on the roadmap)

Quick Start

1

Install

cargo install codemod-cli
2

Create a minimal workflow

workflow.yaml
version: "1"
nodes:
  - id: hello
    name: Hello World
    type: automatic
    steps:
      - name: Say hello
        run: echo "Hello, World!"
3

Validate & run

codemod@next validate -w workflow.yaml
codemod@next run      -w workflow.yaml
codemod@next run      ./my-workflow/   # run a bundle folder

Registry identifiers such as my-registry/react-mods:latest are on the roadmap.


Directory Layout

my-workflow/
├─ workflow.yaml
├─ scripts/
└─ rules/

The folder—called a workflow bundle—is the root when you run codemod@next run ./my-workflow/. $CODEMOD_PATH points here inside every task.

Workflow File

workflow.yaml
version: "1"
state:
  schema: []
templates: []
nodes: []

A workflow has four top-level keys:

KeyRequiredPurpose
versionDeclare workflow schema version (default: "1").
stateDeclares shared-state schema.
templatesRe-usable blocks.
nodesExecutable DAG.

Shared State

state:
  schema:
    - name: shards
      type: array
      items:
        type: object
        properties:
          team:    { type: string }
          shardId: { type: string }

Templates

templates:
  - id: checkout-repo
    name: Checkout Repository
    inputs:
      - name: repo_url
        type: string
        required: true
    steps:
      - name: Clone
        run: git clone ${{inputs.repo_url}} repo

Template Inputs & Usage:

Templates can define required or optional inputs, which are referenced in their steps.

To use a template in a node step:

steps:
  - name: Checkout
    uses:
      - template: checkout-repo
        inputs:
          repo_url: ${{params.repo_url}}

Nodes & Steps

Nodes

nodes:
  - id: build
    name: Build
    type: automatic
    steps:
      - name: npm install
        run: npm ci
id
string
required

Unique within the workflow.

name
string
required

Display name.

type
string
required

automatic (default) or manual.

depends_on
string[]

Upstream node IDs.

trigger
object

{ type: manual } → approval gate.

strategy
object

Matrix configuration.

steps
array
required

Ordered list of steps.

runtime
object
Container/runtime configuration (e.g., Docker).
env
object
Environment variables for the node or step.

Step

name
string
required

Step label.

run
string

Inline shell command to execute.


Provide either run or uses, not both.
uses
object

Template call(s).


Provide either run or uses, not both.

Matrix Strategy

nodes:
  - id: matrix-codemod
    name: Matrix Codemod
    strategy:
      type: matrix
      from_state: shards
    steps:
      - name: Codemod
        run: node codemod.js --team=$team --shard=$shardId

Manual Trigger

nodes:
  - id: manual-approval
    name: Manual Approval
    trigger:
      type: manual
    steps:
      - name: Wait for approval
        run: echo "Waiting for manual approval"

State Updates

SyntaxMeaningExample
KEY=VALSet state key to valuecount=10
KEY@=VALAppend value to array at state keyshards@={"team":"core","shardId":"1"}
Dot notationSet nested state fieldsconfig.retries=5
JSON valuesUse valid JSON for objects/arraysuser={"name":"Alice","id":123}

All state updates must be valid JSON if not a primitive. Updates are applied only if the task exits successfully.


End-to-End Example

version: "1"
state:
  schema:
    - name: shards
      type: array
      items:
        type: object
        properties:
          team: { type: string }
          shardId: { type: string }
templates:
  - id: checkout-repo
    name: Checkout Repository
    inputs:
      - name: repo_url
        type: string
        required: true
    steps:
      - name: Clone
        run: git clone ${{inputs.repo_url}} repo
nodes:
  - id: make-shards
    name: Make Shards
    type: automatic
    steps:
      - name: Write shards
        run: echo 'shards@={"team":"core","shardId":"1"}' >> "$STATE_OUTPUTS"
  - id: matrix-codemod
    name: Matrix Codemod
    strategy:
      type: matrix
      from_state: shards
    trigger:
      type: manual
    steps:
      - name: Codemod
        run: node codemod.js --team=$team --shard=$shardId
      - name: PR
        run: codemodctl pr create

Task Statuses

Pending

Queued; waiting for runner.

Running

Currently executing.

Completed

Succeeded; diff applied.

Failed

Script exited non-zero; diff discarded.

AwaitingTrigger

Waiting for manual approval.

Blocked

Dependencies not finished.

WontDo

Matrix item removed; task skipped.

Variable Resolution

  • Parameter: ${{params.branch}} — Supplied at runtime
  • Environment: ${{env.CI}} — Host env var
  • Shared State: ${{state.counter}} — Live JSON value

In matrix tasks, each object key becomes an environment variable (e.g., $team, $shardId, …).


CLI Commands

Codemod CLI is accessible using the codemod@next command. Please note that once the legacy Codemod CLI is deprecated, using the @next specifier will no longer be required.

codemod@next init

Initialize a new Codemod workflow project with interactive setup. Scaffolds rules, scripts, and tests for AST-grep or blank workflows.

--name <NAME>
string

Set the project name (defaults to directory name).

--force
boolean

Overwrite existing files if they exist.

[directory]
string

Target directory path (defaults to current directory).

codemod@next run

Execute a workflow from a file, bundle directory, or registry.

-w, --workflow <FILE>
string

Path to the workflow definition file.

[file|directory|registry]
string

Path to workflow file, bundle directory, or registry identifier.

codemod@next resume

Resume a paused workflow or trigger manual tasks.

-i, --id <UUID>
string
required

Workflow run ID to resume.

-t, --task <UUID>
string

Specific task UUID to trigger.

--trigger-all
boolean

Trigger all tasks in AwaitingTrigger state.

codemod@next validate

Validate a workflow definition or bundle without executing it. Checks for schema errors, cyclic dependencies, and more.

-w, --workflow <FILE>
string

Path to the workflow definition file.

[directory]
string

Path to workflow bundle directory.

codemod@next validate catches issues before execution:

CheckEnsures
Schema validationYAML matches spec
Unique IDsNode & template IDs unique
Dependency validationEvery depends_on exists
Cyclic dependency detectionDAG has no cycles
Template referencesAll template: IDs exist
Matrix validationfrom_state matches schema
State schema validationstate.schema is valid
Variable syntax${{…}} uses params, env, state

codemod@next login

Authenticate with a Codemod registry to publish and manage codemods.

--registry <URL>
string

Registry URL (defaults to official registry).

--token <TOKEN>
string

Authentication token (for non-interactive login, e.g., CI/CD).

--username <USERNAME>
string

Username for interactive login.

--scope <SCOPE>
string

Organization or user scope for publishing.

codemod@next publish

Publish a codemod to a registry for sharing and reuse.

--version <VERSION>
string

Explicit version (overrides manifest version).

--registry <URL>
string

Target registry URL.

--tag <TAG>
string

Tag for the release (e.g., beta, latest).

--access <LEVEL>
string

Access level (public or private).

--dry-run
boolean

Validate and pack without uploading.

--force
boolean

Override existing version (use with caution).

[directory]
string

Path to the codemod directory to publish (defaults to current directory).

codemod@next graph

Render DAG image.

-w <file>
string
required

Path to the workflow file.

-o <png>
string
required

Output PNG file for the DAG image.

Roadmap

Container runtime support

Support for runtime: docker and other container runtimes, allowing tasks to run in isolated environments.

Parameter flags

Ability to pass parameters to workflows via —param key=value flags.

Nested matrix strategies

Support for matrix strategies within matrix strategies, enabling more complex task fan-out.