Ast-Guided Llm Edits: LLMs Propose Intents and Codemods Apply Them

AST-Guided Edits: LLMs Propose Intents and Codemods Apply Them
16 views

This guide covers AST-guided LLM edits with production-focused examples and trade-offs.

Natural-language patches drift: off-by-one hunks, broken JSX, imports the model invented. Flip the contract — the LLM emits a structured transform intent; jscodeshift or ts-morph applies it. Mechanical precision stays in the codemod; the model only chooses what to rename, wrap, or migrate.
⚡ TL;DR: Define a JSON schema for intents (rename_symbol, wrap_call, replace_import). Validate with Ajv; execute via ts-morph/jscodeshift; typecheck before commit. Reject freeform unified diffs for mechanical migrations. Pair with Structured Outputs for Codegen and LLM Output Validators.

Intent schema — AST-guided LLM edits

json
{
  "$id": "https://acme.dev/codemod-intent.json",
  "type": "object",
  "required": ["kind", "target"],
  "properties": {
    "kind": { "enum": ["rename_symbol", "replace_import", "wrap_call", "add_jsdoc"] },
    "target": {
      "type": "object",
      "required": ["path", "exportName"],
      "properties": {
        "path": { "type": "string" },
        "exportName": { "type": "string" }
      }
    },
    "payload": { "type": "object" }
  }
}
typescript
// intents/rename.ts
import { z } from "zod";

export const RenameIntent = z.object({
  kind: z.literal("rename_symbol"),
  target: z.object({ path: z.string(), exportName: z.string() }),
  payload: z.object({ to: z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/) }),
});

Apply with ts-morph

typescript
import { Project } from "ts-morph";
import { RenameIntent } from "./intents/rename";

export function applyRename(raw: unknown) {
  const intent = RenameIntent.parse(raw); // ❌ never apply unparsed model JSON
  const project = new Project({ tsConfigFilePath: "tsconfig.json" });
  const file = project.getSourceFileOrThrow(intent.target.path);
  const exported = file.getExportedDeclarations().get(intent.target.exportName);
  if (!exported?.length) throw new Error("symbol_not_found");

  for (const decl of exported) {
    // ts-morph rename updates references in the project graph
    if ("rename" in decl) (decl as any).rename(intent.payload.to);
  }
  project.saveSync();
}
javascript
// jscodeshift example for import replacement
export default function transformer(file, api, opts) {
  const j = api.jscodeshift;
  const root = j(file.source);
  root.find(j.ImportDeclaration, { source: { value: opts.from } })
    .forEach((p) => { p.value.source.value = opts.to; });
  return root.toSource();
}

Agent loop

  1. Model proposes intent JSON (structured output / tool).
  2. Ajv/Zod validate → deny unknown kinds.
  3. Codemod dry-run → diff.
  4. tsc --noEmit + unit tests on affected packages.
  5. Human review for kinds marked high_risk (public API renames).
Same gate philosophy as Cursor Agent Mode CI survival.

When freeform patches are still OK

Small non-mechanical edits (prose, one-off logic) can use hunks — but migrations across N files should be intents. Hybrid: model writes intent for 90% and a tiny freeform patch for the residual, each validated separately.

Closing checklist

✅ Dos
– ✅ Schema-validate every intent
– ✅ Apply via AST tools, not string replace
– ✅ Typecheck + tests after apply
– ✅ Dry-run diffs in the PR body
– ✅ Allowlist intent kinds in production agents ❌ Don’ts
– ❌ Don’t eval model-produced codemod source
– ❌ Don’t regex-rename symbols across the monorepo
– ❌ Don’t skip reference updates (exports only)
– ❌ Don’t accept intents that touch node_modules or generated/

Related reading

Last updated on September 11, 2026

Related: OpenTelemetry for LLMs: Trace Prompt Latency Across Microservices · Day 57: Coding Agents That Only Emit Intents · Day 69: Policy as Code for Shell and Apply

Related: Cursor Rules for TypeScript Monorepos: Make AI Edits Stick · Node SDK Generation: OpenAPI-Driven Clients LLMs Cannot Hallucinate · AI-Written Terraform: Plan Diff Gates Before Any Apply Runs

Watch: 100 Days of AI on YouTube

Short videos from CheatCoders, one AI engineering topic per day.

Open the playlist on YouTube · CheatCoders on YouTube

Deep-dive PDF

Get the expanded guide for this post — extra diagrams-style checklists, failure modes, and a production walkthrough. Free when you subscribe to CheatCoders.

Already subscribed? or open the subscribe page.


Discover more from CheatCoders

Subscribe to get the latest posts sent to your email.

Comments

No comments yet. Why don’t you start the discussion?

Leave a comment

No account needed. Name and email are optional.