Add -e/--edit flag: open config file in $EDITOR

New module src/editor.ts handles the flag end-to-end:

  - editorCommand(editor, path) returns the sh -c argv that lets the
    shell tokenize multi-word $EDITOR values like 'code --wait'.
    Extracted so editor.test.ts can verify construction without
    actually launching an editor.
  - editConfig(path) checks $EDITOR is set, checks the target file
    exists, spawns 'sh -c <editor> "$@" -- <path>' with stdio
    inherited, and exits with the editor's status code.

Refuses (CliError -> exit 2) when:
  - $EDITOR is unset or empty.
  - The target config file doesn't exist. (Same recovery hint as
    elsewhere: 'run move once or reinstall'.)

src/cli.ts adds the flag to the parser and printHelp(). src/move.ts
dispatches it after --version and before config-load. Resolution
mirrors the loader: --config <path> wins, else defaultConfigPath().

8 new tests in src/editor.test.ts cover the pure helper and both
refusal paths; the spawn success path is verified via manual
'EDITOR=true move -e' (would otherwise kill the test process).

README Usage block, Configuration section (new Editing subsection),
and Files table all updated to match.
This commit is contained in:
2026-06-17 22:20:27 -05:00
parent cc7a487aca
commit 10dcc1791a
5 changed files with 229 additions and 1 deletions
+14 -1
View File
@@ -30,9 +30,10 @@
import { parseCliArgs, printHelp, VERSION } from "./cli.ts";
import type { ParsedCliArgs } from "./cli.ts";
import { resolveConfig } from "./config.ts";
import { defaultConfigPath, resolveConfig } from "./config.ts";
import type { ConfigOverrides } from "./config.ts";
import { loadConfigFile } from "./configFile.ts";
import { editConfig } from "./editor.ts";
// `keeper.ts` is intentionally NOT statically imported here. It transitively
// pulls in `@nut-tree-fork/nut-js`, which in turn dlopens a sizeable native
@@ -91,6 +92,18 @@ if (cliArgs.version) {
process.exit(0);
}
if (cliArgs.edit) {
// Edit is a fully terminal action: open the config file in $EDITOR and
// hand the user's terminal over. Path resolution mirrors loadConfigFile's:
// honor `--config <path>` if set, else use the XDG default.
try {
const path: string = cliArgs.config ?? defaultConfigPath();
editConfig(path);
} catch (err: unknown) {
failUser(err);
}
}
let fileOverrides: ConfigOverrides | null;
try {
fileOverrides = loadConfigFile(cliArgs.config);