@heyitsiveen/dotfiles 1.0.0 → 1.0.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/dist/index.mjs +112 -85
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -186,12 +186,26 @@ async function deleteManifest() {
186
186
  const manifestPath = getManifestPath();
187
187
  if (await pathExists$2(manifestPath)) await remove(manifestPath);
188
188
  }
189
+ const TERMINAL_DEFAULTS = {
190
+ WezTerm: {
191
+ file: "wezterm.lua",
192
+ content: "local wezterm = require 'wezterm'\nreturn {}\n"
193
+ },
194
+ Ghostty: {
195
+ file: "config",
196
+ content: "# defaults\n"
197
+ }
198
+ };
189
199
  async function uninstallGroups(groups) {
190
200
  const errors = [];
191
- for (const group of groups) for (const file of group.files) try {
192
- if (await pathExists$2(file)) await remove(file);
193
- } catch (err) {
194
- errors.push(`${file}: ${err instanceof Error ? err.message : String(err)}`);
201
+ for (const group of groups) {
202
+ const termDefault = TERMINAL_DEFAULTS[group.name];
203
+ for (const file of group.files) try {
204
+ if (await pathExists$2(file)) if (termDefault && file.endsWith(termDefault.file)) await writeFile(file, termDefault.content, "utf-8");
205
+ else await remove(file);
206
+ } catch (err) {
207
+ errors.push(`${file}: ${err instanceof Error ? err.message : String(err)}`);
208
+ }
195
209
  }
196
210
  return errors;
197
211
  }
@@ -731,6 +745,7 @@ function showPrerequisites(platform) {
731
745
  log.message(` ${pc.cyan(item.link)}`);
732
746
  }
733
747
  }
748
+ /** Detect tools and show status. Returns missing tools (no install). */
734
749
  async function showToolStatus(groups, platform) {
735
750
  const allTools = [];
736
751
  const groupNames = new Set(groups.map((g) => g.name));
@@ -752,7 +767,7 @@ async function showToolStatus(groups, platform) {
752
767
  installCmd: g.installCmd,
753
768
  required: g.required
754
769
  });
755
- if (allTools.length === 0) return;
770
+ if (allTools.length === 0) return [];
756
771
  const forGroupNames = new Set([...subTools.values()].flat().map((t) => t.name));
757
772
  const requiredTools = [];
758
773
  const optionalTools = [];
@@ -791,45 +806,26 @@ async function showToolStatus(groups, platform) {
791
806
  const subs = subTools.get(t.name);
792
807
  if (subs) for (const st of subs) showSubTool(st);
793
808
  }
794
- if (missing.length > 0) {
795
- const selectedNames = await multiselect({
796
- message: "Install missing tools?",
797
- options: missing.map((t) => ({
798
- value: t.name,
799
- label: `${t.name} ${t.description}`,
800
- hint: t.installCmd
801
- })),
802
- initialValues: missing.map((t) => t.name),
803
- required: false
804
- });
805
- handleCancel(selectedNames);
806
- const toInstall = missing.filter((t) => selectedNames.includes(t.name));
807
- if (toInstall.length > 0) {
808
- const brewFirst = toInstall.filter((t) => t.name === "Homebrew");
809
- const rest = toInstall.filter((t) => t.name !== "Homebrew");
810
- const ordered = [...brewFirst, ...rest];
811
- const total = ordered.length;
812
- let installed = 0;
813
- for (const t of ordered) {
814
- log.message(` ${pc.dim("○")} Installing ${t.name}...`);
815
- try {
816
- execSync(t.installCmd, {
817
- stdio: "pipe",
818
- encoding: "utf-8",
819
- timeout: 3e5
820
- });
821
- installed++;
822
- log.message(` ${pc.green("◆")} ${t.name} installed`);
823
- } catch (err) {
824
- log.message(` ${pc.yellow("⚠")} ${t.name} failed`);
825
- log.message(` ${pc.dim(t.installCmd)} — ${pc.dim(err instanceof Error ? err.message : String(err))}`);
826
- }
827
- }
828
- log.message("");
829
- if (installed === total) log.message(` ${pc.green("◆")} ${installed} tool${installed > 1 ? "s" : ""} installed successfully`);
830
- else log.message(` ${pc.yellow("⚠")} ${installed}/${total} tools installed (${total - installed} failed)`);
831
- }
832
- } else log.success("All tools installed!");
809
+ if (missing.length === 0) log.success("All tools installed!");
810
+ return missing;
811
+ }
812
+ /** Show multiselect picker for missing tools. Returns tools to install (no execution). */
813
+ async function pickMissingTools(missing) {
814
+ const selectedNames = await multiselect({
815
+ message: "Install missing tools?",
816
+ options: missing.map((t) => ({
817
+ value: t.name,
818
+ label: t.name,
819
+ hint: t.installCmd
820
+ })),
821
+ initialValues: missing.map((t) => t.name),
822
+ required: false
823
+ });
824
+ handleCancel(selectedNames);
825
+ const toInstall = missing.filter((t) => selectedNames.includes(t.name));
826
+ const brewFirst = toInstall.filter((t) => t.name === "Homebrew");
827
+ const rest = toInstall.filter((t) => t.name !== "Homebrew");
828
+ return [...brewFirst, ...rest];
833
829
  }
