mover-os 4.5.1 → 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.
- package/install.js +206 -60
- 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
|
-
|
|
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
|
-
|
|
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
|
|
@@ -954,14 +947,17 @@ function compareVersions(a, b) {
|
|
|
954
947
|
// ─── Change detection (update mode) ─────────────────────────────────────────
|
|
955
948
|
function detectChanges(bundleDir, vaultPath, selectedAgentIds) {
|
|
956
949
|
const home = os.homedir();
|
|
957
|
-
const result = { workflows: [], hooks: [], rules: null, templates: [] };
|
|
950
|
+
const result = { workflows: [], hooks: [], rules: null, templates: [], skills: [], statusline: "unchanged" };
|
|
951
|
+
|
|
952
|
+
// Expand selection IDs to target IDs (e.g. "gemini-cli" → includes "antigravity")
|
|
953
|
+
const targetIds = expandTargetIds(selectedAgentIds);
|
|
958
954
|
|
|
959
955
|
// --- Workflows: compare source vs first installed destination ---
|
|
960
956
|
const wfSrc = path.join(bundleDir, "src", "workflows");
|
|
961
957
|
const wfDests = [
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
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) &&
|
|
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
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
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) {
|
|
@@ -1046,6 +1042,49 @@ function detectChanges(bundleDir, vaultPath, selectedAgentIds) {
|
|
|
1046
1042
|
walkTemplates(structDir, "");
|
|
1047
1043
|
}
|
|
1048
1044
|
|
|
1045
|
+
// --- Skills: compare source vs installed ---
|
|
1046
|
+
const skillsSrc = path.join(bundleDir, "src", "skills");
|
|
1047
|
+
const skillsDests = [
|
|
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"),
|
|
1051
|
+
].filter(Boolean);
|
|
1052
|
+
const skillsDest = skillsDests.find((d) => fs.existsSync(d));
|
|
1053
|
+
if (fs.existsSync(skillsSrc) && skillsDest) {
|
|
1054
|
+
const walkSkills = (dir) => {
|
|
1055
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
1056
|
+
const full = path.join(dir, entry.name);
|
|
1057
|
+
if (entry.isDirectory()) {
|
|
1058
|
+
const skillFile = path.join(full, "SKILL.md");
|
|
1059
|
+
if (fs.existsSync(skillFile)) {
|
|
1060
|
+
const srcContent = fs.readFileSync(skillFile, "utf8");
|
|
1061
|
+
const destSkill = path.join(skillsDest, entry.name, "SKILL.md");
|
|
1062
|
+
if (!fs.existsSync(destSkill)) {
|
|
1063
|
+
result.skills.push({ file: entry.name, status: "new" });
|
|
1064
|
+
} else {
|
|
1065
|
+
const destContent = fs.readFileSync(destSkill, "utf8");
|
|
1066
|
+
result.skills.push({ file: entry.name, status: srcContent === destContent ? "unchanged" : "changed" });
|
|
1067
|
+
}
|
|
1068
|
+
} else {
|
|
1069
|
+
walkSkills(full);
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
};
|
|
1074
|
+
walkSkills(skillsSrc);
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
// --- Statusline ---
|
|
1078
|
+
const slSrc = path.join(bundleDir, "src", "hooks", "statusline.js");
|
|
1079
|
+
const slDest = path.join(home, ".claude", "statusline.js");
|
|
1080
|
+
if (fs.existsSync(slSrc) && fs.existsSync(slDest) && targetIds.includes("claude-code")) {
|
|
1081
|
+
const srcSl = fs.readFileSync(slSrc, "utf8").replace(/\r\n/g, "\n");
|
|
1082
|
+
const destSl = fs.readFileSync(slDest, "utf8").replace(/\r\n/g, "\n");
|
|
1083
|
+
result.statusline = srcSl === destSl ? "unchanged" : "changed";
|
|
1084
|
+
} else if (fs.existsSync(slSrc) && !fs.existsSync(slDest) && targetIds.includes("claude-code")) {
|
|
1085
|
+
result.statusline = "new";
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1049
1088
|
return result;
|
|
1050
1089
|
}
|
|
1051
1090
|
|
|
@@ -1055,6 +1094,8 @@ function countChanges(changes) {
|
|
|
1055
1094
|
n += changes.hooks.filter((f) => f.status !== "unchanged").length;
|
|
1056
1095
|
if (changes.rules === "changed") n++;
|
|
1057
1096
|
n += changes.templates.filter((f) => f.status !== "unchanged").length;
|
|
1097
|
+
n += (changes.skills || []).filter((f) => f.status !== "unchanged").length;
|
|
1098
|
+
if (changes.statusline === "changed" || changes.statusline === "new") n++;
|
|
1058
1099
|
return n;
|
|
1059
1100
|
}
|
|
1060
1101
|
|
|
@@ -1102,6 +1143,22 @@ function displayChangeSummary(changes, installedVersion, newVersion) {
|
|
|
1102
1143
|
} else {
|
|
1103
1144
|
barLn(` Templates: ${dim("unchanged")}`);
|
|
1104
1145
|
}
|
|
1146
|
+
|
|
1147
|
+
// Skills
|
|
1148
|
+
const skChanged = (changes.skills || []).filter((f) => f.status === "changed");
|
|
1149
|
+
const skNew = (changes.skills || []).filter((f) => f.status === "new");
|
|
1150
|
+
const skUnchanged = (changes.skills || []).filter((f) => f.status === "unchanged");
|
|
1151
|
+
if (skChanged.length > 0 || skNew.length > 0 || skUnchanged.length > 0) {
|
|
1152
|
+
barLn(` Skills ${dim(`(${skChanged.length + skNew.length} changed, ${skUnchanged.length} unchanged)`)}:`);
|
|
1153
|
+
for (const f of skChanged) barLn(` ${yellow("✦")} ${f.file}`);
|
|
1154
|
+
for (const f of skNew) barLn(` ${green("+")} ${f.file} ${dim("(new)")}`);
|
|
1155
|
+
if (skChanged.length === 0 && skNew.length === 0) barLn(` ${dim("all up to date")}`);
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
// Statusline
|
|
1159
|
+
if (changes.statusline === "changed") barLn(` Statusline: ${yellow("changed")}`);
|
|
1160
|
+
else if (changes.statusline === "new") barLn(` Statusline: ${green("new")}`);
|
|
1161
|
+
|
|
1105
1162
|
barLn();
|
|
1106
1163
|
}
|
|
1107
1164
|
|
|
@@ -1566,6 +1623,17 @@ const AGENTS = AGENT_SELECTIONS.map((s) => ({
|
|
|
1566
1623
|
detect: AGENT_REGISTRY[s.targets[0]].detect,
|
|
1567
1624
|
}));
|
|
1568
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
|
+
|
|
1569
1637
|
// ─── Utility functions ──────────────────────────────────────────────────────
|
|
1570
1638
|
function cmdExists(cmd) {
|
|
1571
1639
|
try {
|
|
@@ -2052,6 +2120,8 @@ function writeMoverConfig(vaultPath, agentIds, licenseKey, opts = {}) {
|
|
|
2052
2120
|
if (existing.settings) config.settings = { ...existing.settings };
|
|
2053
2121
|
// Preserve prayer_times fallback
|
|
2054
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;
|
|
2055
2125
|
config.updatedAt = new Date().toISOString();
|
|
2056
2126
|
} catch {}
|
|
2057
2127
|
}
|
|
@@ -2970,7 +3040,8 @@ async function cmdDoctor(opts) {
|
|
|
2970
3040
|
// Per-agent checks
|
|
2971
3041
|
barLn();
|
|
2972
3042
|
barLn(dim(" Agents:"));
|
|
2973
|
-
|
|
3043
|
+
let cfgData = {};
|
|
3044
|
+
if (fs.existsSync(cfg)) { try { cfgData = JSON.parse(fs.readFileSync(cfg, "utf8")); } catch { statusLine("warn", "Config", "corrupt JSON"); } }
|
|
2974
3045
|
const installedAgents = cfgData.agents || [];
|
|
2975
3046
|
if (installedAgents.length === 0) {
|
|
2976
3047
|
barLn(dim(" No agents recorded in config."));
|
|
@@ -2996,7 +3067,7 @@ async function cmdDoctor(opts) {
|
|
|
2996
3067
|
const hasCmds = fs.existsSync(cp) && fs.readdirSync(cp).length > 0;
|
|
2997
3068
|
checks.push(hasCmds ? "commands" : dim("no commands"));
|
|
2998
3069
|
}
|
|
2999
|
-
const allOk = checks.every((c) =>
|
|
3070
|
+
const allOk = checks.every((c) => c === strip(c)); // styled text = problem
|
|
3000
3071
|
statusLine(allOk ? "ok" : "warn", ` ${reg.name}`, checks.join(", "));
|
|
3001
3072
|
}
|
|
3002
3073
|
|
|
@@ -3041,7 +3112,7 @@ async function cmdPulse(opts) {
|
|
|
3041
3112
|
const focusMatch = ac.match(/\*\*Focus:\*\*\s*(.+)/i) || ac.match(/Single Test:\s*(.+)/i);
|
|
3042
3113
|
if (focusMatch) focus = focusMatch[1].trim();
|
|
3043
3114
|
// Blockers
|
|
3044
|
-
const blockerSection = ac.match(/##.*Blocker[s]?[\s\S]*?(?=\n##|\n
|
|
3115
|
+
const blockerSection = ac.match(/##.*Blocker[s]?[\s\S]*?(?=\n##|\n---|$)/i);
|
|
3045
3116
|
if (blockerSection) {
|
|
3046
3117
|
const lines = blockerSection[0].split("\n").filter((l) => l.trim().startsWith("-") || l.trim().startsWith("*"));
|
|
3047
3118
|
blockers = lines.map((l) => l.replace(/^[\s*-]+/, "").trim()).filter(Boolean).slice(0, 3);
|
|
@@ -3430,7 +3501,7 @@ async function cmdReplay(opts) {
|
|
|
3430
3501
|
const daily = fs.readFileSync(dailyPath, "utf8");
|
|
3431
3502
|
|
|
3432
3503
|
// Extract session log entries
|
|
3433
|
-
const logMatch = daily.match(/##\s*Session Log[\s\S]*?(?=\n## [^#]
|
|
3504
|
+
const logMatch = daily.match(/##\s*Session Log[\s\S]*?(?=\n## [^#]|$)/i);
|
|
3434
3505
|
if (!logMatch) {
|
|
3435
3506
|
barLn(dim(" No session log found in this Daily Note."));
|
|
3436
3507
|
return;
|
|
@@ -3985,10 +4056,11 @@ async function cmdRestore(opts) {
|
|
|
3985
4056
|
const archivesDir = path.join(vault, "04_Archives");
|
|
3986
4057
|
if (!fs.existsSync(archivesDir)) { barLn(yellow(" No archives found.")); return; }
|
|
3987
4058
|
|
|
4059
|
+
const backupPrefixes = ["Backup_", "Engine_Backup_", "Areas_Backup_", "Agent_Backup_"];
|
|
3988
4060
|
const backups = fs.readdirSync(archivesDir)
|
|
3989
4061
|
.filter((d) => {
|
|
3990
4062
|
const full = path.join(archivesDir, d);
|
|
3991
|
-
return fs.statSync(full).isDirectory() &&
|
|
4063
|
+
return fs.statSync(full).isDirectory() && backupPrefixes.some((p) => d.startsWith(p));
|
|
3992
4064
|
})
|
|
3993
4065
|
.sort()
|
|
3994
4066
|
.reverse();
|
|
@@ -4016,25 +4088,67 @@ async function cmdRestore(opts) {
|
|
|
4016
4088
|
|
|
4017
4089
|
const backupPath = path.join(archivesDir, selected);
|
|
4018
4090
|
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
if (
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
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
|
+
}
|
|
4027
4126
|
}
|
|
4028
|
-
statusLine("ok", "
|
|
4127
|
+
statusLine("ok", "Agents", `${agentCount} items restored`);
|
|
4128
|
+
totalRestored = agentCount;
|
|
4029
4129
|
} else {
|
|
4030
|
-
//
|
|
4031
|
-
const
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
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;
|
|
4036
4151
|
}
|
|
4037
|
-
if (restored > 0) statusLine("ok", "Engine", `${restored} files restored`);
|
|
4038
4152
|
}
|
|
4039
4153
|
|
|
4040
4154
|
barLn();
|
|
@@ -4631,14 +4745,20 @@ async function cmdUpdateComprehensive(opts, bundleDir, startTime) {
|
|
|
4631
4745
|
createVaultStructure(vaultPath);
|
|
4632
4746
|
installTemplateFiles(bundleDir, vaultPath);
|
|
4633
4747
|
const writtenFiles = new Set();
|
|
4634
|
-
const skillOpts = { install: true, categories: null, workflows: null };
|
|
4748
|
+
const skillOpts = { install: true, categories: null, workflows: null, statusLine: changes.statusline !== "unchanged" };
|
|
4635
4749
|
for (const agent of detectedAgents) {
|
|
4636
|
-
const
|
|
4637
|
-
|
|
4638
|
-
const
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
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
|
+
}
|
|
4642
4762
|
}
|
|
4643
4763
|
fs.writeFileSync(path.join(vaultPath, ".mover-version"), `${require("./package.json").version}\n`, "utf8");
|
|
4644
4764
|
writeMoverConfig(vaultPath, selectedIds);
|
|
@@ -4697,15 +4817,36 @@ async function cmdUpdateComprehensive(opts, bundleDir, startTime) {
|
|
|
4697
4817
|
fs.mkdirSync(agentBackupDir, { recursive: true });
|
|
4698
4818
|
let agentsBacked = 0;
|
|
4699
4819
|
for (const ag of AGENTS.filter((a) => a.detect())) {
|
|
4700
|
-
const
|
|
4701
|
-
|
|
4702
|
-
for (const
|
|
4703
|
-
|
|
4704
|
-
|
|
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 {}
|
|
4705
4846
|
}
|
|
4706
4847
|
}
|
|
4707
4848
|
}
|
|
4708
|
-
statusLine("ok", "Backed up", `${agentsBacked} agent config
|
|
4849
|
+
statusLine("ok", "Backed up", `${agentsBacked} agent config items`);
|
|
4709
4850
|
} catch (err) { barLn(yellow(` Agent backup failed: ${err.message}`)); }
|
|
4710
4851
|
}
|
|
4711
4852
|
}
|
|
@@ -4806,7 +4947,7 @@ async function cmdUpdateComprehensive(opts, bundleDir, startTime) {
|
|
|
4806
4947
|
}
|
|
4807
4948
|
|
|
4808
4949
|
const writtenFiles = new Set();
|
|
4809
|
-
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" };
|
|
4810
4951
|
for (const agent of selectedAgents) {
|
|
4811
4952
|
const sel = AGENT_SELECTIONS.find((s) => s.id === agent.id);
|
|
4812
4953
|
const targets = sel ? sel.targets : [agent.id];
|
|
@@ -4839,8 +4980,12 @@ async function cmdUpdateComprehensive(opts, bundleDir, startTime) {
|
|
|
4839
4980
|
const catSet = new Set(selectedCatIds);
|
|
4840
4981
|
const refreshOpts = { install: true, categories: catSet, workflows: null };
|
|
4841
4982
|
for (const agent of selectedAgents) {
|
|
4842
|
-
const
|
|
4843
|
-
|
|
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
|
+
}
|
|
4844
4989
|
}
|
|
4845
4990
|
const skillCount = allSkills.filter((s) => s.category === "tools" || catSet.has(s.category)).length;
|
|
4846
4991
|
statusLine("ok", "Skills refreshed", `${skillCount} across ${selectedAgents.length} agent(s)`);
|
|
@@ -5223,8 +5368,8 @@ async function main() {
|
|
|
5223
5368
|
],
|
|
5224
5369
|
{ multi: false, defaultIndex: 1 }
|
|
5225
5370
|
);
|
|
5226
|
-
if (!ptChoice)
|
|
5227
|
-
if (ptChoice === "yes") {
|
|
5371
|
+
if (!ptChoice || ptChoice === "no") { /* skip prayer setup */ }
|
|
5372
|
+
else if (ptChoice === "yes") {
|
|
5228
5373
|
prayerSetup = true;
|
|
5229
5374
|
barLn();
|
|
5230
5375
|
question("How would you like to set up prayer times?");
|
|
@@ -5238,7 +5383,9 @@ async function main() {
|
|
|
5238
5383
|
],
|
|
5239
5384
|
{ multi: false, defaultIndex: 0 }
|
|
5240
5385
|
);
|
|
5241
|
-
if (!method || method === "later") {
|
|
5386
|
+
if (!method || method === "later") {
|
|
5387
|
+
// User cancelled method pick — still enable the setting, no timetable
|
|
5388
|
+
} else {
|
|
5242
5389
|
|
|
5243
5390
|
const moverDir = path.join(os.homedir(), ".mover");
|
|
5244
5391
|
if (!fs.existsSync(moverDir)) fs.mkdirSync(moverDir, { recursive: true, mode: 0o700 });
|
|
@@ -5290,9 +5437,7 @@ async function main() {
|
|
|
5290
5437
|
} else if (method === "fetch") {
|
|
5291
5438
|
barLn();
|
|
5292
5439
|
const city = await textInput({ label: "City (e.g. London, Watford, Istanbul)", placeholder: "London" });
|
|
5293
|
-
|
|
5294
|
-
const country = await textInput({ label: "Country", placeholder: "United Kingdom" });
|
|
5295
|
-
if (country === null) return;
|
|
5440
|
+
const country = city ? await textInput({ label: "Country", placeholder: "United Kingdom" }) : null;
|
|
5296
5441
|
barLn();
|
|
5297
5442
|
|
|
5298
5443
|
if (city && country) {
|
|
@@ -5310,6 +5455,7 @@ async function main() {
|
|
|
5310
5455
|
}
|
|
5311
5456
|
}
|
|
5312
5457
|
// method === "later" → just enable the setting, no timetable yet
|
|
5458
|
+
} // end if method !== "later"
|
|
5313
5459
|
}
|
|
5314
5460
|
}
|
|
5315
5461
|
|