mover-os 4.5.0 → 4.5.2

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 +72 -5
  2. package/package.json +1 -1
package/install.js CHANGED
@@ -954,7 +954,7 @@ function compareVersions(a, b) {
954
954
  // ─── Change detection (update mode) ─────────────────────────────────────────
955
955
  function detectChanges(bundleDir, vaultPath, selectedAgentIds) {
956
956
  const home = os.homedir();
957
- const result = { workflows: [], hooks: [], rules: null, templates: [] };
957
+ const result = { workflows: [], hooks: [], rules: null, templates: [], skills: [], statusline: "unchanged" };
958
958
 
959
959
  // --- Workflows: compare source vs first installed destination ---
960
960
  const wfSrc = path.join(bundleDir, "src", "workflows");
@@ -1046,6 +1046,49 @@ function detectChanges(bundleDir, vaultPath, selectedAgentIds) {
1046
1046
  walkTemplates(structDir, "");
1047
1047
  }
1048
1048
 
1049
+ // --- Skills: compare source vs installed ---
1050
+ const skillsSrc = path.join(bundleDir, "src", "skills");
1051
+ 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"),
1055
+ ].filter(Boolean);
1056
+ const skillsDest = skillsDests.find((d) => fs.existsSync(d));
1057
+ if (fs.existsSync(skillsSrc) && skillsDest) {
1058
+ const walkSkills = (dir) => {
1059
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
1060
+ const full = path.join(dir, entry.name);
1061
+ if (entry.isDirectory()) {
1062
+ const skillFile = path.join(full, "SKILL.md");
1063
+ if (fs.existsSync(skillFile)) {
1064
+ const srcContent = fs.readFileSync(skillFile, "utf8");
1065
+ const destSkill = path.join(skillsDest, entry.name, "SKILL.md");
1066
+ if (!fs.existsSync(destSkill)) {
1067
+ result.skills.push({ file: entry.name, status: "new" });
1068
+ } else {
1069
+ const destContent = fs.readFileSync(destSkill, "utf8");
1070
+ result.skills.push({ file: entry.name, status: srcContent === destContent ? "unchanged" : "changed" });
1071
+ }
1072
+ } else {
1073
+ walkSkills(full);
1074
+ }
1075
+ }
1076
+ }
1077
+ };
1078
+ walkSkills(skillsSrc);
1079
+ }
1080
+
1081
+ // --- Statusline ---
1082
+ const slSrc = path.join(bundleDir, "src", "hooks", "statusline.js");
1083
+ const slDest = path.join(home, ".claude", "statusline.js");
1084
+ if (fs.existsSync(slSrc) && fs.existsSync(slDest) && selectedAgentIds.includes("claude-code")) {
1085
+ const srcSl = fs.readFileSync(slSrc, "utf8").replace(/\r\n/g, "\n");
1086
+ const destSl = fs.readFileSync(slDest, "utf8").replace(/\r\n/g, "\n");
1087
+ result.statusline = srcSl === destSl ? "unchanged" : "changed";
1088
+ } else if (fs.existsSync(slSrc) && !fs.existsSync(slDest) && selectedAgentIds.includes("claude-code")) {
1089
+ result.statusline = "new";
1090
+ }
1091
+
1049
1092
  return result;
1050
1093
  }
1051
1094
 
@@ -1055,6 +1098,8 @@ function countChanges(changes) {
1055
1098
  n += changes.hooks.filter((f) => f.status !== "unchanged").length;
1056
1099
  if (changes.rules === "changed") n++;
1057
1100
  n += changes.templates.filter((f) => f.status !== "unchanged").length;
1101
+ n += (changes.skills || []).filter((f) => f.status !== "unchanged").length;
1102
+ if (changes.statusline === "changed" || changes.statusline === "new") n++;
1058
1103
  return n;
1059
1104
  }
1060
1105
 
@@ -1102,6 +1147,22 @@ function displayChangeSummary(changes, installedVersion, newVersion) {
1102
1147
  } else {
1103
1148
  barLn(` Templates: ${dim("unchanged")}`);
1104
1149
  }
