<!-- Markdown mirror. Canonical: https://empryo.com/docs/tools/ast-edit -->

# AST editing

> How Empryo edits TypeScript and JavaScript by symbol name instead of by matching text, so edits don't fail on whitespace or drift with line numbers.

When Empryo edits a TypeScript or JavaScript file, it changes the code by symbol, not by searching for text. It says "set the return type of `fetchUser`" instead of "find these exact three lines and replace them". Small changes stay small, and an edit can't fail because of whitespace, quotes or line endings.

The tool that does this is called `ast_edit`. AST stands for abstract syntax tree, the structured form of your code that the TypeScript compiler builds. You never call `ast_edit` yourself. Empryo uses it automatically for TypeScript and JavaScript files, and this page is a reference for what it can do.

In a project with no TypeScript or JavaScript files, such as a Go or Rust repository, Empryo loads `ast_edit` on demand instead of sending it with every request. The model can still pull it in when it meets a stray `.ts` file. Empryo decides when a tab starts, from the files git tracks plus new files that are not ignored, or from a scan of the folder when it is not a git repository. The decision is saved with the session, so a resumed session keeps the same tools and its prompt cache.

## Where the idea comes from

The approach grew out of a Master's thesis in Computer Science, [*Typed vs Untyped Programming Languages*](https://www.diva-portal.org/smash/record.jsf?pid=diva2%3A1690910&dswid=-6127) (Ouail Bni and Artur Matusiak, 2022), and its reference implementation [JS Typer](https://github.com/proxysoul/Javascript-Typer). The thesis showed that converting JavaScript to TypeScript could be done entirely by editing the syntax tree rather than the text. `ast_edit` applies the same idea to AI coding.

## Examples

Change a function's return type:

```ts
ast_edit({
  path: "src/api.ts",
  action: "set_return_type",
  target: "function",
  name: "fetchUser",
  value: "Promise",
})
```

Make a function async, add a parameter and add an import, as one batch:

```ts
ast_edit({
  path: "src/api.ts",
  operations: [
    { action: "set_async", target: "function", name: "fetchUser", value: "true" },
    { action: "add_parameter", target: "function", name: "fetchUser", value: "cache: boolean" },
    { action: "add_named_import", value: "./types", newCode: "User" },
  ],
})
```

Rename a class method:

```ts
ast_edit({
  path: "src/store.ts",
  action: "rename",
  target: "method",
  name: "UserStore.fetch",
  value: "load",
})
```

Replace a whole function body:

```ts
ast_edit({
  path: "src/util.ts",
  action: "set_body",
  target: "function",
  name: "parseInput",
  newCode: "return JSON.parse(raw);",
})
```

A batch is all-or-nothing. If one operation fails, none of them are applied.

## Targets

You point an edit at a symbol by its kind and name.

| Target | Addressed by |
|--------|-------------|
| `function`, `class`, `interface`, `type`, `enum`, `variable`, `constant` | `name` |
| `method`, `property` | `"ClassName.member"` or `"member"` |
| `arrow_function` | `name` (for `const fn = () => {}`) |
| `constructor` | `ClassName` |

Anonymous code, such as an inline callback, can't be targeted directly. Empryo edits the named symbol that contains it instead.

## Action reference

**Small edits**

| Action | Effect |
|--------|--------|
| `set_type` | Type annotation of a variable, property or parameter |
| `set_return_type` | Function return type |
| `set_initializer` / `remove_initializer` | Variable initializer |
| `set_value` | Enum member value |
| `set_async` / `set_generator` | Toggle `async` / `function*` |
| `set_export` / `set_default_export` | Toggle export modifiers |
| `set_abstract` / `set_static` / `set_readonly` / `set_scope` / `set_optional` / `set_overrides` / `set_ambient` | Class member modifiers |
| `set_const_enum` / `set_declaration_kind` | Other modifiers |
| `rename` | Rename the declaration only |
| `remove` | Delete the declaration |
| `add_parameter` / `remove_parameter` | Function and method parameters |

**Body and member edits**

| Action | Effect |
|--------|--------|
| `set_body` | Replace a function or method body |
| `add_statement` / `insert_statement` / `remove_statement` | Individual statements |
| `add_property` / `remove_property` | Interface and class properties |
| `add_method` / `remove_method` / `add_constructor` / `add_getter` / `add_setter` | Class members |
| `add_member` / `remove_member` | Enum and type-literal members |
| `add_decorator` / `remove_decorator` | Decorators |
| `add_overload` | Function overload signatures |
| `set_extends` / `add_extends` / `remove_extends` / `add_implements` / `remove_implements` | Inheritance |
| `add_type_parameter` | Generic type parameters |
| `add_jsdoc` / `remove_jsdoc` | JSDoc comments |
| `unwrap`, `set_structure`, `extract_interface` | Structural rewrites |

**Whole-symbol replacement**

| Action | Effect |
|--------|--------|
| `replace` | Replace a whole symbol |
| `replace_in_body` | Replace text inside a named symbol, found by a unique anchor or a start/end anchor pair |

**File-level**

| Action | Effect |
|--------|--------|
| `create_file` | Create a new file (`newCode` is the content) |
| `add_imports` / `add_import` / `remove_import` / `add_named_import` / `remove_named_import` | Imports |
| `set_module_specifier` | Change an import path |
| `add_export_declaration` / `add_named_reexport` | Re-exports |
| `add_namespace` | Namespace declarations |
| `organize_imports` / `fix_missing_imports` / `fix_unused` | Cleanup |
| `add_function` / `add_class` / `add_interface` / `add_type_alias` / `add_enum` / `add_variable` | Add top-level declarations |
| `insert_text` | Insert text at an anchor (`"after-imports"`, `"before-exports"`, or an index) |
| `rename_global` | Rename a symbol across the project |

## Supported files

`.ts`, `.tsx`, `.js`, `.jsx`, `.mts`, `.cts`, `.mjs`, `.cjs`. For every other file type, and for files with a syntax error, Empryo uses its regular text-based edit tools.

## Safety

- Batches are atomic: all operations apply, or none do.
- `undo_edit` reverses an `ast_edit` change the same way it reverses any other edit.
- After an edit, new type errors are reported back to the agent so it can fix them in the same turn.
- Files blocked by your `/privacy` patterns can't be edited.
