mover-os 4.6.5 → 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 +99 -17
- package/package.json +1 -1
package/install.js
CHANGED
|
@@ -30,6 +30,9 @@ function jsonOut(command, data, ok = true) {
|
|
|
30
30
|
process.stdout.write(JSON.stringify(envelope, null, 2) + "\n");
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
// ─── TTY detection (must be before output budget check) ──────────────────────
|
|
34
|
+
const IS_TTY = process.stdout.isTTY && process.stdin.isTTY;
|
|
35
|
+
|
|
33
36
|
// ─── Output budget (30K chars = Anthropic bash tool truncation limit) ────────
|
|
34
37
|
const MAX_OUTPUT_CHARS = 28000; // Leave 2K buffer below 30K limit
|
|
35
38
|
let _outputCharCount = 0;
|
|
@@ -51,7 +54,6 @@ if (!IS_TTY) {
|
|
|
51
54
|
const EXIT = { OK: 0, ERROR: 1, USAGE: 2, NOT_FOUND: 3, PERMISSION: 4, CONFLICT: 5, NETWORK: 6 };
|
|
52
55
|
|
|
53
56
|
// ─── ANSI ────────────────────────────────────────────────────────────────────
|
|
54
|
-
const IS_TTY = process.stdout.isTTY && process.stdin.isTTY;
|
|
55
57
|
const S = IS_TTY
|
|
56
58
|
? {
|
|
57
59
|
reset: "\x1b[0m",
|
|
@@ -1154,15 +1156,17 @@ function saveUpdateManifest(manifest) {
|
|
|
1154
1156
|
fs.writeFileSync(p, JSON.stringify(manifest, null, 2), "utf8");
|
|
1155
1157
|
}
|
|
1156
1158
|
|
|
1157
|
-
function savePristineCopy(category, srcFile) {
|
|
1159
|
+
function savePristineCopy(category, srcFile, relPath) {
|
|
1158
1160
|
const pristineDir = path.join(os.homedir(), ".mover", "installed", category);
|
|
1159
|
-
|
|
1161
|
+
const destName = relPath || path.basename(srcFile);
|
|
1162
|
+
const destPath = path.join(pristineDir, destName);
|
|
1163
|
+
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
1160
1164
|
const content = fs.readFileSync(srcFile, "utf8").replace(/\r\n/g, "\n");
|
|
1161
|
-
fs.writeFileSync(
|
|
1165
|
+
fs.writeFileSync(destPath, content, "utf8");
|
|
1162
1166
|
}
|
|
1163
1167
|
|
|
1164
|
-
function compareForUpdate(category, srcFile, destFile, manifest) {
|
|
1165
|
-
const fileName = `${category}/${path.basename(srcFile)}`;
|
|
1168
|
+
function compareForUpdate(category, srcFile, destFile, manifest, relPath) {
|
|
1169
|
+
const fileName = relPath ? `${category}/${relPath}` : `${category}/${path.basename(srcFile)}`;
|
|
1166
1170
|
const newHash = fileContentHash(srcFile);
|
|
1167
1171
|
const pristineHash = manifest?.files?.[fileName];
|
|
1168
1172
|
|
|
@@ -1310,9 +1314,9 @@ function diff3Merge(a, o, b) {
|
|
|
1310
1314
|
|
|
1311
1315
|
// ── Auto-Merge (uses diff3 + pristine snapshots) ──────────────────────────
|
|
1312
1316
|
|
|
1313
|
-
function tryAutoMerge(category, srcFile, destFile) {
|
|
1317
|
+
function tryAutoMerge(category, srcFile, destFile, relPath) {
|
|
1314
1318
|
const pristineDir = path.join(os.homedir(), ".mover", "installed", category);
|
|
1315
|
-
const pristineFile = path.join(pristineDir, path.basename(srcFile));
|
|
1319
|
+
const pristineFile = path.join(pristineDir, relPath || path.basename(srcFile));
|
|
1316
1320
|
if (!fs.existsSync(pristineFile)) return { success: false, reason: "no-pristine" };
|
|
1317
1321
|
|
|
1318
1322
|
const base = fs.readFileSync(pristineFile, "utf8").replace(/\r\n/g, "\n");
|
|
@@ -2473,18 +2477,21 @@ function writeMoverConfig(vaultPath, agentIds, licenseKey, opts = {}) {
|
|
|
2473
2477
|
|
|
2474
2478
|
function installTemplateFiles(bundleDir, vaultPath) {
|
|
2475
2479
|
const structDir = path.join(bundleDir, "src", "structure");
|
|
2476
|
-
if (!fs.existsSync(structDir)) return 0;
|
|
2480
|
+
if (!fs.existsSync(structDir)) return { count: 0, hashes: {} };
|
|
2477
2481
|
|
|
2478
2482
|
let count = 0;
|
|
2483
|
+
const hashes = {};
|
|
2479
2484
|
const walk = (dir, rel) => {
|
|
2480
2485
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
2481
2486
|
const srcPath = path.join(dir, entry.name);
|
|
2482
|
-
const
|
|
2487
|
+
const entryRel = path.join(rel, entry.name);
|
|
2488
|
+
const destPath = path.join(vaultPath, entryRel);
|
|
2483
2489
|
|
|
2484
2490
|
if (entry.isDirectory()) {
|
|
2485
|
-
walk(srcPath,
|
|
2491
|
+
walk(srcPath, entryRel);
|
|
2486
2492
|
} else {
|
|
2487
|
-
const relNorm =
|
|
2493
|
+
const relNorm = entryRel.replace(/\\/g, "/");
|
|
2494
|
+
// Engine files are SACRED — never overwrite existing
|
|
2488
2495
|
if (relNorm.includes("02_Areas") && relNorm.includes("Engine") && fs.existsSync(destPath)) {
|
|
2489
2496
|
continue;
|
|
2490
2497
|
}
|
|
@@ -2493,11 +2500,17 @@ function installTemplateFiles(bundleDir, vaultPath) {
|
|
|
2493
2500
|
fs.copyFileSync(srcPath, destPath);
|
|
2494
2501
|
count++;
|
|
2495
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
|
+
}
|
|
2496
2509
|
}
|
|
2497
2510
|
}
|
|
2498
2511
|
};
|
|
2499
2512
|
walk(structDir, "");
|
|
2500
|
-
return count;
|
|
2513
|
+
return { count, hashes };
|
|
2501
2514
|
}
|
|
2502
2515
|
|
|
2503
2516
|
function installWorkflows(bundleDir, destDir, selectedWorkflows) {
|
|
@@ -5323,9 +5336,9 @@ async function cmdUpdateComprehensive(opts, bundleDir, startTime) {
|
|
|
5323
5336
|
statusLine("ok", "/update", "workflow synced");
|
|
5324
5337
|
}
|
|
5325
5338
|
|
|
5326
|
-
// Vault structure
|
|
5339
|
+
// Vault structure (folders only — no file changes)
|
|
5327
5340
|
createVaultStructure(vaultPath);
|
|
5328
|
-
|
|
5341
|
+
// NOTE: templates are now handled via smart merge below (after manifest load), NOT installTemplateFiles()
|
|
5329
5342
|
|
|
5330
5343
|
// Update version marker
|
|
5331
5344
|
fs.writeFileSync(path.join(vaultPath, ".mover-version"), `${require("./package.json").version}\n`, "utf8");
|
|
@@ -5390,6 +5403,70 @@ async function cmdUpdateComprehensive(opts, bundleDir, startTime) {
|
|
|
5390
5403
|
}
|
|
5391
5404
|
}
|
|
5392
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
|
+
|
|
5393
5470
|
// ── Rules: keep sentinel merge + track pristine ──
|
|
5394
5471
|
if (changes.rules === "changed") {
|
|
5395
5472
|
const rulesSrc = path.join(bundleDir, "src", "system", "Mover_Global_Rules.md");
|
|
@@ -6149,11 +6226,12 @@ async function main() {
|
|
|
6149
6226
|
totalSteps++;
|
|
6150
6227
|
|
|
6151
6228
|
// 2. Template files (runs in both modes — only creates missing files, never overwrites)
|
|
6229
|
+
let tmplResult = { count: 0, hashes: {} };
|
|
6152
6230
|
if (!skipTemplates) {
|
|
6153
6231
|
sp = spinner("Engine templates");
|
|
6154
|
-
|
|
6232
|
+
tmplResult = installTemplateFiles(bundleDir, vaultPath);
|
|
6155
6233
|
await sleep(200);
|
|
6156
|
-
sp.stop(`Engine templates${
|
|
6234
|
+
sp.stop(`Engine templates${tmplResult.count > 0 ? dim(` ${tmplResult.count} files`) : dim(" up to date")}`);
|
|
6157
6235
|
totalSteps++;
|
|
6158
6236
|
}
|
|
6159
6237
|
|
|
@@ -6275,6 +6353,10 @@ async function main() {
|
|
|
6275
6353
|
mfst.files[`hooks/${f}`] = fileContentHash(path.join(hooksDir, f));
|
|
6276
6354
|
}
|
|
6277
6355
|
}
|
|
6356
|
+
// Template files (vault scaffolding — installTemplateFiles now returns hashes)
|
|
6357
|
+
if (tmplResult && tmplResult.hashes) {
|
|
6358
|
+
Object.assign(mfst.files, tmplResult.hashes);
|
|
6359
|
+
}
|
|
6278
6360
|
saveUpdateManifest(mfst);
|
|
6279
6361
|
} catch {}
|
|
6280
6362
|
|