mover-os 4.5.2 → 4.5.3

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 +149 -64
  2. package/package.json +1 -1
package/install.js CHANGED
@@ -92,17 +92,10 @@ function waveGradient(text, frame, totalFrames) {
92
92
  // ─── TUI: Alternate screen buffer ────────────────────────────────────────────
93
93
  let _inAltScreen = false;
94
94
  function enterAltScreen() {
95
- if (!IS_TTY || _inAltScreen) return;
96
- w("\x1b[?1049h"); // Enter alternate screen
97
- w("\x1b[2J\x1b[H"); // Clear + cursor home
98
- w(S.hide);
99
- _inAltScreen = true;
95
+ // Disabled: keep output in terminal scrollback history
100
96
  }
101
97
  function exitAltScreen() {
102
- if (!IS_TTY || !_inAltScreen) return;
103
- w("\x1b[?1049l"); // Restore original screen
104
- w(S.show);
105
- _inAltScreen = false;
98
+ // Disabled: no alt screen to exit
106
99
  }
107
100
  function clearContent() {
108
101
  w("\x1b[2J\x1b[H"); // Clear screen + cursor to top
@@ -956,12 +949,15 @@ function detectChanges(bundleDir, vaultPath, selectedAgentIds) {
956
949
  const home = os.homedir();
957
950
  const result = { workflows: [], hooks: [], rules: null, templates: [], skills: [], statusline: "unchanged" };
958
951
 
952
+ // Expand selection IDs to target IDs (e.g. "gemini-cli" → includes "antigravity")
953
+ const targetIds = expandTargetIds(selectedAgentIds);
954
+
959
955
  // --- Workflows: compare source vs first installed destination ---
960
956
  const wfSrc = path.join(bundleDir, "src", "workflows");
961
957
  const wfDests = [
962
- selectedAgentIds.includes("claude-code") && path.join(home, ".claude", "commands"),
963
- selectedAgentIds.includes("cursor") && path.join(home, ".cursor", "commands"),
964
- selectedAgentIds.includes("antigravity") && path.join(home, ".gemini", "antigravity", "global_workflows"),
958
+ targetIds.includes("claude-code") && path.join(home, ".claude", "commands"),
959
+ targetIds.includes("cursor") && path.join(home, ".cursor", "commands"),
960
+ targetIds.includes("antigravity") && path.join(home, ".gemini", "antigravity", "global_workflows"),
965
961
  ].filter(Boolean);
966
962
  const wfDest = wfDests.find((d) => fs.existsSync(d));
967
963
 
@@ -984,7 +980,7 @@ function detectChanges(bundleDir, vaultPath, selectedAgentIds) {
984
980
  // --- Hooks: compare with CRLF normalization ---
985
981
  const hooksSrc = path.join(bundleDir, "src", "hooks");
986
982
  const hooksDest = path.join(home, ".claude", "hooks");
987
- if (fs.existsSync(hooksSrc) && selectedAgentIds.includes("claude-code")) {
983
+ if (fs.existsSync(hooksSrc) && targetIds.includes("claude-code")) {
988
984
  for (const file of fs.readdirSync(hooksSrc).filter((f) => f.endsWith(".sh") || f.endsWith(".md"))) {
989
985
  const srcContent = fs.readFileSync(path.join(hooksSrc, file), "utf8")
990
986
  .replace(/\r\n/g, "\n").replace(/\r/g, "\n");
@@ -1005,9 +1001,9 @@ function detectChanges(bundleDir, vaultPath, selectedAgentIds) {
1005
1001
  // --- Rules: compare source vs first installed destination ---
1006
1002
  const rulesSrc = path.join(bundleDir, "src", "system", "Mover_Global_Rules.md");
1007
1003
  const rulesDests = [
1008
- selectedAgentIds.includes("claude-code") && path.join(home, ".claude", "CLAUDE.md"),
1009
- selectedAgentIds.includes("cursor") && path.join(home, ".cursor", "rules", "mover-os.mdc"),
1010
- selectedAgentIds.includes("gemini-cli") && path.join(home, ".gemini", "GEMINI.md"),
1004
+ targetIds.includes("claude-code") && path.join(home, ".claude", "CLAUDE.md"),
1005
+ targetIds.includes("cursor") && path.join(home, ".cursor", "rules", "mover-os.mdc"),
1006
+ targetIds.includes("gemini-cli") && path.join(home, ".gemini", "GEMINI.md"),
1011
1007
  ].filter(Boolean);
1012
1008
  const rulesDest = rulesDests.find((d) => fs.existsSync(d));
1013
1009
  if (fs.existsSync(rulesSrc) && rulesDest) {
@@ -1049,9 +1045,9 @@ function detectChanges(bundleDir, vaultPath, selectedAgentIds) {
1049
1045
  // --- Skills: compare source vs installed ---
1050
1046
  const skillsSrc = path.join(bundleDir, "src", "skills");
1051
1047
  const skillsDests = [
1052
- selectedAgentIds.includes("claude-code") && path.join(home, ".claude", "skills"),
1053
- selectedAgentIds.includes("cursor") && path.join(home, ".cursor", "skills"),
1054
- selectedAgentIds.includes("cline") && path.join(home, ".cline", "skills"),
1048
+ targetIds.includes("claude-code") && path.join(home, ".claude", "skills"),
1049
+ targetIds.includes("cursor") && path.join(home, ".cursor", "skills"),
1050
+ targetIds.includes("cline") && path.join(home, ".cline", "skills"),
1055
1051
  ].filter(Boolean);
1056
1052
  const skillsDest = skillsDests.find((d) => fs.existsSync(d));
1057
1053
  if (fs.existsSync(skillsSrc) && skillsDest) {
@@ -1081,11 +1077,11 @@ function detectChanges(bundleDir, vaultPath, selectedAgentIds) {
1081
1077
  // --- Statusline ---
1082
1078
  const slSrc = path.join(bundleDir, "src", "hooks", "statusline.js");
1083
1079
  const slDest = path.join(home, ".claude", "statusline.js");
1084
- if (fs.existsSync(slSrc) && fs.existsSync(slDest) && selectedAgentIds.includes("claude-code")) {
1080
+ if (fs.existsSync(slSrc) && fs.existsSync(slDest) && targetIds.includes("claude-code")) {
1085
1081
  const srcSl = fs.readFileSync(slSrc, "utf8").replace(/\r\n/g, "\n");
1086
1082
  const destSl = fs.readFileSync(slDest, "utf8").replace(/\r\n/g, "\n");
1087
1083
  result.statusline = srcSl === destSl ? "unchanged" : "changed";
1088
- } else if (fs.existsSync(slSrc) && !fs.existsSync(slDest) && selectedAgentIds.includes("claude-code")) {
1084
+ } else if (fs.existsSync(slSrc) && !fs.existsSync(slDest) && targetIds.includes("claude-code")) {
1089
1085
  result.statusline = "new";
1090
1086
  }
1091
1087
 
@@ -1627,6 +1623,17 @@ const AGENTS = AGENT_SELECTIONS.map((s) => ({
1627
1623
  detect: AGENT_REGISTRY[s.targets[0]].detect,
1628
1624
  }));
1629
1625
 
1626
+ // Expand selection IDs to install target IDs (e.g. "gemini-cli" → ["gemini-cli", "antigravity"])
1627
+ function expandTargetIds(selectionIds) {
1628
+ const result = [];
1629
+ for (const id of selectionIds) {
1630
+ const sel = AGENT_SELECTIONS.find((s) => s.id === id);
1631
+ if (sel) { for (const t of sel.targets) result.push(t); }
1632
+ else result.push(id);
1633
+ }
1634
+ return [...new Set(result)];
1635
+ }
1636
+
1630
1637
  // ─── Utility functions ──────────────────────────────────────────────────────
1631
1638
  function cmdExists(cmd) {
1632
1639
  try {
@@ -2113,6 +2120,8 @@ function writeMoverConfig(vaultPath, agentIds, licenseKey, opts = {}) {
2113
2120
  if (existing.settings) config.settings = { ...existing.settings };
2114
2121
  // Preserve prayer_times fallback
2115
2122
  if (existing.prayer_times) config.prayer_times = existing.prayer_times;
2123
+ // Preserve frecency data
2124
+ if (existing.cli_usage) config.cli_usage = existing.cli_usage;
2116
2125
  config.updatedAt = new Date().toISOString();
2117
2126
  } catch {}
2118
2127
  }
@@ -3031,7 +3040,8 @@ async function cmdDoctor(opts) {
3031
3040
  // Per-agent checks
3032
3041
  barLn();
3033
3042
  barLn(dim(" Agents:"));
3034
- const cfgData = fs.existsSync(cfg) ? JSON.parse(fs.readFileSync(cfg, "utf8")) : {};
3043
+ let cfgData = {};
3044
+ if (fs.existsSync(cfg)) { try { cfgData = JSON.parse(fs.readFileSync(cfg, "utf8")); } catch { statusLine("warn", "Config", "corrupt JSON"); } }
3035
3045
  const installedAgents = cfgData.agents || [];
3036
3046
  if (installedAgents.length === 0) {
3037
3047
  barLn(dim(" No agents recorded in config."));
@@ -3057,7 +3067,7 @@ async function cmdDoctor(opts) {
3057
3067
  const hasCmds = fs.existsSync(cp) && fs.readdirSync(cp).length > 0;
3058
3068
  checks.push(hasCmds ? "commands" : dim("no commands"));
3059
3069
  }
3060
- const allOk = checks.every((c) => !c.includes("missing"));
3070
+ const allOk = checks.every((c) => c === strip(c)); // styled text = problem
3061
3071
  statusLine(allOk ? "ok" : "warn", ` ${reg.name}`, checks.join(", "));
3062
3072
  }
3063
3073
 
@@ -3102,7 +3112,7 @@ async function cmdPulse(opts) {
3102
3112
  const focusMatch = ac.match(/\*\*Focus:\*\*\s*(.+)/i) || ac.match(/Single Test:\s*(.+)/i);
3103
3113
  if (focusMatch) focus = focusMatch[1].trim();
3104
3114
  // Blockers
3105
- const blockerSection = ac.match(/##.*Blocker[s]?[\s\S]*?(?=\n##|\n---|\Z)/i);
3115
+ const blockerSection = ac.match(/##.*Blocker[s]?[\s\S]*?(?=\n##|\n---|$)/i);
3106
3116
  if (blockerSection) {
3107
3117
  const lines = blockerSection[0].split("\n").filter((l) => l.trim().startsWith("-") || l.trim().startsWith("*"));
3108
3118
  blockers = lines.map((l) => l.replace(/^[\s*-]+/, "").trim()).filter(Boolean).slice(0, 3);
@@ -3491,7 +3501,7 @@ async function cmdReplay(opts) {
3491
3501
  const daily = fs.readFileSync(dailyPath, "utf8");
3492
3502
 
3493
3503
  // Extract session log entries
3494
- const logMatch = daily.match(/##\s*Session Log[\s\S]*?(?=\n## [^#]|\Z)/i);
3504
+ const logMatch = daily.match(/##\s*Session Log[\s\S]*?(?=\n## [^#]|$)/i);
3495
3505
  if (!logMatch) {
3496
3506
  barLn(dim(" No session log found in this Daily Note."));
3497
3507
  return;
@@ -4046,10 +4056,11 @@ async function cmdRestore(opts) {
4046
4056
  const archivesDir = path.join(vault, "04_Archives");
4047
4057
  if (!fs.existsSync(archivesDir)) { barLn(yellow(" No archives found.")); return; }
4048
4058
 
4059
+ const backupPrefixes = ["Backup_", "Engine_Backup_", "Areas_Backup_", "Agent_Backup_"];
4049
4060
  const backups = fs.readdirSync(archivesDir)
4050
4061
  .filter((d) => {
4051
4062
  const full = path.join(archivesDir, d);
4052
- return fs.statSync(full).isDirectory() && (d.startsWith("Backup_") || d.startsWith("Engine_Backup_"));
4063
+ return fs.statSync(full).isDirectory() && backupPrefixes.some((p) => d.startsWith(p));
4053
4064
  })
4054
4065
  .sort()
4055
4066
  .reverse();
@@ -4077,25 +4088,67 @@ async function cmdRestore(opts) {
4077
4088
 
4078
4089
  const backupPath = path.join(archivesDir, selected);
4079
4090
 
4080
- // Check for engine dir in backup
4081
- const engPath = path.join(backupPath, "engine");
4082
- if (fs.existsSync(engPath)) {
4083
- const engineDir = path.join(vault, "02_Areas", "Engine");
4084
- let restored = 0;
4085
- for (const f of fs.readdirSync(engPath).filter((f) => fs.statSync(path.join(engPath, f)).isFile())) {
4086
- fs.copyFileSync(path.join(engPath, f), path.join(engineDir, f));
4087
- restored++;
4091
+ let totalRestored = 0;
4092
+
4093
+ if (selected.startsWith("Areas_Backup_")) {
4094
+ // Areas backup: restore entire 02_Areas folder
4095
+ const areasTarget = path.join(vault, "02_Areas");
4096
+ copyDirRecursive(backupPath, areasTarget);
4097
+ statusLine("ok", "Areas", "folder restored");
4098
+ totalRestored++;
4099
+ } else if (selected.startsWith("Agent_Backup_")) {
4100
+ // Agent backup: restore rules/skills per agent
4101
+ let agentCount = 0;
4102
+ for (const agDir of fs.readdirSync(backupPath).filter((d) => fs.statSync(path.join(backupPath, d)).isDirectory())) {
4103
+ const reg = AGENT_REGISTRY[agDir];
4104
+ if (!reg) continue;
4105
+ const srcDir = path.join(backupPath, agDir);
4106
+ // Restore rules file
4107
+ if (reg.rules && reg.rules.dest) {
4108
+ const rulesFile = fs.readdirSync(srcDir).find((f) => !f.startsWith("skills") && fs.statSync(path.join(srcDir, f)).isFile());
4109
+ if (rulesFile) {
4110
+ try {
4111
+ const dest = reg.rules.dest(vault);
4112
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
4113
+ fs.copyFileSync(path.join(srcDir, rulesFile), dest);
4114
+ agentCount++;
4115
+ } catch {}
4116
+ }
4117
+ }
4118
+ // Restore skills directory
4119
+ const skillsSrc = path.join(srcDir, "skills");
4120
+ if (fs.existsSync(skillsSrc) && reg.skills && reg.skills.dest) {
4121
+ try {
4122
+ copyDirRecursive(skillsSrc, reg.skills.dest(vault));
4123
+ agentCount++;
4124
+ } catch {}
4125
+ }
4088
4126
  }
4089
- statusLine("ok", "Engine", `${restored} files restored`);
4127
+ statusLine("ok", "Agents", `${agentCount} items restored`);
4128
+ totalRestored = agentCount;
4090
4129
  } else {
4091
- // Legacy backup format (files directly in backup dir)
4092
- const engineDir = path.join(vault, "02_Areas", "Engine");
4093
- let restored = 0;
4094
- for (const f of fs.readdirSync(backupPath).filter((f) => f.endsWith(".md") && f !== ".backup-manifest.json")) {
4095
- fs.copyFileSync(path.join(backupPath, f), path.join(engineDir, f));
4096
- restored++;
4130
+ // Engine backup (Engine_Backup_ or legacy Backup_)
4131
+ const engPath = path.join(backupPath, "engine");
4132
+ if (fs.existsSync(engPath)) {
4133
+ const engineDir = path.join(vault, "02_Areas", "Engine");
4134
+ let restored = 0;
4135
+ for (const f of fs.readdirSync(engPath).filter((f) => fs.statSync(path.join(engPath, f)).isFile())) {
4136
+ fs.copyFileSync(path.join(engPath, f), path.join(engineDir, f));
4137
+ restored++;
4138
+ }
4139
+ statusLine("ok", "Engine", `${restored} files restored`);
4140
+ totalRestored = restored;
4141
+ } else {
4142
+ // Legacy backup format (files directly in backup dir)
4143
+ const engineDir = path.join(vault, "02_Areas", "Engine");
4144
+ let restored = 0;
4145
+ for (const f of fs.readdirSync(backupPath).filter((f) => f.endsWith(".md") && f !== ".backup-manifest.json")) {
4146
+ fs.copyFileSync(path.join(backupPath, f), path.join(engineDir, f));
4147
+ restored++;
4148
+ }
4149
+ if (restored > 0) statusLine("ok", "Engine", `${restored} files restored`);
4150
+ totalRestored = restored;
4097
4151
  }
4098
- if (restored > 0) statusLine("ok", "Engine", `${restored} files restored`);
4099
4152
  }
4100
4153
 
4101
4154
  barLn();
@@ -4692,14 +4745,20 @@ async function cmdUpdateComprehensive(opts, bundleDir, startTime) {
4692
4745
  createVaultStructure(vaultPath);
4693
4746
  installTemplateFiles(bundleDir, vaultPath);
4694
4747
  const writtenFiles = new Set();
4695
- const skillOpts = { install: true, categories: null, workflows: null };
4748
+ const skillOpts = { install: true, categories: null, workflows: null, statusLine: changes.statusline !== "unchanged" };
4696
4749
  for (const agent of detectedAgents) {
4697
- const fn = AGENT_INSTALLERS[agent.id];
4698
- if (!fn) continue;
4699
- const sp = spinner(agent.name);
4700
- const steps = fn(bundleDir, vaultPath, skillOpts, writtenFiles, agent.id);
4701
- await sleep(200);
4702
- sp.stop(steps.length > 0 ? `${agent.name} ${dim(steps.join(", "))}` : `${agent.name} ${dim("configured")}`);
4750
+ const sel = AGENT_SELECTIONS.find((s) => s.id === agent.id);
4751
+ const targets = sel ? sel.targets : [agent.id];
4752
+ for (const targetId of targets) {
4753
+ const fn = AGENT_INSTALLERS[targetId];
4754
+ if (!fn) continue;
4755
+ const targetReg = AGENT_REGISTRY[targetId];
4756
+ const displayName = targetReg ? targetReg.name : agent.name;
4757
+ const sp = spinner(displayName);
4758
+ const steps = fn(bundleDir, vaultPath, skillOpts, writtenFiles, targetId);
4759
+ await sleep(200);
4760
+ sp.stop(steps.length > 0 ? `${displayName} ${dim(steps.join(", "))}` : `${displayName} ${dim("configured")}`);
4761
+ }
4703
4762
  }
4704
4763
  fs.writeFileSync(path.join(vaultPath, ".mover-version"), `${require("./package.json").version}\n`, "utf8");
4705
4764
  writeMoverConfig(vaultPath, selectedIds);
@@ -4758,15 +4817,36 @@ async function cmdUpdateComprehensive(opts, bundleDir, startTime) {
4758
4817
  fs.mkdirSync(agentBackupDir, { recursive: true });
4759
4818
  let agentsBacked = 0;
4760
4819
  for (const ag of AGENTS.filter((a) => a.detect())) {
4761
- const agDir = path.join(agentBackupDir, ag.id);
4762
- fs.mkdirSync(agDir, { recursive: true });
4763
- for (const cp of (ag.configPaths || [])) {
4764
- if (fs.existsSync(cp.src)) {
4765
- try { fs.copyFileSync(cp.src, path.join(agDir, path.basename(cp.src))); agentsBacked++; } catch {}
4820
+ const sel = AGENT_SELECTIONS.find((s) => s.id === ag.id);
4821
+ const targets = sel ? sel.targets : [ag.id];
4822
+ for (const targetId of targets) {
4823
+ const reg = AGENT_REGISTRY[targetId];
4824
+ if (!reg) continue;
4825
+ const agDir = path.join(agentBackupDir, targetId);
4826
+ fs.mkdirSync(agDir, { recursive: true });
4827
+ // Back up rules file
4828
+ if (reg.rules && reg.rules.dest) {
4829
+ try {
4830
+ const rulesPath = reg.rules.dest(vaultPath);
4831
+ if (fs.existsSync(rulesPath)) {
4832
+ fs.copyFileSync(rulesPath, path.join(agDir, path.basename(rulesPath)));
4833
+ agentsBacked++;
4834
+ }
4835
+ } catch {}
4836
+ }
4837
+ // Back up skills directory
4838
+ if (reg.skills && reg.skills.dest) {
4839
+ try {
4840
+ const skillsDir = reg.skills.dest(vaultPath);
4841
+ if (fs.existsSync(skillsDir)) {
4842
+ copyDirRecursive(skillsDir, path.join(agDir, "skills"));
4843
+ agentsBacked++;
4844
+ }
4845
+ } catch {}
4766
4846
  }
4767
4847
  }
4768
4848
  }
4769
- statusLine("ok", "Backed up", `${agentsBacked} agent config files`);
4849
+ statusLine("ok", "Backed up", `${agentsBacked} agent config items`);
4770
4850
  } catch (err) { barLn(yellow(` Agent backup failed: ${err.message}`)); }
4771
4851
  }
4772
4852
  }
@@ -4867,7 +4947,7 @@ async function cmdUpdateComprehensive(opts, bundleDir, startTime) {
4867
4947
  }
4868
4948
 
4869
4949
  const writtenFiles = new Set();
4870
- const skillOpts = { install: true, categories: null, workflows: selectedWorkflows, skipHooks, skipRules, skipTemplates };
4950
+ const skillOpts = { install: true, categories: null, workflows: selectedWorkflows, skipHooks, skipRules, skipTemplates, statusLine: changes.statusline !== "unchanged" };
4871
4951
  for (const agent of selectedAgents) {
4872
4952
  const sel = AGENT_SELECTIONS.find((s) => s.id === agent.id);
4873
4953
  const targets = sel ? sel.targets : [agent.id];
@@ -4900,8 +4980,12 @@ async function cmdUpdateComprehensive(opts, bundleDir, startTime) {
4900
4980
  const catSet = new Set(selectedCatIds);
4901
4981
  const refreshOpts = { install: true, categories: catSet, workflows: null };
4902
4982
  for (const agent of selectedAgents) {
4903
- const fn = AGENT_INSTALLERS[agent.id];
4904
- if (fn) fn(bundleDir, vaultPath, refreshOpts, writtenFiles, agent.id);
4983
+ const sel = AGENT_SELECTIONS.find((s) => s.id === agent.id);
4984
+ const targets = sel ? sel.targets : [agent.id];
4985
+ for (const targetId of targets) {
4986
+ const fn = AGENT_INSTALLERS[targetId];
4987
+ if (fn) fn(bundleDir, vaultPath, refreshOpts, writtenFiles, targetId);
4988
+ }
4905
4989
  }
4906
4990
  const skillCount = allSkills.filter((s) => s.category === "tools" || catSet.has(s.category)).length;
4907
4991
  statusLine("ok", "Skills refreshed", `${skillCount} across ${selectedAgents.length} agent(s)`);
@@ -5284,8 +5368,8 @@ async function main() {
5284
5368
  ],
5285
5369
  { multi: false, defaultIndex: 1 }
5286
5370
  );
5287
- if (!ptChoice) return;
5288
- if (ptChoice === "yes") {
5371
+ if (!ptChoice || ptChoice === "no") { /* skip prayer setup */ }
5372
+ else if (ptChoice === "yes") {
5289
5373
  prayerSetup = true;
5290
5374
  barLn();
5291
5375
  question("How would you like to set up prayer times?");
@@ -5299,7 +5383,9 @@ async function main() {
5299
5383
  ],
5300
5384
  { multi: false, defaultIndex: 0 }
5301
5385
  );
5302
- if (!method || method === "later") { /* skip */ }
5386
+ if (!method || method === "later") {
5387
+ // User cancelled method pick — still enable the setting, no timetable
5388
+ } else {
5303
5389
 
5304
5390
  const moverDir = path.join(os.homedir(), ".mover");
5305
5391
  if (!fs.existsSync(moverDir)) fs.mkdirSync(moverDir, { recursive: true, mode: 0o700 });
@@ -5351,9 +5437,7 @@ async function main() {
5351
5437
  } else if (method === "fetch") {
5352
5438
  barLn();
5353
5439
  const city = await textInput({ label: "City (e.g. London, Watford, Istanbul)", placeholder: "London" });
5354
- if (city === null) return;
5355
- const country = await textInput({ label: "Country", placeholder: "United Kingdom" });
5356
- if (country === null) return;
5440
+ const country = city ? await textInput({ label: "Country", placeholder: "United Kingdom" }) : null;
5357
5441
  barLn();
5358
5442
 
5359
5443
  if (city && country) {
@@ -5371,6 +5455,7 @@ async function main() {
5371
5455
  }
5372
5456
  }
5373
5457
  // method === "later" → just enable the setting, no timetable yet
5458
+ } // end if method !== "later"
5374
5459
  }
5375
5460
  }
5376
5461
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mover-os",
3
- "version": "4.5.2",
3
+ "version": "4.5.3",
4
4
  "description": "The self-improving OS for AI agents. Turns Obsidian into an execution engine.",
5
5
  "bin": {
6
6
  "moveros": "install.js"