mover-os 4.3.0 → 4.3.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 +45 -1
  2. package/package.json +1 -1
package/install.js CHANGED
@@ -12,6 +12,7 @@ const readline = require("readline");
12
12
  const fs = require("fs");
13
13
  const path = require("path");
14
14
  const os = require("os");
15
+ const crypto = require("crypto");
15
16
  const { execSync } = require("child_process");
16
17
 
17
18
  const VERSION = "4";
@@ -1349,6 +1350,7 @@ function writeMoverConfig(vaultPath, agentIds, licenseKey) {
1349
1350
  const existing = JSON.parse(fs.readFileSync(configPath, "utf8"));
1350
1351
  if (existing.installedAt) config.installedAt = existing.installedAt;
1351
1352
  if (existing.licenseKey && !licenseKey) config.licenseKey = existing.licenseKey;
1353
+ if (existing.feedbackWebhook) config.feedbackWebhook = existing.feedbackWebhook;
1352
1354
  config.updatedAt = new Date().toISOString();
1353
1355
  } catch {}
1354
1356
  }
@@ -1519,17 +1521,52 @@ function installRules(bundleDir, destPath, agentId) {
1519
1521
  return true;
1520
1522
  }
1521
1523
 
1524
+ function computeSkillHash(dirPath) {
1525
+ const hash = crypto.createHash("sha256");
1526
+ const entries = fs.readdirSync(dirPath, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
1527
+ for (const entry of entries) {
1528
+ const full = path.join(dirPath, entry.name);
1529
+ if (entry.isFile()) {
1530
+ hash.update(entry.name);
1531
+ hash.update(fs.readFileSync(full));
1532
+ } else if (entry.isDirectory()) {
1533
+ hash.update(entry.name + "/");
1534
+ hash.update(computeSkillHash(full));
1535
+ }
1536
+ }
1537
+ return hash.digest("hex");
1538
+ }
1539
+
1522
1540
  function installSkillPacks(bundleDir, destDir, selectedCategories) {
1523
1541
  const skills = findSkills(bundleDir);
1524
1542
  fs.mkdirSync(destDir, { recursive: true });
1525
1543
  const installedNames = new Set();
1526
1544
  let count = 0;
1545
+ let skipped = 0;
1546
+
1547
+ // Load existing manifest for change detection
1548
+ const manifestPath = path.join(destDir, ".skill-manifest.json");
1549
+ let manifest = { skills: {} };
1550
+ try {
1551
+ if (fs.existsSync(manifestPath)) manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
1552
+ } catch {}
1553
+
1527
1554
  for (const skill of skills) {
1528
1555
  // Filter by category if categories were selected (tools always installed)
1529
1556
  if (selectedCategories && skill.category !== "tools" && !selectedCategories.has(skill.category)) continue;
1530
1557
  const dest = path.join(destDir, skill.name);
1558
+
1559
+ // Skip unchanged skills
1560
+ const sourceHash = computeSkillHash(skill.path);
1561
+ if (manifest.skills[skill.name]?.hash === sourceHash && fs.existsSync(dest)) {
1562
+ installedNames.add(skill.name);
1563
+ skipped++;
1564
+ continue;
1565
+ }
1566
+
1531
1567
  if (fs.existsSync(dest)) fs.rmSync(dest, { recursive: true, force: true });
1532
1568
  copyDirRecursive(skill.path, dest);
1569
+ manifest.skills[skill.name] = { hash: sourceHash, installedAt: new Date().toISOString() };
1533
1570
  installedNames.add(skill.name);
1534
1571
  count++;
1535
1572
  }
@@ -1549,6 +1586,11 @@ function installSkillPacks(bundleDir, destDir, selectedCategories) {
1549
1586
  }
1550
1587
  } catch (e) { /* skip */ }
1551
1588
  }
1589
+
1590
+ // Write manifest for next run's change detection
1591
+ try { fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n"); } catch {}
1592
+
1593
+ if (skipped > 0) ln(` ${dim(`${skipped} skills unchanged, skipped`)}`);
1552
1594
  return count;
1553
1595
  }
1554
1596
 
@@ -2080,6 +2122,7 @@ async function main() {
2080
2122
  }
2081
2123
 
2082
2124
  if (!key) {
2125
+ let validated = false;
2083
2126
  let attempts = 0;
2084
2127
  while (attempts < 3) {
2085
2128
  key = await textInput({
@@ -2093,6 +2136,7 @@ async function main() {
2093
2136
  if (valid) {
2094
2137
  sp.stop(green("License verified"));
2095
2138
  await activateKey(key);
2139
+ validated = true;
2096
2140
  break;
2097
2141
  }
2098
2142
 
@@ -2105,7 +2149,7 @@ async function main() {
2105
2149
  }
2106
2150
  }
2107
2151
 
2108
- if (!await validateKey(key)) {
2152
+ if (!validated) {
2109
2153
  barLn(red("Invalid license key."));
2110
2154
  barLn();
2111
2155
  barLn(dim("Get a key at https://moveros.dev"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mover-os",
3
- "version": "4.3.0",
3
+ "version": "4.3.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"