mover-os 4.4.0 → 4.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/install.js +78 -7
  2. package/package.json +1 -1
package/install.js CHANGED
@@ -420,8 +420,8 @@ function interactiveSelect(items, { multi = false, preSelected = [], defaultInde
420
420
  lines++;
421
421
 
422
422
  const hint = multi
423
- ? dim(" ↑↓ navigate space select a all enter confirm")
424
- : dim(" ↑↓ navigate enter select");
423
+ ? dim(" ↑↓ navigate space select a all enter confirm esc back")
424
+ : dim(" ↑↓ navigate enter select esc back");
425
425
  w(`\x1b[2K${BAR_COLOR}│${S.reset}${hint}\n`);
426
426
  lines++;
427
427
 
@@ -467,6 +467,20 @@ function interactiveSelect(items, { multi = false, preSelected = [], defaultInde
467
467
  }
468
468
  return;
469
469
  }
470
+ else if (data === "\x1b" || data === "\x1b\x1b" || (!multi && data === "q")) {
471
+ // Escape — go back / cancel
472
+ stdin.removeListener("data", handler);
473
+ stdin.setRawMode(false);
474
+ stdin.pause();
475
+ if (prevLines > 0) {
476
+ w(`\x1b[${prevLines}A`);
477
+ for (let i = 0; i < prevLines; i++) w("\x1b[2K\n");
478
+ w(`\x1b[${prevLines}A`);
479
+ }
480
+ w(S.show);
481
+ resolve(null);
482
+ return;
483
+ }
470
484
  else if (data === "\x03") { cleanup(); ln(); process.exit(0); }
471
485
 
472
486
  render();
@@ -655,6 +669,7 @@ function parseArgs() {
655
669
  if (a === "--key" && args[i + 1]) { opts.key = args[++i]; continue; }
656
670
  // Backward compat: --update / -u → command 'update'
657
671
  if (a === "--update" || a === "-u") { opts.command = "update"; continue; }
672
+ if (a === "--_self-updated") { opts._selfUpdated = true; continue; }
658
673
  if (a === "--help" || a === "-h") {
659
674
  ln();
660
675
  ln(` ${bold("moveros")} ${dim("— the Mover OS companion CLI")}`);
@@ -708,6 +723,17 @@ function detectObsidianVaults() {
708
723
  }
709
724
  }
710
725
 
726
+ function compareVersions(a, b) {
727
+ const pa = a.split(".").map(Number);
728
+ const pb = b.split(".").map(Number);
729
+ for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
730
+ const va = pa[i] || 0, vb = pb[i] || 0;
731
+ if (va > vb) return 1;
732
+ if (va < vb) return -1;
733
+ }
734
+ return 0;
735
+ }
736
+
711
737
  // ─── Change detection (update mode) ─────────────────────────────────────────
712
738
  function detectChanges(bundleDir, vaultPath, selectedAgentIds) {
713
739
  const home = os.homedir();
@@ -2995,6 +3021,7 @@ async function cmdCapture(opts) {
2995
3021
  { id: "link", name: "Link", tier: "URL with optional note" },
2996
3022
  { id: "dump", name: "Brain dump", tier: "Free-form text" },
2997
3023
  ], { multi: false });
3024
+ if (!type) return;
2998
3025
  }
2999
3026
  content = await textInput({ label: `Enter ${type}:` });
3000
3027
  }
@@ -3052,6 +3079,7 @@ async function cmdWho(opts) {
3052
3079
  { id: "yes", name: "Create stub", tier: `Creates ${name}.md in People/` },
3053
3080
  { id: "no", name: "Skip", tier: "" },
3054
3081
  ], { multi: false });