834
830
  const groupCategories = {
835
831
  "Shell & Terminal": [
@@ -884,9 +880,17 @@ function showReloadCommands(groups) {
884
880
  break;
885
881
  }
886
882
  if (reloads.length > 0) {
887
- log.message(pc.bold("\n Reload your configs:"));
888
- for (const r of reloads) log.message(` ${r.name.padEnd(12)} → ${pc.cyan(r.cmd)}`);
883
+ log.info("Reload your configs:");
884
+ for (const r of reloads) log.message(` ${r.name.padEnd(12)} → ${pc.cyan(r.cmd)}`);
889
885
  }
886
+ const restartMsg = "Restart your terminal for all changes to take effect.";
887
+ const w = 57;
888
+ log.message([
889
+ "",
890
+ ` ${pc.dim("╭" + "─".repeat(w) + "╮")}`,
891
+ ` ${pc.dim("│")} ${pc.bold(restartMsg)} ${pc.dim("│")}`,
892
+ ` ${pc.dim("╰" + "─".repeat(w) + "╯")}`
893
+ ].join("\n"));
890
894
  }
891
895
  async function resolvePlatform(flagPlatform) {
892
896
  if (flagPlatform === "macos" || flagPlatform === "windows") return flagPlatform;
@@ -912,13 +916,29 @@ async function resolvePlatform(flagPlatform) {
912
916
  handleCancel(chosen);
913
917
  return chosen;
914
918
  }
915
- async function firstRunFlow(flagPlatform, dryRun = false) {
919
+ async function showUpdateNotification(updatePromise) {
920
+ if (!updatePromise) return;
921
+ const latest = await updatePromise;
922
+ if (!latest) return;
923
+ const msg = `Update available: ${VERSION} → ${latest}`;
924
+ const cmd = "Run: bunx @heyitsiveen/dotfiles@latest";
925
+ const w = Math.max(msg.length, 38) + 4;
926
+ log.message([
927
+ ` ${pc.dim("╭" + "─".repeat(w) + "╮")}`,
928
+ ` ${pc.dim("│")} ${pc.yellow(msg.padEnd(w - 2))}${pc.dim("│")}`,
929
+ ` ${pc.dim("│")} ${pc.cyan(cmd.padEnd(w - 2))}${pc.dim("│")}`,
930
+ ` ${pc.dim("╰" + "─".repeat(w) + "╯")}`
931
+ ].join("\n"));
932
+ }
933
+ async function firstRunFlow(flagPlatform, dryRun = false, updatePromise) {
916
934
  showBanner();
917
935
  intro(pc.bold("heyitsiveen"));
936
+ await showUpdateNotification(updatePromise);
918
937
  const platform = await resolvePlatform(flagPlatform);
919
938
  showPrerequisites(platform);
920
939
  const allGroups = getDotfileGroups(platform);
921
- await showToolStatus(allGroups, platform);
940
+ const missingTools = await showToolStatus(allGroups, platform);
941
+ const toolsToInstall = missingTools.length > 0 ? await pickMissingTools(missingTools) : [];
922
942
  showOverview(allGroups);
923
943
  const depTools = getDependencyTools(platform);
924
944
  const selectedNames = await multiselect({
@@ -929,10 +949,11 @@ async function firstRunFlow(flagPlatform, dryRun = false) {
929
949
  const warnings = [];
930
950
  if (toolMissing) warnings.push("not installed");
931
951
  if (missingDeps.length > 0) warnings.push(`${missingDeps.join(", ")} missing`);
932
- const suffix = warnings.length > 0 ? pc.yellow(` ⚠ (${warnings.join(", ")})`) : "";
952
+ const hint = warnings.length > 0 ? `⚠ ${warnings.join(", ")}` : g.description;
933
953
  return {
934
954
  value: g.name,
935
- label: `${g.name} — ${g.description}${suffix}`
955
+ label: g.name,
956
+ hint
936
957
  };
937
958
  }),
938
959
  initialValues: allGroups.map((g) => g.name),
@@ -975,8 +996,27 @@ async function firstRunFlow(flagPlatform, dryRun = false) {
975
996
  shouldBackup = doBackup;
976
997
  }
977
998
  if (dryRun) log.info(pc.yellow("Dry run — showing planned operations:"));
978
- const s = spinner();
979
- s.start("Installing dotfiles...");
999
+ if (toolsToInstall.length > 0) {
1000
+ log.info("Installing tools...");
1001
+ let toolsInstalled = 0;
1002
+ for (const t of toolsToInstall) {
1003
+ log.message(` ${pc.dim("○")} ${t.name}...`);
1004
+ try {
1005
+ execSync(t.installCmd, {
1006
+ stdio: "pipe",
1007
+ encoding: "utf-8",
1008
+ timeout: 3e5
1009
+ });
1010
+ toolsInstalled++;
1011
+ log.message(` ${pc.green("◆")} ${t.name} installed`);
1012
+ } catch (err) {
1013
+ log.message(` ${pc.yellow("⚠")} ${t.name} failed`);
1014
+ log.message(` ${pc.dim(t.installCmd)} — ${pc.dim(err instanceof Error ? err.message : String(err))}`);
1015
+ }
1016
+ }
1017
+ log.message(` ${pc.green("◆")} ${toolsInstalled}/${toolsToInstall.length} tools installed`);
1018
+ }
1019
+ log.info("Installing dotfiles...");
980
1020
  const result = await install({
981
1021
  platform,
982
1022
  selectedGroups,
@@ -984,8 +1024,21 @@ async function firstRunFlow(flagPlatform, dryRun = false) {
984
1024
  backup: shouldBackup,
985
1025
  dryRun
986
1026
  });
1027
+ for (const ig of result.installedGroups) log.message(` ${pc.green("◆")} ${ig.name} → ${pc.dim(ig.target)}`);
1028
+ log.message(` ${pc.green("◆")} ${result.installedGroups.length}/${selectedGroups.length} configs installed`);
987
1029
  const themeGroups = selectedGroups.filter((g) => g.themeSupport);
988
- if (themeGroups.length > 0) await switchTheme(theme, result.installedGroups.filter((ig) => themeGroups.some((sg) => sg.name === ig.name)), platform, dryRun);
1030
+ if (themeGroups.length > 0) {
1031
+ log.info("Applying theme...");
1032
+ const themeResults = await switchTheme(theme, result.installedGroups.filter((ig) => themeGroups.some((sg) => sg.name === ig.name)), platform, dryRun);
1033
+ for (const r of themeResults) log.message(` ${pc.green("◆")} ${r}`);
1034
+ log.message(` ${pc.green("◆")} Theme: ${theme} activated`);
1035
+ }
1036
+ if (result.backedUp.length > 0) {
1037
+ const unique = [...new Set(result.backedUp)];
1038
+ log.info("Backed up existing configs:");
1039
+ for (const name of unique) log.message(` ${pc.green("◆")} ${name}`);
1040
+ log.message(` ${pc.green("◆")} ${unique.length} configs → ${pc.dim("~/.config/heyitsiveen/dotfiles/backup/")}`);
1041
+ }
989
1042
  if (!dryRun) await createManifest(result, {
990
1043
  platform,
991
1044
  selectedGroups,
@@ -993,27 +1046,14 @@ async function firstRunFlow(flagPlatform, dryRun = false) {
993
1046
  backup: shouldBackup,
994
1047
  dryRun
995
1048
  });
996
- s.stop("Installation complete!");
997
- if (result.backedUp.length > 0) {
998
- const unique = [...new Set(result.backedUp)];
999
- log.message(` ${pc.green("◆")} Backed up ${unique.length} existing configs → ${pc.dim("~/.config/heyitsiveen/dotfiles/backup/")}`);
1000
- }
1001
- for (const [category, names] of Object.entries(groupCategories)) {
1002
- const installed = result.installedGroups.filter((ig) => names.includes(ig.name));
1003
- if (installed.length === 0) continue;
1004
- log.message(` ${pc.bold(category)}`);
1005
- for (const ig of installed) log.message(` ${pc.green("◆")} ${ig.name} → ${pc.dim(ig.target)}`);
1006
- }
1007
- log.message(` ${pc.green("◆")} Theme: ${theme} activated`);
1008
1049
  if (result.errors.length > 0) for (const err of result.errors) log.message(` ${pc.yellow("⚠")} ${err.file}: ${pc.dim(err.error)}`);
1009
1050
  showReloadCommands(selectedGroups);
1010
- log.message("");
1011
- log.info(pc.bold("Restart your terminal for all changes to take effect."));
1012
1051
  outro("Done! Your dotfiles are installed.");
1013
1052
  }
1014
- async function reRunFlow(manifest, flagPlatform, dryRun = false) {
1053
+ async function reRunFlow(manifest, flagPlatform, dryRun = false, updatePromise) {
1015
1054
  showBanner();
1016
1055
  intro(pc.bold("heyitsiveen"));
1056
+ await showUpdateNotification(updatePromise);
1017
1057
  showPrerequisites(manifest.platform);
1018
1058
  log.info(`Existing installation detected (v${manifest.version}, installed ${manifest.installedAt.split("T")[0]})`);
1019
1059
  const mode = await select({
@@ -1127,7 +1167,8 @@ async function uninstallFlow(manifest, dryRun = false) {
1127
1167
  message: "Which configs to remove?",
1128
1168
  options: manifest.groups.map((g) => ({
1129
1169
  value: g.name,
1130
- label: `${g.name} — ${g.target}`
1170
+ label: g.name,
1171
+ hint: g.target
1131
1172
  })),
1132
1173
  initialValues: manifest.groups.map((g) => g.name),
1133
1174
  required: true
@@ -1418,22 +1459,8 @@ runMain(defineCommand({
1418
1459
  await themeFlow(manifest, dryRun, args.theme);
1419
1460
  return;
1420
1461
  }
1421
- if (manifest) await reRunFlow(manifest, args.platform, dryRun);
1422
- else await firstRunFlow(args.platform, dryRun);
1423
- const latest = await updatePromise;
1424
- if (latest) {
1425
- const msg = `Update available: ${VERSION} → ${latest}`;
1426
- const cmd = "Run: bunx @heyitsiveen/dotfiles@latest";
1427
- const w = Math.max(msg.length, 38) + 4;
1428
- console.log([
1429
- "",
1430
- ` ${pc.dim("╭" + "─".repeat(w) + "╮")}`,
1431
- ` ${pc.dim("│")} ${pc.yellow(msg.padEnd(w - 2))}${pc.dim("│")}`,
1432
- ` ${pc.dim("│")} ${pc.cyan(cmd.padEnd(w - 2))}${pc.dim("│")}`,
1433
- ` ${pc.dim("╰" + "─".repeat(w) + "╯")}`,
1434
- ""
1435
- ].join("\n"));
1436
- }
1462
+ if (manifest) await reRunFlow(manifest, args.platform, dryRun, updatePromise);
1463
+ else await firstRunFlow(args.platform, dryRun, updatePromise);
1437
1464
  } catch (err) {
1438
1465
  if (err.code === "ERR_USE_AFTER_CLOSE") process.exit(0);
1439
1466
  log.error(pc.red(err instanceof Error ? err.message : "An unexpected error occurred."));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heyitsiveen/dotfiles",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Interactive CLI to set up dotfiles for macOS and Windows 11",
5
5
  "type": "module",
6
6
  "bin": {