- MovePlan gains moves[] (file move = 1 relocation), left_behind[] for unindexable files that deliberately stay, prune_dirs[] applied after the batch (remove_dir only succeeds when empty, leftovers keep their home) - plan::dir: nesting/target-exists guards, merged per-file rewrites - apply: atomic N-rename batch (per-file git mv), rollback restores every move, rewrite and created dir newest-first - mv_reject accepts dirs; --json adds moved_files/would_move_files only for real dir moves (single-file contract byte-identical); summary shows (N files) and left-behind count - engine rollback/prune tests moved to tests/apply.rs (public API; a mid-batch failure cannot be forced through the CLI), java-rule e2e split into its own binary to keep every file under the 250-line budget
3 KiB
jmove
Move source files. Keep every import intact.
jmove is a fast, single-binary CLI that moves/renames source files in a
project and rewrites all import statements that reference them — no IDE,
no language server, no runtime dependencies. Built for humans and AI
agents.
Why
git mv moves the file but breaks every relative import pointing at it.
IDE renames fix imports but need an IDE. Existing CLI alternatives either
shell out to heavy language servers (bun/ts-morph, Rope, gopls,
rust-analyzer) or refuse to preview what they will change.
jmove's answer:
- Dry-run + unified diff — see every change before it hits disk.
- Atomic apply with rollback — imports are rewritten first, the file moves last; any failure restores everything.
- AI-agent mode —
--jsoneverywhere, stable error codes, exit codes, and a machine-readable skill doc (docs/SKILL.md). - Pure Rust, one binary — no external toolchains to install.
- Java support — the package/directory coupling nobody else automates outside IntelliJ.
Install
# From a clone of this repo (installs `jmove` into ~/.cargo/bin, on PATH):
cargo install --path . --locked
# Latest published version (once jmove is on crates.io — not yet published):
cargo install jmove
Use as a library (the CLI and the engine are separate targets; jmove::core
holds indexing/planning/apply, jmove::parser the language frontends):
[dependencies]
jmove = { git = "https://github.com/loki5512344/jmove" }
# or, for a local checkout: jmove = { path = "../jmove" }
Usage
# Preview what a move would change
jmove mv src/utils/parser.ts src/core/parser.ts --dry-run
# Apply it
jmove mv src/utils/parser.ts src/core/parser.ts
# Inside a git repo a tracked file moves via `git mv` (staged, history kept);
# --no-git forces a plain rename
jmove mv src/foo.ts src/bar/foo.ts --no-git
# Move a whole directory: every file relocates, every importer follows
jmove mv src/utils src/helpers
# Java: jmove updates `package`, all `import`s and moves the file
jmove mv src/com/example/utils/Parser.java src/com/example/core/Parser.java
# Find broken imports (exit code 2 if any)
jmove check
# Auto-repair import problems (unused + missing imports today) — same dry-run/atomic engine
jmove fix --dry-run
jmove fix
# AI-agent workflow
jmove mv src/foo.ts src/bar/foo.ts --dry-run --json
jmove mv src/foo.ts src/bar/foo.ts --json
jmove check --json
See docs/EXAMPLES.md for more, and docs/SKILL.md if you are an AI agent.
Roadmap
TypeScript/JavaScript and Java (the open niche) are in — real-world tested
on google/guava. fix auto-repairs small breakages on the same
dry-run/atomic engine (Java: unused, missing and
misordered imports; TS: unused imports), then Python, Go. split (automatic file decomposition) is
planned — no tool does it. Full plan: docs/PLAN.md.
License
GPL-3.0-only. See LICENSE.