jmove/docs/PLAN.md

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:

  • --json on 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: 0 ok · 1 error · 2 broken imports (from check).
  • docs/SKILL.md — machine-readable instructions an agent can drop into context: short, no fluff, workflow-first.

Correctness principles

  1. Rewrite only the specifier string (byte span), never reformat the statement — no formatter dependency, minimal diffs.
  2. Resolution must match the language's own lookup: TS extension guessing
    • index.*; Java package⇄path; (Phase 2) tsconfig paths aliases.
  3. Imports are rewritten before the move; any error rolls back all writes. Prefer git mv when inside a repo to preserve history.
  4. Beyond imports, warn (don't silently rewrite) about other references: package.json exports, jest mocks, tsconfig includes, 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); check as 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, --git integration.
  • Phase 3 — Python, Go (per-file, unlike refac), split command.
  • 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.