4.1 KiB
jmove — Product Plan
Vision
The mv for source code that never breaks imports: fast, safe (dry-run +
atomic rollback), single static Rust binary — usable by humans in a
terminal and by AI agents over --json, with zero external toolchains.
Competitive landscape
The closest analog is refac / ai_refac (github: jav-ed/ai_refac): moves sources and updates imports/paths across the project, aimed at AI agents without an IDE.
| refac/ai_refac | jmove | |
|---|---|---|
| Languages | TS/JS (ts-morph+Bun), Python (Rope), Rust (rust-analyzer LSP), Go (gopls), Dart, Markdown | TS/JS → Java → Python/Go |
| Under the hood | spawns external toolchains/LSPs per run | pure Rust, tree-sitter grammars in-process |
| Dry-run | none — writes straight to disk | core feature, unified diff |
| Atomicity/rollback | not described | rewrite-then-move, rollback on failure |
| TS limit | 30 files per call | none (index-based) |
| Go behaviour | moves whole package for any file | per-file moves |
| Java | not supported, not planned | open niche — headline feature |
| UX | agent-oriented CLI | human UX + first-class --json agent mode |
Conclusion: the idea is validated, the product is not. jmove wins on
dry-run, speed (no LSP startup), human UX, single-binary distribution,
the split command (nobody has it) and Java.
Why Java is the wedge
Java imports are fully-qualified package paths hard-coupled to directory layout:
src/com/example/utils/Parser.java
→ package com.example.utils; // must change
→ import com.example.utils.Parser; // must change in every importer
→ + physical file move
Three coordinated edits per move; outside IntelliJ nobody automates it.
In headless/CI/agent contexts there is no IntelliJ. Implementation:
tree-sitter Java grammar (mature) + directory⇄package convention +
source-root detection (src/main/java, src/, ...). This is the feature
that makes jmove the answer for "move a Java file in CI".
AI-agent design
Existing tools ship a raw .agents/skills/ folder; we do it properly:
--jsonon every command. Envelope:{ "status": "ok" | "dry_run" | "error", "operation": "...", ... }- Stable error codes + actionable hints:
{ "status":"error", "code":"TARGET_EXISTS", "message":"...", "hint":"Use --force or choose another target" } mv --dry-run --json→{ would_move, would_update, affected_files }so agents preview → confirm → apply →check --json.- Exit codes:
0ok ·1error ·2broken imports (fromcheck). docs/SKILL.md— machine-readable instructions an agent can drop into context: short, no fluff, workflow-first.
Correctness principles
- Rewrite only the specifier string (byte span), never reformat the statement — no formatter dependency, minimal diffs.
- Resolution must match the language's own lookup: TS extension guessing
index.*; Java package⇄path; (Phase 2) tsconfigpathsaliases.
- Imports are rewritten before the move; any error rolls back all
writes. Prefer
git mvwhen inside a repo to preserve history. - Beyond imports, warn (don't silently rewrite) about other references:
package.jsonexports, jest mocks,tsconfigincludes, markdown links.
Phases
- Phase 1 (MVP) — as in
todo.md: TS/JS scan+index, graph, mv with dry-run/atomic apply. Add to MVP: relative-path resolution incl.index.*(without it the tool is toy-grade);checkas the self-test. - Phase 1.5 — Java. The differentiator, as soon as the TS pipeline is
proven. Also:
--json+ SKILL.md ship with Java, not after. - Phase 2 — disk index cache (
.jmove/), incremental reindex, tsconfig paths, rayon parallelism,--gitintegration. - Phase 3 — Python, Go (per-file, unlike refac),
splitcommand. - Later — LSP server mode (become the thing refac spawns), watch mode, VS Code wrapper.
Rules (binding, see todo.md)
KISS · DRY · SOLID · ≤250 lines/file · ≤4 files/folder · fmt+clippy
-D warnings per commit · no dead code, no #[allow(dead_code)] ·
English Javadoc-style comments on all public API.