mover-os 4.6.6 → 4.6.7
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.
- package/install.js +96 -16
- package/package.json +1 -1
package/install.js
CHANGED
|
@@ -1156,15 +1156,17 @@ function saveUpdateManifest(manifest) {
|
|
|
1156
1156
|
fs.writeFileSync(p, JSON.stringify(manifest, null, 2), "utf8");
|
|
1157
1157
|
}
|
|
1158
1158
|
|
|
1159
|
-
function savePristineCopy(category, srcFile) {
|
|
1159
|
+
function savePristineCopy(category, srcFile, relPath) {
|
|
1160
1160
|
const pristineDir = path.join(os.homedir(), ".mover", "installed", category);
|
|
1161
|
-
|
|
1161
|
+
const destName = relPath || path.basename(srcFile);
|
|
1162
|
+
const destPath = path.join(pristineDir, destName);
|
|
1163
|
+
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
1162
1164
|
const content = fs.readFileSync(srcFile, "utf8").replace(/\r\n/g, "\n");
|
|
1163
|
-
fs.writeFileSync(
|
|
1165
|
+
fs.writeFileSync(destPath, content, "utf8");
|
|
1164
1166
|
}
|
|
1165
1167
|
|
|
1166
|
-
function compareForUpdate(category, srcFile, destFile, manifest) {
|
|
1167
|
-
const fileName = `${category}/${path.basename(srcFile)}`;
|
|
1168
|
+
function compareForUpdate(category, srcFile, destFile, manifest, relPath) {
|
|
1169
|
+
const fileName = relPath ? `${category}/${relPath}` : `${category}/${path.basename(srcFile)}`;
|
|
1168
1170
|
const newHash = fileContentHash(srcFile);
|
|
1169
1171
|
const pristineHash = manifest?.files?.[fileName];
|
|
1170
1172
|
|
|
@@ -1312,9 +1314,9 @@ function diff3Merge(a, o, b) {
|
|
|
1312
1314
|
|
|
1313
1315
|
// ── Auto-Merge (uses diff3 + pristine snapshots) ──────────────────────────
|
|
1314
1316
|
|
|
1315
|
-
function tryAutoMerge(category, srcFile, destFile) {
|
|
1317
|
+
function tryAutoMerge(category, srcFile, destFile, relPath) {
|
|
1316
1318
|
const pristineDir = path.join(os.homedir(), ".mover", "installed", category);
|
|
1317
|
-
const pristineFile = path.join(pristineDir, path.basename(srcFile));
|
|
1319
|
+
const pristineFile = path.join(pristineDir, relPath || path.basename(srcFile));
|
|
1318
1320
|
if (!fs.existsSync(pristineFile)) return { success: false, reason: "no-pristine" };
|
|
1319
1321
|
|
|
1320
1322
|
const base = fs.readFileSync(pristineFile, "utf8").replace(/\r\n/g, "\n");
|
|
@@ -2475,18 +2477,21 @@ function writeMoverConfig(vaultPath, agentIds, licenseKey, opts = {}) {
|
|
|
2475
2477
|
|
|
2476
2478
|
function installTemplateFiles(bundleDir, vaultPath) {
|
|
2477
2479
|
const structDir = path.join(bundleDir, "src", "structure");
|
|
2478
|
-
if (!fs.existsSync(structDir)) return 0;
|
|
2480
|
+
if (!fs.existsSync(structDir)) return { count: 0, hashes: {} };
|
|
2479
2481
|
|
|
2480
2482
|
let count = 0;
|
|
2483
|
+
const hashes = {};
|
|
2481
2484
|
const walk = (dir, rel) => {
|
|
2482
2485
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
2483
2486
|
const srcPath = path.join(dir, entry.name);
|
|
2484
|
-
const
|
|
2487
|
+
const entryRel = path.join(rel, entry.name);
|
|
2488
|
+
const destPath = path.join(vaultPath, entryRel);
|
|
2485
2489
|
|
|
2486
2490
|
if (entry.isDirectory()) {
|
|
2487
|
-
walk(srcPath,
|
|
2491
|
+
walk(srcPath, entryRel);
|
|
2488
2492
|
} else {
|
|
2489
|
-
const relNorm =
|
|
2493
|
+
const relNorm = entryRel.replace(/\\/g, "/");
|
|
2494
|
+
// Engine files are SACRED — never overwrite existing
|
|
2490
2495
|
if (relNorm.includes("02_Areas") && relNorm.includes("Engine") && fs.existsSync(destPath)) {
|
|
2491
2496
|
continue;
|
|
2492
2497
|
}
|
|
@@ -2495,11 +2500,17 @@ function installTemplateFiles(bundleDir, vaultPath) {
|
|
|
2495
2500
|
fs.copyFileSync(srcPath, destPath);
|
|
2496
2501
|
count++;
|
|
2497
2502
|
}
|
|
2503
|
+
// Track hash and save pristine copy for ALL non-Engine template files
|
|
2504
|
+
if (!(relNorm.includes("02_Areas") && relNorm.includes("Engine"))) {
|
|
2505
|
+
const relKey = relNorm;
|
|
2506
|
+
hashes[`templates/${relKey}`] = fileContentHash(srcPath);
|
|
2507
|
+
savePristineCopy("templates", srcPath, relKey);
|
|
2508
|
+
}
|
|
2498
2509
|
}
|
|
2499
2510
|
}
|
|
2500
2511
|
};
|
|
2501
2512
|
walk(structDir, "");
|
|
2502
|
-
return count;
|
|
2513
|
+
return { count, hashes };
|
|
2503
2514
|
}
|
|
2504
2515
|
|
|
2505
2516
|
function installWorkflows(bundleDir, destDir, selectedWorkflows) {
|
|
@@ -5325,9 +5336,9 @@ async function cmdUpdateComprehensive(opts, bundleDir, startTime) {
|
|
|
5325
5336
|
statusLine("ok", "/update", "workflow synced");
|
|
5326
5337
|
}
|
|
5327
5338
|
|
|
5328
|
-
// Vault structure
|
|
5339
|
+
// Vault structure (folders only — no file changes)
|
|
5329
5340
|
createVaultStructure(vaultPath);
|
|
5330
|
-
|
|
5341
|
+
// NOTE: templates are now handled via smart merge below (after manifest load), NOT installTemplateFiles()
|
|
5331
5342
|
|
|
5332
5343
|
// Update version marker
|
|
5333
5344
|
fs.writeFileSync(path.join(vaultPath, ".mover-version"), `${require("./package.json").version}\n`, "utf8");
|
|
@@ -5392,6 +5403,70 @@ async function cmdUpdateComprehensive(opts, bundleDir, startTime) {
|
|
|
5392
5403
|
}
|
|
5393
5404
|
}
|
|
5394
5405
|
|
|
5406
|
+
// ── Templates: vault scaffold files (same smart merge as workflows) ──
|
|
5407
|
+
const tmplSrcDir = path.join(bundleDir, "src", "structure");
|
|
5408
|
+
if (fs.existsSync(tmplSrcDir)) {
|
|
5409
|
+
const walkTmplUpdate = (dir, rel) => {
|
|
5410
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
5411
|
+
const srcPath = path.join(dir, entry.name);
|
|
5412
|
+
const entryRel = path.join(rel, entry.name);
|
|
5413
|
+
if (entry.isDirectory()) {
|
|
5414
|
+
walkTmplUpdate(srcPath, entryRel);
|
|
5415
|
+
continue;
|
|
5416
|
+
}
|
|
5417
|
+
const relNorm = entryRel.replace(/\\/g, "/");
|
|
5418
|
+
// Engine files are SACRED — never touch via update
|
|
5419
|
+
if (relNorm.includes("02_Areas") && relNorm.includes("Engine")) continue;
|
|
5420
|
+
|
|
5421
|
+
const destFile = path.join(vaultPath, entryRel);
|
|
5422
|
+
const result = compareForUpdate("templates", srcPath, destFile, manifest, relNorm);
|
|
5423
|
+
|
|
5424
|
+
switch (result.action) {
|
|
5425
|
+
case "install":
|
|
5426
|
+
fs.mkdirSync(path.dirname(destFile), { recursive: true });
|
|
5427
|
+
fs.copyFileSync(srcPath, destFile);
|
|
5428
|
+
savePristineCopy("templates", srcPath, relNorm);
|
|
5429
|
+
updated.push(relNorm);
|
|
5430
|
+
break;
|
|
5431
|
+
case "safe-overwrite":
|
|
5432
|
+
fs.copyFileSync(srcPath, destFile);
|
|
5433
|
+
savePristineCopy("templates", srcPath, relNorm);
|
|
5434
|
+
updated.push(relNorm);
|
|
5435
|
+
break;
|
|
5436
|
+
case "user-only-skip":
|
|
5437
|
+
userOnly.push(relNorm);
|
|
5438
|
+
break;
|
|
5439
|
+
case "skip":
|
|
5440
|
+
skippedFiles.push(relNorm);
|
|
5441
|
+
break;
|
|
5442
|
+
case "legacy-overwrite": {
|
|
5443
|
+
// First update with manifest — backup then overwrite
|
|
5444
|
+
const backupDir = path.join(os.homedir(), ".mover", "backups", `pre-update-${new Date().toISOString().slice(0, 10)}`);
|
|
5445
|
+
fs.mkdirSync(path.join(backupDir, "templates", path.dirname(relNorm)), { recursive: true });
|
|
5446
|
+
if (fs.existsSync(destFile)) fs.copyFileSync(destFile, path.join(backupDir, "templates", relNorm));
|
|
5447
|
+
fs.copyFileSync(srcPath, destFile);
|
|
5448
|
+
savePristineCopy("templates", srcPath, relNorm);
|
|
5449
|
+
updated.push(relNorm);
|
|
5450
|
+
break;
|
|
5451
|
+
}
|
|
5452
|
+
case "conflict": {
|
|
5453
|
+
const mergeResult = tryAutoMerge("templates", srcPath, destFile, relNorm);
|
|
5454
|
+
if (mergeResult.success) {
|
|
5455
|
+
fs.writeFileSync(destFile, mergeResult.content, "utf8");
|
|
5456
|
+
savePristineCopy("templates", srcPath, relNorm);
|
|
5457
|
+
autoMerged.push(relNorm);
|
|
5458
|
+
} else {
|
|
5459
|
+
conflicts.push({ name: relNorm, reason: mergeResult.reason, conflictCount: mergeResult.conflictCount });
|
|
5460
|
+
}
|
|
5461
|
+
break;
|
|
5462
|
+
}
|
|
5463
|
+
}
|
|
5464
|
+
newManifest.files[`templates/${relNorm}`] = result.newHash;
|
|
5465
|
+
}
|
|
5466
|
+
};
|
|
5467
|
+
walkTmplUpdate(tmplSrcDir, "");
|
|
5468
|
+
}
|
|
5469
|
+
|
|
5395
5470
|
// ── Rules: keep sentinel merge + track pristine ──
|
|
5396
5471
|
if (changes.rules === "changed") {
|
|
5397
5472
|
const rulesSrc = path.join(bundleDir, "src", "system", "Mover_Global_Rules.md");
|
|
@@ -6151,11 +6226,12 @@ async function main() {
|
|
|
6151
6226
|
totalSteps++;
|
|
6152
6227
|
|
|
6153
6228
|
// 2. Template files (runs in both modes — only creates missing files, never overwrites)
|
|
6229
|
+
let tmplResult = { count: 0, hashes: {} };
|
|
6154
6230
|
if (!skipTemplates) {
|
|
6155
6231
|
sp = spinner("Engine templates");
|
|
6156
|
-
|
|
6232
|
+
tmplResult = installTemplateFiles(bundleDir, vaultPath);
|
|
6157
6233
|
await sleep(200);
|
|
6158
|
-
sp.stop(`Engine templates${
|
|
6234
|
+
sp.stop(`Engine templates${tmplResult.count > 0 ? dim(` ${tmplResult.count} files`) : dim(" up to date")}`);
|
|
6159
6235
|
totalSteps++;
|
|
6160
6236
|
}
|
|
6161
6237
|
|
|
@@ -6277,6 +6353,10 @@ async function main() {
|
|
|
6277
6353
|
mfst.files[`hooks/${f}`] = fileContentHash(path.join(hooksDir, f));
|
|
6278
6354
|
}
|
|
6279
6355
|
}
|
|
6356
|
+
// Template files (vault scaffolding — installTemplateFiles now returns hashes)
|
|
6357
|
+
if (tmplResult && tmplResult.hashes) {
|
|
6358
|
+
Object.assign(mfst.files, tmplResult.hashes);
|
|
6359
|
+
}
|
|
6280
6360
|
saveUpdateManifest(mfst);
|
|
6281
6361
|
} catch {}
|
|
6282
6362
|
|