AI refac
Reference

بِسْمِ ٱللَّهِ ٱلرَّحْمَـٰنِ ٱلرَّحِيمِ

Commands

Every refac command with its arguments, the rules per language, and examples.

Help comes in three depths, all printed from inside the binary, so it is there when this site is not:

refac move -h          # a few lines: what the command is and its flags
refac move --help      # everything about one command: arguments, rules, output, examples
refac guide            # in-depth topics: languages, safety, batching, kotlin, output, servers

refac --help is the whole tool on one page. This page gives you the same map with the details in reach.

CommandDoes
moveMoves or renames files and folders and rewrites every import, module line, package line, and Markdown link that pointed at them
move-moduleMoves a whole Rust module subtree and every crate:: path to it
renameRenames a symbol and every reference to it
doctorChecks the language servers refac starts and prints how to install a missing one
guidePrints in-depth documentation by topic
completions, manPrint shell completions and the manual page

move

refac move [OPTIONS] (--source-path <PATH>... --target-path <PATH>... | --batch <FILE>)
OptionMeaning
--project-path DIRThe package root of the language. Defaults to the current directory. Environment: REFAC_PROJECT_PATH
--source-path PATH...A file or folder to move, absolute or relative to --project-path. Repeat for several
--target-path PATH...Where it goes. A folder in the path that does not exist yet is created. The n-th source goes to the n-th target
--batch FILEThe moves as a JSON list instead of flags, - for stdin. Replaces --source-path and --target-path
--dry-runPlan the move, print what it would do, and change no file
--allow-singleKotlin only: let a single Kotlin change through, which is refused by default
--jsonPrint one JSON document instead of text

The --batch file holds [{"source_path": "a.kt", "target_path": "pkg/a.kt"}, ...]. Each entry has exactly those two keys.

Rules by language

LanguageRule
TypeScript / JavaScriptFolders can be moved. At most 30 source files per call. The tsconfig decides which files are callers
Kotlin / AndroidNeeds the Kotlin language server. Every call imports the Gradle build (about 24 s), so put all moves in one call. .java files, folders that contain Java, and moves between modules or source sets are refused
GoMoving a file to another folder moves the whole package. The output lists the files that moved with it
RustA .rs file can be renamed inside its folder. A cross-folder target is refused and points at move-module
PythonRope rewrites the imports, with Pyrefly as the fallback. Imports through an __init__.py re-export may be missed
Dartdart pub get must have run. A move that would leave a package: import pointing at a missing file is refused
MarkdownFiles, images, other assets, and folders. Code blocks, comments, and ignored files are left alone

Preview

--dry-run plans the same move and changes no file. Per language it prints the paths that would move, then one // <file> (N edits) line per file that would be edited, then the notes. It runs the same checks and refuses what the move refuses, with exit code 1 and the same report. Python (Rope) and Kotlin cannot plan without moving, so they run the real move on a throw-away copy of the project (limit 500 MiB, REFAC_DRY_RUN_COPY_MAX_MB) and report the difference.

move-module

refac move-module [OPTIONS] <SOURCE_MODULE> <TARGET_MODULE>
Argument or optionMeaning
SOURCE_MODULEThe module as it is now, starting with crate::
TARGET_MODULEThe module as it should be, starting with crate:: and in the same crate
--project-path DIRA Cargo package or workspace root. Defaults to the current directory
--dry-runPlan the move and change no file. It does not compile the result
--checkWith --dry-run: also run cargo check --workspace --all-targets on a throw-away copy of the workspace. It is refused without --dry-run
--jsonOne JSON document

See the Rust page for what is rewritten and refused.

rename

refac rename [OPTIONS]

The language comes from the extension of --file:

ExtensionsLanguageDone by
.ts .tsx .js .jsx .mts .cts .mjs .cjsTypeScript / JavaScriptOxc and the TypeScript native language server
.ktKotlin / JavaThe JetBrains Kotlin language server, plus Android XML
.goGogopls
.rsRustrust-analyzer
.pyPythonbasedpyright
.dartDartThe Dart SDK analysis server
OptionMeaning
--project-path DIRThe root the language server loads. Defaults to the current directory
--file FILEThe file that contains the symbol, absolute or relative to --project-path
--symbol NAMEThe current name, written exactly as in that file
--new-name NAMEThe new name. It must be a plain identifier and not a keyword of the language
--line N, --column NPick one symbol when the name means several in the file. --column is a 1-based byte column, like rg --column
--batch FILESeveral renames in one server session, all or nothing. A JSON list of file, symbol, new_name, and optional line and column. - is stdin
--dry-runPlan and prove the rename, print it, and change no file
--allow-singleKotlin only: let a single Kotlin change through
--jsonOne JSON document

Before anything is written

  1. The server plans the rename.
  2. refac proves it: the server is shown the renamed text in memory and must find exactly the places it found before. A new name that clashes with or shadows another declaration changes what a call means. That is refused, naming the places, and no file changes.
  3. Only then are the files written, through an undo journal.

Some names are refused before a server even starts: a name that is not a plain identifier, a keyword of the language, the current name itself, and for Python a special method such as __init__. The name of a module or package is refused with the hint to use refac move. A rename of a symbol that also lives in files outside the project is refused, because those packages would stop building.

When the name means several symbols in the file, nothing is renamed and the error lists every candidate with its line.

doctor

refac doctor              # one line per language, no server started
refac doctor <language>   # find, start, check, and stop one server

<language> is one of go, rust, python, dart, kotlin, or typescript. Without a language, doctor prints ready, missing, or broken for each one and returns at once. With a language it finds the server exactly as a command would, starts it, makes it answer the handshake, stops it, and prints the version, the path, and the numbered steps that fix what is wrong.

OptionMeaning
--project-path DIRThe project the server will work in. rustup picks the Rust toolchain from there, and Python its environment
--jsonA JSON list with one object per language

The exit code of refac doctor alone is 0. For refac doctor <language> it is 0 when the server is ready and 1 when it is missing or broken, so a script or an agent can branch on it.

guide

refac guide             # list the topics
refac guide languages   # one topic
refac guide all         # every topic
TopicAnswers
languagesWhat refac does in each language, and where it stops
safetyWhat is checked, what is written, and what happens when something fails
batchingWhat a command costs and how to do many changes with few server starts
kotlinWhy a Kotlin change is slow, what it costs, and the options you have
outputHow to read the report, the JSON documents, and the exit codes
serversWhich language servers are started, where they are found, and how to fix a missing one

completions and man

refac completions bash > ~/.local/share/bash-completion/completions/refac
refac man | man -l -

completions supports bash, elvish, fish, powershell, and zsh.

On this page