mover-os 4.6.1 → 4.6.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 +106 -64
  2. package/package.json +1 -1
package/install.js CHANGED
@@ -199,15 +199,23 @@ async function printHeader(animate = IS_TTY) {
199
199
  ln(` ${dim(`v${pkgVer}`)} ${gray("the agentic operating system for obsidian")}${infoRight ? ` ${infoRight}` : ""}`);
200
200
 
201
201
  // Non-blocking update check
202
+ let _updateAvailable = null;
202
203
  try {
203
204
  const latest = execSync("npm view mover-os version", { encoding: "utf8", timeout: 5000 }).trim();
204
205
  if (latest && latest !== pkgVer && compareVersions(latest, pkgVer) > 0) {
205
- ln(` ${yellow(`Update available: v${pkgVer} → v${latest}`)} ${dim(`run ${bold("moveros update")}`)}`);
206
+ _updateAvailable = latest;
206
207
  }
207
208
  } catch {}
208
209
 
209
210
  ln();
210
- ln(gray(" ─────────────────────────────────────────────"));
211
+ if (_updateAvailable) {
212
+ ln(` ${yellow("┌──────────────────────────────────────────┐")}`);
213
+ ln(` ${yellow("│")} Update available: ${dim(`v${pkgVer}`)} ${dim("\u2192")} ${green(`v${_updateAvailable}`)}${" ".repeat(Math.max(0, 16 - _updateAvailable.length - pkgVer.length))}${yellow("│")}`);
214
+ ln(` ${yellow("│")} Run ${bold("moveros update")} to get the latest ${yellow("│")}`);
215
+ ln(` ${yellow("└──────────────────────────────────────────┘")}`);
216
+ } else {
217
+ ln(gray(" ─────────────────────────────────────────────"));
218
+ }
211
219
  ln();
212
220
  }
213
221
 
@@ -5827,74 +5835,108 @@ async function main() {
5827
5835
  }
5828
5836
  }
5829
5837
 
