Skip to content
← Public packages

@kentcdodds/codemod-runner

Run codemods over your own saved packages: scan, dry-run with real publish checks and diffs, apply, and revert.

AGENTS.md

134 lines · 4.1 KB · Markdown

@kentcdodds/codemod-runner — agent notes

Human setup and intent live in README.md. This file is for agents: imports, smoke/dry-run snippets, and edge cases. No user secrets. Do not disable live webhooks or jobs.

Secrets

None.

Import paths

ExportImport
scankody:@kentcdodds/codemod-runner/scan
dry-runkody:@kentcdodds/codemod-runner/dry-run
applykody:@kentcdodds/codemod-runner/apply
revertkody:@kentcdodds/codemod-runner/revert
runskody:@kentcdodds/codemod-runner/runs
contractkody:@kentcdodds/codemod-runner/contract

Prefer static kody:@kentcdodds/codemod-runner/... imports from execute. Do not lead with packages.invoke.

Smoke / dryRun

Contract docs (no side effects):

import contract from 'kody:@kentcdodds/codemod-runner/contract'

export default async function main() {
	return contract()
	// => { exportName: 'codemod', input: '...', limits: {...} }
}

Ledger list (read-only):

import runs from 'kody:@kentcdodds/codemod-runner/runs'

export default async function main() {
	return await runs()
	// => { runs: [...] }
}

Canary dry-run (publishes nothing; needs a real codemod you own):

import dryRun from 'kody:@kentcdodds/codemod-runner/dry-run'

export default async function main() {
	return await dryRun({
		codemod: {
			kodyId: 'my-codemod',
			specifier: 'kody:@your-username/my-codemod',
		},
		packageIds: ['one-kody-id'],
		limit: 1,
	})
	// page with returned runId + cursor until nextCursor is null
}

Scan (read-only detect):

import scan from 'kody:@kentcdodds/codemod-runner/scan'

export default async function main() {
	return await scan({
		codemod: {
			kodyId: 'my-codemod',
			specifier: 'kody:@your-username/my-codemod',
		},
		packageIds: ['one-kody-id'],
		limit: 1,
	})
}

Apply / revert only after a completed dry-run; keep runId for revert:

import apply from 'kody:@kentcdodds/codemod-runner/apply'
import revert from 'kody:@kentcdodds/codemod-runner/revert'

export default async function main() {
	const result = await apply({
		codemod: {
			kodyId: 'my-codemod',
			specifier: 'kody:@your-username/my-codemod',
		},
		packageIds: ['one-kody-id'],
		limit: 1,
	})
	// later:
	// return await revert({ revertOfRunId: result.runId })
	return result
}

Edge cases

  • Every export is paged: keep calling with runId + cursor until nextCursor is null. Interactive MCP execute times out ~90s — do not try to finish a whole fleet in one call.
  • Drive large sweeps from workflows.create with an idempotencyKey on the outer create so client timeouts recover without double-starting.
  • Canary first: packageIds: ['one-kody-id']. Keep limit small (scan default 1 / max 2; dry-run/apply default 1 / max 2).
  • Explicit specifier must name the same kodyId under its actual owner — the runner never guesses the codemod owner from the caller.
  • Optional detectPattern (JS regex): zero repo_search hits → clean without reading the tree.
  • apply refuses until a dry-run for the same codemod completed. Transformed trees must pass real publish checks; non-idempotent transforms fail before write.
  • Drift: unpublished HEAD vs published commit → skip; revert skips packages republished after apply. Runner excludes itself and the codemod package.
  • Statuses: detected, clean, dry_run_ok, checks_failed, needs_manual, skipped_drift, skipped_unpublished, applied, reverted, failed. Runs are single-pass — start a new run to retry.
  • UTF-8 text only; empty files invisible; paths with whitespace cannot be deleted by a transform. Fix pre-existing check failures before apply.
  • When the runner is wrong for a simple string rewrite and sweeps keep timing out: batched repo_search discovery + packageGetGitRemote clone/edit/push
    • one-at-a-time packagePublishExternalPush. Poll dispatched publishes via workflowRunList; read static_dependents.stale — Kody does not republish dependents.
  • Substring migrations: confirm must-keep holdouts with the user; put classification in the codemod; re-scan after apply.