1150
+
1151
+ // Skills
1152
+ const skChanged = (changes.skills || []).filter((f) => f.status === "changed");
1153
+ const skNew = (changes.skills || []).filter((f) => f.status === "new");
1154
+ const skUnchanged = (changes.skills || []).filter((f) => f.status === "unchanged");
1155
+ if (skChanged.length > 0 || skNew.length > 0 || skUnchanged.length > 0) {
1156
+ barLn(` Skills ${dim(`(${skChanged.length + skNew.length} changed, ${skUnchanged.length} unchanged)`)}:`);
1157
+ for (const f of skChanged) barLn(` ${yellow("✦")} ${f.file}`);
1158
+ for (const f of skNew) barLn(` ${green("+")} ${f.file} ${dim("(new)")}`);
1159
+ if (skChanged.length === 0 && skNew.length === 0) barLn(` ${dim("all up to date")}`);
1160
+ }
1161
+
1162
+ // Statusline
1163
+ if (changes.statusline === "changed") barLn(` Statusline: ${yellow("changed")}`);
1164
+ else if (changes.statusline === "new") barLn(` Statusline: ${green("new")}`);
1165
+
1105
1166
  barLn();
1106
1167
  }
1107
1168
 
@@ -4575,17 +4636,22 @@ async function cmdUpdateComprehensive(opts, bundleDir, startTime) {
4575
4636
  try { updateKey = JSON.parse(fs.readFileSync(cfgPath, "utf8")).licenseKey; } catch {}
4576
4637
  }
4577
4638
  }
4639
+ const keyFromConfig = !!updateKey;
4578
4640
  if (!updateKey) {
4579
4641
  updateKey = await textInput({ label: "License key", mask: "\u25AA", placeholder: "MOVER-XXXX-XXXX" });
4580
4642
  if (!updateKey) return;
4581
4643
  }
4582
4644
  const sp1 = spinner("Validating license");
4583
- if (!await validateKey(updateKey)) {
4645
+ const keyValid = await validateKey(updateKey);
4646
+ if (!keyValid && keyFromConfig) {
4647
+ sp1.stop(dim("License check skipped (offline — using stored key)"));
4648
+ } else if (!keyValid) {
4584
4649
  sp1.stop(red("Invalid key"));
4585
4650
  outro(red("Valid license key required."));
4586
4651
  process.exit(1);
4652
+ } else {
4653
+ sp1.stop(green("License verified"));
4587
4654
  }
4588
- sp1.stop(green("License verified"));
4589
4655
  barLn();
4590
4656
 
4591
4657
  // Download payload if not bundled
@@ -4715,7 +4781,7 @@ async function cmdUpdateComprehensive(opts, bundleDir, startTime) {
4715
4781
  if (fs.existsSync(cfgPath)) {
4716
4782
  try { currentAgents = JSON.parse(fs.readFileSync(cfgPath, "utf8")).agents || []; } catch {}
4717
4783
  }
4718
- const preSelectedAgents = currentAgents.length > 0 ? currentAgents : detectedIds;
4784
+ const preSelectedAgents = [...new Set([...detectedIds, ...currentAgents])];
4719
4785
 
4720
4786
  question(`Agents ${dim("(add or remove)")}`);
4721
4787
  barLn();
@@ -5101,7 +5167,8 @@ async function main() {
5101
5167
  barLn(dim(" Use " + bold("moveros update") + " to refresh agents, rules, and skills."));
5102
5168
  barLn(dim(" Use " + bold("moveros uninstall") + " to remove Mover OS."));
5103
5169
  barLn();
5104
- outro("Use update or uninstall instead.");
5170
+ barLn(dim(" esc to exit"));
5171
+ await waitForEsc();
5105
5172
  return;
5106
5173
  }
5107
5174
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mover-os",
3
- "version": "4.5.0",
3
+ "version": "4.5.2",
4
4
  "description": "The self-improving OS for AI agents. Turns Obsidian into an execution engine.",
5
5
  "bin": {
6
6
  "moveros": "install.js"