Initial commit: move CLI (milestone 2)

- src/move.ts entry point with CLI parsing, --help, --version
- src/cli.ts: parseCliArgs, printHelp, ParsedCliArgs, CliError, VERSION
- src/config.ts: Config type, DEFAULT_CONFIG, resolveConfig
- src/keeper.ts: synthetic-activity sweep + idle-watch loop
- package.json bin entry + shebang for 'bun link' global install
- install.sh: contributor bootstrap (will be repurposed; see
  DISTRIBUTION-PLAN.md for the end-user installer design)
- DISTRIBUTION-PLAN.md captures the tabled end-user distribution work
This commit is contained in:
2026-06-15 14:31:31 -05:00
commit 83d91c5630
12 changed files with 1886 additions and 0 deletions
Executable
+57
View File
@@ -0,0 +1,57 @@
#!/usr/bin/env bun
/**
* move.ts
* -------
* Entry point for the `move` CLI.
*
* Thin shim that ties the three logic modules together:
* - `cli.ts` parses and validates `process.argv`.
* - `config.ts` holds the default tunables and the `resolveConfig` overlay.
* - `keeper.ts` owns the synthetic-activity sweep and idle-watch loop.
*
* Order of operations:
* 1. Parse CLI args. Bad input -> stderr message + short usage hint, exit 2.
* 2. `--help` / `--version` short-circuit before any mouse work happens.
* 3. Resolve CLI overrides on top of `DEFAULT_CONFIG` and hand the result
* (plus the verbose flag) to `runKeeper`.
* 4. Any unhandled rejection from `runKeeper` is logged and exits with
* code 1 so it's catchable by shells / supervisors.
*
* Runtime: Bun (uses `@nut-tree-fork/nut-js` via `keeper.ts`). The shebang
* above lets this file run as a real CLI once linked via `bun link`.
*/
import { parseCliArgs, printHelp, VERSION } from "./cli.ts";
import type { ParsedCliArgs } from "./cli.ts";
import { resolveConfig } from "./config.ts";
import { runKeeper } from "./keeper.ts";
let cliArgs: ParsedCliArgs;
try {
cliArgs = parseCliArgs();
} catch (err: unknown) {
const msg: string = err instanceof Error ? err.message : String(err);
process.stderr.write(`move: ${msg}\nTry 'move --help' for more information.\n`);
process.exit(2);
}
if (cliArgs.help) {
printHelp();
process.exit(0);
}
if (cliArgs.version) {
process.stdout.write(`move ${VERSION}\n`);
process.exit(0);
}
const config = resolveConfig({
moveInterval: cliArgs.moveInterval,
checkInterval: cliArgs.checkInterval,
stepDelay: cliArgs.stepDelay,
stepCount: cliArgs.stepCount,
});
runKeeper(config, cliArgs.verbose).catch((err: unknown): void => {
console.error("Error:", err);
process.exit(1);
});