5830
- // ── Settings steplet user configure before install (loops until esc) ──
5838
+ // ── Confirmation gatereview and optionally change selections ──
5831
5839
  {
5832
- barLn();
5833
- question("Configure settings " + dim("(esc to continue with defaults)"));
5834
- barLn();
5835
- let editingSettings = true;
5836
- while (editingSettings) {
5837
- const cfgPath = path.join(os.homedir(), ".mover", "config.json");
5838
- let cfg = {};
5839
- if (fs.existsSync(cfgPath)) { try { cfg = JSON.parse(fs.readFileSync(cfgPath, "utf8")); } catch {} }
5840
- if (!cfg.settings) cfg.settings = {};
5840
+ let confirmed = false;
5841
+ while (!confirmed) {
5842
+ barLn();
5843
+ question(bold("Review your selections") + dim(" (enter to change, esc to cancel)"));
5844
+ barLn();
5841
5845
 
5842
- const settingsItems = [
5843
- { id: "review_day", name: `review_day ${(cfg.settings.review_day || "Sunday").toString().padEnd(12)}Weekly review day` },
5844
- { id: "track_food", name: `track_food ${(cfg.settings.track_food !== undefined ? (cfg.settings.track_food ? "on" : "off") : "on").padEnd(12)}Track food in daily notes` },
5845
- { id: "track_sleep", name: `track_sleep ${(cfg.settings.track_sleep !== undefined ? (cfg.settings.track_sleep ? "on" : "off") : "on").padEnd(12)}Track sleep in daily notes` },
5846
- { id: "friction_level", name: `friction_level ${(cfg.settings.friction_level || 3).toString().padEnd(12)}Max friction level (1-4)` },
5846
+ const agentNames = selectedAgents.map((a) => a.name).join(", ");
5847
+ const skillsLabel = installSkills && selectedCategories
5848
+ ? [...selectedCategories].join(", ")
5849
+ : installSkills ? "all categories" : "none";
5850
+ const slLabel = selectedIds.includes("claude-code") ? (installStatusLine ? "yes" : "no") : null;
5851
+
5852
+ const reviewItems = [
5853
+ { id: "_install", name: green(bold(" Install")), tier: "Proceed with the selections above" },
5854
+ { id: "agents", name: `Agents ${dim(agentNames)}`, tier: "Change which AI agents to install for" },
5855
+ { id: "skills", name: `Skills ${dim(skillsLabel)}`, tier: "Change skill categories" },
5847
5856
  ];
5848
- const settingsPick = await interactiveSelect(settingsItems);
5849
- if (!settingsPick) { editingSettings = false; break; }
5850
- const meta = KNOWN_SETTINGS[settingsPick];
5851
- if (meta) {
5852
- if (meta.type === "boolean") {
5853
- cfg.settings[settingsPick] = !(cfg.settings[settingsPick] !== undefined ? cfg.settings[settingsPick] : meta.defaults);
5854
- statusLine("ok", settingsPick, cfg.settings[settingsPick] ? "on" : "off");
5855
- } else {
5856
- const answer = await textInput({ label: settingsPick, initial: String(cfg.settings[settingsPick] !== undefined ? cfg.settings[settingsPick] : meta.defaults) });
5857
- if (answer !== null && answer.trim() !== "") {
5858
- cfg.settings[settingsPick] = meta.type === "number" ? parseInt(answer.trim(), 10) : answer.trim();
5859
- statusLine("ok", settingsPick, JSON.stringify(cfg.settings[settingsPick]));
5860
- }
5861
- }
5862
- fs.mkdirSync(path.dirname(cfgPath), { recursive: true });
5863
- fs.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2), "utf8");
5857
+ if (slLabel !== null) {
5858
+ reviewItems.push({ id: "statusline", name: `Status line ${dim(slLabel)}`, tier: "Toggle Claude Code status line" });
5864
5859
  }
5865
- }
5866
- }
5860
+ reviewItems.push({ id: "settings", name: `Settings ${dim("enter to configure")}`, tier: "Configure review_day, tracking, friction level" });
5867
5861
 
5868
- // ── Confirmation gate show what will be installed ──
5869
- {
5870
- barLn();
5871
- question(bold("Review your selections:"));
5872
- barLn();
5873
- barLn(` ${bold("Vault:")} ${vaultPath}`);
5874
- barLn(` ${bold("Agents:")} ${selectedAgents.map((a) => a.name).join(", ")}`);
5875
- if (installSkills && selectedCategories) {
5876
- barLn(` ${bold("Skills:")} ${[...selectedCategories].join(", ")}`);
5877
- } else if (installSkills) {
5878
- barLn(` ${bold("Skills:")} all categories`);
5879
- } else {
5880
- barLn(` ${bold("Skills:")} none`);
5881
- }
5882
- if (selectedIds.includes("claude-code")) {
5883
- barLn(` ${bold("Status line:")} ${installStatusLine ? "yes" : "no"}`);
5884
- }
5885
- barLn(` ${bold("Prayer:")} ${prayerSetup ? "yes" : "no"}`);
5886
- barLn();
5862
+ const pick = await interactiveSelect(reviewItems, { defaultIndex: 0 });
5863
+ if (!pick) { outro("Cancelled."); return; }
5887
5864
 
5888
- const confirmChoice = await interactiveSelect(
5889
- [
5890
- { id: "yes", name: "Install", tier: "Proceed with the selections above" },
5891
- { id: "no", name: "Cancel", tier: "Go back and start over" },
5892
- ],
5893
- { multi: false, defaultIndex: 0 }
5894
- );
5895
- if (!confirmChoice || confirmChoice === "no") {
5896
- outro("Cancelled.");
5897
- return;
5865
+ if (pick === "_install") {
5866
+ confirmed = true;
5867
+ } else if (pick === "agents") {
5868
+ barLn();
5869
+ question("Select your AI agents");
5870
+ const agentItems = AGENTS.map((a) => {
5871
+ const detected = a.detect();
5872
+ return { id: a.id, name: `${a.name}${detected ? dim(" (detected)") : ""}`, tier: dim(a.tier), _detected: detected };
5873
+ });
5874
+ const newIds = await interactiveSelect(agentItems, { multi: true, preSelected: selectedIds });
5875
+ if (newIds && newIds.length > 0) {
5876
+ selectedIds.length = 0;
5877
+ newIds.forEach((id) => selectedIds.push(id));
5878
+ selectedAgents.length = 0;
5879
+ AGENTS.filter((a) => selectedIds.includes(a.id)).forEach((a) => selectedAgents.push(a));
5880
+ }
5881
+ } else if (pick === "skills") {
5882
+ barLn();
5883
+ const allSkills = findSkills(bundleDir);
5884
+ const catCounts = {};
5885
+ for (const sk of allSkills) catCounts[sk.category] = (catCounts[sk.category] || 0) + 1;
5886
+ const categoryItems = CATEGORY_META.map((c) => ({
5887
+ id: c.id, name: `${c.name} ${dim(`(${catCounts[c.id] || 0})`)}`, tier: dim(c.desc),
5888
+ }));
5889
+ question("Select skill categories:");
5890
+ const newCats = await interactiveSelect(categoryItems, { multi: true, preSelected: selectedCategories ? [...selectedCategories] : ["development", "obsidian"] });
5891
+ if (newCats) {
5892
+ installSkills = newCats.length > 0;
5893
+ selectedCategories = installSkills ? new Set(newCats) : null;
5894
+ }
5895
+ } else if (pick === "statusline") {
5896
+ installStatusLine = !installStatusLine;
5897
+ } else if (pick === "settings") {
5898
+ // Re-enter settings loop
5899
+ barLn();
5900
+ question("Configure settings " + dim("(enter to toggle/edit, esc to go back)"));
5901
+ barLn();
5902
+ const sCfgPath = path.join(os.homedir(), ".mover", "config.json");
5903
+ let sCfg = {};
5904
+ if (fs.existsSync(sCfgPath)) { try { sCfg = JSON.parse(fs.readFileSync(sCfgPath, "utf8")); } catch {} }
5905
+ if (!sCfg.settings) sCfg.settings = {};
5906
+ const installSettings = [
5907
+ { key: "track_food", label: "Track food", desc: "Track meals in daily notes" },
5908
+ { key: "track_sleep", label: "Track sleep", desc: "Track sleep in daily notes" },
5909
+ { key: "friction_level", label: "Friction level", desc: "AI pushback intensity (1=gentle, 4=hard block)" },
5910
+ { key: "review_day", label: "Review day", desc: "Day for weekly review" },
5911
+ ];
5912
+ let sLoop = true;
5913
+ while (sLoop) {
5914
+ const sItems = installSettings.map((s) => {
5915
+ const meta = KNOWN_SETTINGS[s.key];
5916
+ const val = sCfg.settings[s.key] !== undefined ? sCfg.settings[s.key] : meta.defaults;
5917
+ const display = meta.type === "boolean" ? (val ? green("on") : dim("off")) : String(val);
5918
+ return { id: s.key, name: `${s.label.padEnd(18)}${display}`, tier: s.desc };
5919
+ });
5920
+ const sPick = await interactiveSelect(sItems);
5921
+ if (!sPick) { sLoop = false; break; }
5922
+ const sMeta = KNOWN_SETTINGS[sPick];
5923
+ if (sMeta) {
5924
+ if (sMeta.type === "boolean") {
5925
+ sCfg.settings[sPick] = !(sCfg.settings[sPick] !== undefined ? sCfg.settings[sPick] : sMeta.defaults);
5926
+ } else if (sPick === "friction_level") {
5927
+ const cur = sCfg.settings[sPick] || sMeta.defaults;
5928
+ sCfg.settings[sPick] = cur >= 4 ? 1 : cur + 1;
5929
+ } else if (sPick === "review_day") {
5930
+ const days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
5931
+ const cur = sCfg.settings[sPick] || sMeta.defaults;
5932
+ const idx = days.indexOf(cur);
5933
+ sCfg.settings[sPick] = days[(idx + 1) % days.length];
5934
+ }
5935
+ fs.mkdirSync(path.dirname(sCfgPath), { recursive: true });
5936
+ fs.writeFileSync(sCfgPath, JSON.stringify(sCfg, null, 2), "utf8");
5937
+ }
5938
+ }
5939
+ }
5898
5940
  }
5899
5941
  }
5900
5942
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mover-os",
3
- "version": "4.6.1",
3
+ "version": "4.6.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"