3082
+ if (!create) return;
3055
3083
  if (create === "yes") {
3056
3084
  const peopleDir = path.join(entitiesDir, "People");
3057
3085
  fs.mkdirSync(peopleDir, { recursive: true });
@@ -3503,6 +3531,8 @@ async function cmdPrayer(opts) {
3503
3531
  barLn();
3504
3532
  const choice = await interactiveSelect(items, { multi: false });
3505
3533
 
3534
+ if (!choice || choice === "back") return;
3535
+
3506
3536
  if (choice === "fetch") {
3507
3537
  barLn();
3508
3538
  const city = await textInput({ label: "City", placeholder: "London" });
@@ -3649,7 +3679,7 @@ async function cmdBackup(opts) {
3649
3679
  ];
3650
3680
 
3651
3681
  const choices = await interactiveSelect(items, { multi: true, preSelected: ["engine"] });
3652
- if (choices.length === 0) { barLn(dim(" Cancelled.")); return; }
3682
+ if (!choices || choices.length === 0) { barLn(dim(" Cancelled.")); return; }
3653
3683
 
3654
3684
  const now = new Date();
3655
3685
  const ts = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}_${String(now.getHours()).padStart(2, "0")}${String(now.getMinutes()).padStart(2, "0")}`;
@@ -4215,6 +4245,39 @@ async function main() {
4215
4245
  process.exit(1);
4216
4246
  }
4217
4247
 
4248
+ // ── CLI self-update check ──
4249
+ if (opts.command === "update" && !opts._selfUpdated) {
4250
+ try {
4251
+ const localVer = require("./package.json").version;
4252
+ const npmVer = execSync("npm view mover-os version", { encoding: "utf8", timeout: 10000 }).trim();
4253
+ if (npmVer && npmVer !== localVer && compareVersions(npmVer, localVer) > 0) {
4254
+ barLn(`${yellow("CLI update available:")} ${dim(localVer)} ${dim("\u2192")} ${green(npmVer)}`);
4255
+ const sp = spinner("Updating CLI");
4256
+ try {
4257
+ execSync("npm i -g mover-os", { stdio: "ignore", timeout: 60000 });
4258
+ sp.stop(`CLI updated to ${npmVer}`);
4259
+ barLn(dim(" Re-running with updated CLI..."));
4260
+ barLn();
4261
+ // Re-exec with new code — pass args through, add flag to prevent loop
4262
+ const args = process.argv.slice(2).concat("--_self-updated");
4263
+ const { spawnSync } = require("child_process");
4264
+ const result = spawnSync(process.argv[0], [process.argv[1], ...args], {
4265
+ stdio: "inherit", cwd: process.cwd(),
4266
+ });
4267
+ process.exit(result.status || 0);
4268
+ } catch (e) {
4269
+ sp.stop(yellow(`CLI self-update failed: ${e.message}`));
4270
+ barLn(dim(" Continuing with current version..."));
4271
+ }
4272
+ } else {
4273
+ barLn(`${green("\u2713")} ${dim("CLI is up to date")} ${dim(`(${localVer})`)}`);
4274
+ }
4275
+ } catch {
4276
+ barLn(dim(" Could not check for CLI updates (offline?)"));
4277
+ }
4278
+ barLn();
4279
+ }
4280
+
4218
4281
  // ── Headless quick update ──
4219
4282
  if (opts.command === "update") {
4220
4283
  // Validate stored key
@@ -4433,6 +4496,7 @@ async function main() {
4433
4496
  });
4434
4497
 
4435
4498
  const selected = await interactiveSelect(vaultItems, { multi: false });
4499
+ if (!selected) return;
4436
4500
 
4437
4501
  if (selected === "__manual__") {
4438
4502
  vaultPath = await textInput({
@@ -4483,6 +4547,7 @@ async function main() {
4483
4547
  ],
4484
4548
  { multi: false, defaultIndex: 0 }
4485
4549
  );
4550
+ if (!installMode) return;
4486
4551
  }
4487
4552
 
4488
4553
  // ── Uninstall flow ──
@@ -4522,7 +4587,7 @@ async function main() {
4522
4587
  preSelected: ["engine"],
4523
4588
  });
4524
4589
 
4525
- if (backupChoices.length > 0 && !(backupChoices.length === 1 && backupChoices.includes("skip"))) {
4590
+ if (backupChoices && backupChoices.length > 0 && !(backupChoices.length === 1 && backupChoices.includes("skip"))) {
4526
4591
  const now = new Date();
4527
4592
  const ts = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}_${String(now.getHours()).padStart(2, "0")}${String(now.getMinutes()).padStart(2, "0")}`;
4528
4593
  const archivesDir = path.join(vaultPath, "04_Archives");
@@ -4685,12 +4750,13 @@ async function main() {
4685
4750
  multi: true,
4686
4751
  preSelected: detectedIds,
4687
4752
  });
4753
+ if (!selectedIds) return;
4688
4754
  const selectedAgents = AGENTS.filter((a) => selectedIds.includes(a.id));
4689
4755
 
4690
4756
  if (selectedAgents.length === 0) {
4691
4757
  barLn(yellow("No agents selected."));
4692
4758
  outro("Cancelled.");
4693
- process.exit(0);
4759
+ return;
4694
4760
  }
4695
4761
 
4696
4762
  // ── Change detection + selection (update mode only) ──
@@ -4735,9 +4801,9 @@ async function main() {
4735
4801
  { multi: false, defaultIndex: 0 }
4736
4802
  );
4737
4803
 
4738
- if (applyChoice === "cancel") {
4804
+ if (!applyChoice || applyChoice === "cancel") {
4739
4805
  outro("Cancelled.");
4740
- process.exit(0);
4806
+ return;
4741
4807
  }
4742
4808
 
4743
4809
  if (applyChoice === "select") {
@@ -4771,6 +4837,7 @@ async function main() {
4771
4837
  multi: true,
4772
4838
  preSelected: changedPreSelected,
4773
4839
  });
4840
+ if (!selectedFileIds) return;
4774
4841
 
4775
4842
  // Build workflow filter Set
4776
4843
  const selectedWfFiles = selectedFileIds
@@ -4817,6 +4884,7 @@ async function main() {
4817
4884
  multi: true,
4818
4885
  preSelected,
4819
4886
  });
4887
+ if (!selectedCatIds) return;
4820
4888
 
4821
4889
  installSkills = selectedCatIds.length > 0;
4822
4890
  if (installSkills) {
@@ -4843,6 +4911,7 @@ async function main() {
4843
4911
  ],
4844
4912
  { multi: false, defaultIndex: 0 }
4845
4913
  );
4914
+ if (!slChoice) return;
4846
4915
  installStatusLine = slChoice === "yes";
4847
4916
  }
4848
4917
 
@@ -4862,6 +4931,7 @@ async function main() {
4862
4931
  ],
4863
4932
  { multi: false, defaultIndex: 1 }
4864
4933
  );
4934
+ if (!ptChoice) return;
4865
4935
  if (ptChoice === "yes") {
4866
4936
  prayerSetup = true;
4867
4937
  barLn();
@@ -4876,6 +4946,7 @@ async function main() {
4876
4946
  ],
4877
4947
  { multi: false, defaultIndex: 0 }
4878
4948
  );
4949
+ if (!method || method === "later") { /* skip */ }
4879
4950
 
4880
4951
  const moverDir = path.join(os.homedir(), ".mover");
4881
4952
  if (!fs.existsSync(moverDir)) fs.mkdirSync(moverDir, { recursive: true, mode: 0o700 });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mover-os",
3
- "version": "4.4.0",
3
+ "version": "4.4.1",
4
4
  "description": "The self-improving OS for AI agents. Turns Obsidian into an execution engine.",
5
5
  "bin": {
6
6
  "moveros": "install.js"