docs: fix rules, git-aware mv, install-as-library; agent workflow for mv+fix+check loop

This commit is contained in:
loki5512344 2026-09-15 16:10:17 +02:00
parent 9dda5f4562
commit 81d442b5b1
Signed by: boba
GPG key ID: 253067914055423B
4 changed files with 135 additions and 20 deletions

View file

@ -3,9 +3,11 @@
Concrete examples for both audiences: humans at a terminal and AI agents
consuming `--json`. All outputs below are captured from the real binary.
Flags (see `jmove --help`): `mv <source> <target> [--dry-run] [--json]`,
`check [--json]`, and the global `--root <DIR>`. Paths may be relative to
the root or absolute inside it; `.gitignore`d files are never indexed.
Flags (see `jmove --help`): `mv <source> <target> [--dry-run] [--json]
[--no-git]`, `check [--json]`, and the global `--root <DIR>`. Paths may be
relative to the root or absolute inside it; `.gitignore`d files are never
indexed. Inside a git repo, `mv` of a tracked file uses `git mv` (the
rename is staged); `--no-git` forces a plain filesystem rename.
Exit codes: `0` ok · `1` operation error · `2` `check` found broken imports.
## Human usage
@ -103,6 +105,50 @@ fourth rewrite. Targets outside the source root, non-`.java` targets and
cross-directory moves of default-package classes are rejected with
`PLAN_REJECTED` (exit 1) and change nothing.
### Fix: auto-repair unused imports
`fix` runs deterministic rules and applies them through the same
dry-run/atomic engine. Preview first, then apply:
```console
$ cat src/main/java/com/example/app/App.java
package com.example.app;
import com.example.Text;
import com.example.unused.Ghost; // never referenced
...
$ jmove fix --dry-run
--- src/main/java/com/example/app/App.java
+++ src/main/java/com/example/app/App.java
@@ -1,6 +1,5 @@
package com.example.app;
import com.example.Text;
-import com.example.unused.Ghost;
...
$ jmove fix
fixed 1 issue in 1 file
```
Only provably-dead imports are removed: the whole statement line (with its
newline) disappears and every other line stays byte-identical. A name that
also appears in a comment, string literal or a sibling static import is
kept, so the rule can only under-report, never delete live code. Scope to
one rule with `--rule java/unused-import` (`java/import-order` sorts the block
google-style); an unknown id exits `1` with `INVALID_ARGUMENT` and lists the rules.
### Fix: add a missing import for a bare type reference
After a Java `mv` changes a file's package, references to former
same-package siblings stop resolving. `java/missing-import` inserts the
import for a unique FQN in the class index; ambiguous ones are reported.
```console
$ jmove fix --rule java/missing-import
fixed 1 issue in 1 file # inserted: import com.example.util.Maths;
```
## AI-agent usage (`--json`)
Every `--json` response is a flat envelope: `status` (`"ok"` | `"dry_run"`
@ -122,6 +168,7 @@ $ jmove mv lib/sum.ts utils/sum.ts --dry-run --json
"affected_files": [
"app.ts"
],
"would_move_via": "fs",
"diff": "--- app.ts\n+++ app.ts\n@@ -1,4 +1,4 @@\n-import { sum } from \"./lib/sum\";\n+import { sum } from \"./utils/sum\";\n..."
}
```
@ -151,12 +198,15 @@ $ jmove mv lib/sum.ts utils/sum.ts --json
}
],
"moved": 1,
"updated_imports": 1
"updated_imports": 1,
"moved_via": "fs"
}
```
`changed_files[].changes[]` lists every rewritten specifier with its
1-based line; `moved` and `updated_imports` are the counters.
1-based line; `moved` and `updated_imports` are the counters,
`moved_via` tells whether the rename went through git (`"git"`, staged)
or the plain filesystem (`"fs"`).
### 3. Verify

View file

@ -18,13 +18,21 @@ kept in sync); Python/Go on the roadmap. Single binary, no LSP needed.
### mv — move a file and rewrite its importers
```
jmove mv <source> <target> [--root DIR] [--dry-run] [--json]
jmove mv <source> <target> [--root DIR] [--dry-run] [--json] [--no-git]
```
Always run `--dry-run` first and confirm the change set looks right.
Moving onto an existing path fails with `TARGET_EXISTS` — choose another
target (Phase 1 has no overwrite mode).
Git integration: inside a git repository, a tracked file is renamed with
`git mv` so the rename is staged (history-preserving `git log --follow` /
`git diff -M` work). Untracked files, non-repositories and `--no-git`
fall back to a plain filesystem rename. The import rewrites land in the
working tree unstaged either way — stage or commit them yourself.
`--json` reports the choice as `moved_via` (`"git"`/`"fs"`) and, on a
dry-run, `would_move_via`.
### check — find broken imports
```
@ -33,6 +41,32 @@ jmove check [--root DIR] [--json]
Run after any move (or any edit) to validate project consistency.
### fix — auto-repair import problems
```
jmove fix [--root DIR] [--rule ID] [--dry-run] [--json]
```
Runs the deterministic rules over the whole project and applies the
repairs through the same atomic engine as `mv` (dry-run diff, rollback,
exit codes). Current Java rules: `java/unused-import` (deletes single-type
imports whose name is provably unreferenced), `java/missing-import`
(inserts the import of a project class used by simple name — unique FQN
candidate required) and `java/import-order` (Google style: statics first,
then single-type, ASCII-sorted, duplicates dropped). Unknown `--rule`
fails with `INVALID_ARGUMENT` and lists the known ids. A candidate the
engine cannot prove safe is reported with `"applied": false` and a
`candidates` array of FQN options — resolve it yourself (pick one, add
the import) and re-run; `fix` never guesses. When two rules want the same
bytes (order rewrite vs an unused deletion), the urgent rule applies and
the other is deferred (`"applied": false`, "skipped") — re-run `fix`
until it reports "nothing to change" (converges in 2–3 runs).
Recommended: `fix --dry-run --json`, inspect `files[].fixes[]`
(`rule`, `line`, `message`, `applied`, optional `candidates`), then apply
without `--dry-run`. For Java projects the practical loop is: `mv` →
`fix` → `fix` again if anything was "skipped" → `check`.
## Java specifics
- Moving a `.java` file rewrites three coordinated edits: its own
@ -52,7 +86,9 @@ Run after any move (or any edit) to validate project consistency.
1. `jmove mv <src> <target> --dry-run --json` — preview.
2. Inspect `affected_files`; if unexpected, abort and ask the user.
3. `jmove mv <src> <target> --json` — apply (atomic, rolls back on error).
4. `jmove check --json` — verify nothing is broken.
4. Java moves: `jmove fix --json` — heal bare same-package references
left behind by the move (they break `javac` otherwise).
5. `jmove check --json` — verify nothing is broken.
## Output format (--json)