theamify-cli 2.2.1 → 2.2.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/README.md CHANGED
@@ -28,25 +28,47 @@ theamify doctor # diagnose install, GRUB, tools & dependencies
28
28
  theamify upgrade # check for & install the latest npm version
29
29
  theamify repair # fix a broken install (re-provision the engine)
30
30
  theamify uninstall # remove theamify entirely (incl. npm package)
31
+ theamify version # print the version
32
+ theamify help # show all commands
31
33
  ```
32
34
 
35
+ > **Command aliases** — `ls` (list), `show` (info), `get`/`fetch`/`download`, `use`/`apply`/`set`, `rm`/`uncache` (remove), `del`/`delete`, `purge-cache` (clean), `browse`/`wizard`/`install` (wizard). \
36
+ > **Version flags** — `-v`, `-V`, `--version` · **Help flags** — `-h`, `--help`
37
+
33
38
  > 💡 Upgrading is the same command — `npm install -g theamify-cli@latest`, and your downloaded themes & registry edits are preserved.
34
39
  > 🧰 Zero-install run (no permanent install): `npx -y theamify-cli`
35
40
 
36
41
  ---
37
42
 
38
- ## ✨ What's New (v1.1.0npm release)
43
+ ## ✨ What's New (v2.2.3complete uninstall)
44
+
45
+ | Feature | Details |
46
+ |---|---|
47
+ | 🗑️ **Uninstall removes EVERYTHING** | `theamify uninstall` now removes **every** runtime tree (`~/.local/share/theamify` AND `/usr/local/share/theamify`), all downloaded themes, the applied GRUB theme, the `theamify-cli` npm package **and** `~/.local/bin/theamify` (legacy shadow, file or symlink) plus every PATH marker from `~/.bashrc`/`~/.zshrc` |
48
+ | ✅ **`command not found` guaranteed** | After uninstall, `theamify` shadows nothing on PATH — typing it returns `bash: theamify: command not found`, exactly like any tool you never installed |
49
+ | 🔒 **One confirmation** | A single confirmation covers the whole removal (no more piecemeal "keep the npm package?" prompts) |
50
+ | 🩹 **Regression-tested** | `removeUserBin` (symlink + real-file shadow), runtime-tree removal and rc cleanup are all covered by tests |
51
+
52
+ ## ✨ What's New (v2.2.2 — uninstall crash hotfix)
53
+
54
+ | Feature | Details |
55
+ |---|---|
56
+ | 🩹 **`ReferenceError: path is not defined` fixed** | The v2.2.1 uninstall crashed while stripping leftover PATH markers from `~/.bashrc`/`~/.zshrc` — `cleanRcMarkers` used `path`/`os`/`fs` without importing them. Now fixed with proper `node:` imports and guarded by regression tests. |
57
+ | 🔄 **Engine lockstep to v2.2.2** | Bundled engine `VERSION` bumped to match the npm package — no more "update available" nag after a fresh install. |
58
+
59
+ ## ✨ What's New (v2.2.1 — release)
39
60
 
40
61
  | Feature | Details |
41
62
  |---|---|
42
63
  | 🧙 **Interactive wizard** | One command: detect companion tools → pick theme → **preview** → apply |
43
64
  | 🧩 **Auto-detected companion tools** | **chafa** (thumbnails) + **grub-customizer** (GUI) are detected first; installed if missing, or **option to update** if present — then it continues on its own |
44
- | 🖼️ **Terminal thumbnail preview** | See the boot-screen image inline via `chafa` before you commit |
65
+ | 🖼️ **Thumbnail by default** | Selecting a cached theme renders its terminal thumbnail **automatically** (no separate “preview” item) |
45
66
  | 📦 **npm global install** | Engine provisions itself user-writable (no sudo for downloads) |
46
67
  | 🩺 **`theamify doctor`** | Diagnose install, GRUB, active theme, registry, tools & dependencies |
47
- | 🗑 **Safe uninstall** | Removes **only theamify** — leaves chafa & grub-customizer for later use |
68
+ | 🗑 **Complete uninstall** | Deletes **ALL downloaded themes**, resets GRUB to default, removes the `theamify-cli` npm package **and** strips leftover PATH markers from `~/.bashrc`/`~/.zshrc` after which `theamify` gives a normal `command not found` |
48
69
  | ⌨️ **Ctrl+C / Ctrl+Z safe** | Signal safety net; interactive `sudo` is never frozen |
49
70
  | 🔄 **Self-manage (v2.2+)** | `upgrade`, `repair`, auto-update check on every wizard run |
71
+ | 🧹 **Install-flow download-all** | Wizard offers to download every theme up front so each one is instantly previewable/applicable |
50
72
 
51
73
  ---
52
74
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "theamify-cli",
3
- "version": "2.2.1",
3
+ "version": "2.2.3",
4
4
  "description": "theamify — GRUB theme manager & interactive browser wizard. Browse, preview, download and apply GRUB boot themes from the terminal.",
5
5
  "type": "module",
6
6
  "main": "src/cli.js",
package/src/cli.js CHANGED
@@ -73,6 +73,13 @@ const main = defineCommand({
73
73
  async run({ args }) {
74
74
  const [cmd, ...rest] = args._;
75
75
 
76
+ // citty parses `-V` as a boolean flag into args.V (never reached via args._);
77
+ // treat it as a version request like the other aliases.
78
+ if (args.V) {
79
+ console.log(`theamify v${pkg.version}`);
80
+ return undefined;
81
+ }
82
+
76
83
  // No arguments → the interactive theme browser wizard.
77
84
  if (!cmd) return runThemeBrowser();
78
85
 
@@ -5,8 +5,10 @@ import pc from 'picocolors';
5
5
  import { execa, execaSync } from 'execa';
6
6
  import {
7
7
  findInstalledRuntime,
8
+ SYSTEM_DIR,
8
9
  USER_DIR,
9
- removeShadowBin,
10
+ BIN_NAME,
11
+ removeUserBin,
10
12
  repairRuntime,
11
13
  resolveEngine,
12
14
  } from '../core/engine.js';
@@ -160,46 +162,51 @@ export async function resetGrubTheme({ grubFile = '/etc/default/grub', asRoot =
160
162
  return res.exitCode === 0;
161
163
  }
162
164
 
163
- /** `theamify uninstall` — removes theamify, ALL downloaded themes, and resets GRUB to default. */
165
+ /** `theamify uninstall` — remove EVERY trace of theamify so the command vanishes. */
164
166
  export async function runUninstallWizard() {
165
167
  p.intro(pc.bgRed(pc.black(' theamify Uninstaller ')));
168
+
166
169
  const found = findInstalledRuntime();
167
- if (!found) p.log.info('No installed theamify runtime found (nothing to remove).');
168
-
169
- let removed = false;
170
- if (found) {
171
- const confirm = await p.confirm({
172
- message: `Remove theamify files at ${found.dir}/? (this deletes ALL downloaded themes)`,
173
- initialValue: true,
174
- });
175
- if (!p.isCancel(confirm) && confirm) {
176
- removed = await removeRuntimeDir(found.dir);
177
- if (removed) removeShadowBin();
178
- else p.log.warn(`Could not remove ${found.dir}.`);
179
- }
180
- }
170
+ if (found) p.note(found.dir, 'Installed runtime detected');
181
171
 
182
- // Reset the boot menu back to the default (remove the applied GRUB theme).
183
- const resetGrub = await p.confirm({
184
- message: 'Remove your applied GRUB theme and reset to the default boot menu?',
172
+ const confirm = await p.confirm({
173
+ message: 'Uninstall theamify completely? This removes the runtime, ALL downloaded themes, the applied GRUB theme, the theamify-cli npm package, ~/.local/bin/theamify and every PATH marker.',
185
174
  initialValue: true,
186
175
  });
187
- if (p.isCancel(resetGrub)) { p.cancel('Aborted.'); process.exit(0); }
188
- const grubReset = resetGrub ? await resetGrubTheme() : false;
176
+ if (p.isCancel(confirm)) { p.cancel('Aborted.'); process.exit(0); }
177
+ if (!confirm) { p.outro('Nothing was removed.'); return; }
178
+
179
+ // 1. Remove EVERY runtime tree — user AND system. (removeRuntimeDir uses
180
+ // sudo automatically for root-owned dirs like /usr/local/share/theamify.)
181
+ let removedCount = 0;
182
+ for (const dir of [SYSTEM_DIR, USER_DIR]) {
183
+ if (fs.existsSync(path.join(dir, BIN_NAME))) {
184
+ if (await removeRuntimeDir(dir)) removedCount++;
185
+ else p.log.warn(`Could not remove ${dir}.`);
186
+ }
187
+ }
188
+
189
+ // 2. Remove any legacy `~/.local/bin/theamify` shadow — symlink OR real file.
190
+ // This is what KEEPS the command alive when `~/.local/bin` is on PATH.
191
+ if (removeUserBin()) p.log.success('Removed ~/.local/bin/theamify.');
192
+
193
+ // 3. Reset the boot menu back to the default (remove the applied GRUB theme).
194
+ const grubReset = await resetGrubTheme();
189
195
 
190
- // Remove the npm package so the `theamify` command actually disappears.
196
+ // 4. Remove the npm package so the `theamify` command truly disappears.
191
197
  const npmRemoved = await selfUninstall();
192
198
 
193
- // Remove leftover PATH markers from ~/.bashrc / ~/.zshrc so zero traces remain.
199
+ // 5. Remove leftover PATH markers from ~/.bashrc / ~/.zshrc so zero traces remain.
194
200
  await cleanRcMarkers();
195
201
 
196
202
  // Companion tools (chafa, grub-customizer) are intentionally LEFT in place —
197
- // the user may want them for later; uninstall only removes theamify itself.
203
+ // they are not theamify; uninstall only removes theamify itself.
198
204
  const themeNote = grubReset
199
205
  ? 'Boot menu reset to the default theme.'
200
- : (resetGrub ? 'GRUB reset could not be completed — remove GRUB_THEME= from /etc/default/grub and rebuild.' : 'Your applied GRUB theme was left in place.');
206
+ : 'GRUB reset could not be completed — remove GRUB_THEME= from /etc/default/grub and rebuild.';
201
207
 
202
208
  p.outro(pc.green(
203
- `Uninstalled.${removed ? ' Runtime + all downloaded themes removed.' : ''} ${themeNote} ${npmRemoved ? 'theamify npm package removed — command no longer available.' : 'theamify npm package kept.'} Companion tools (chafa, grub-customizer) were kept for your use.`,
209
+ `Uninstalled.${removedCount ? ` ${removedCount} runtime tree(s) + all downloaded themes removed.` : ''} ${themeNote} ${npmRemoved ? 'theamify-cli npm package removed — the theamify command is gone.' : 'theamify-cli npm package could not be removed automatically — run: npm uninstall -g theamify-cli'} Companion tools (chafa, grub-customizer) were kept for your use.`,
204
210
  ));
211
+ p.log.message(pc.dim('After uninstall, `theamify` reports: bash: theamify: command not found'));
205
212
  }
@@ -44,6 +44,24 @@ export function removeShadowBin() {
44
44
  } catch { /* nothing to remove */ }
45
45
  }
46
46
 
47
+ /**
48
+ * Remove anything at `~/.local/bin/theamify` — a legacy shadow executable that
49
+ * keeps the `theamify` command alive and SHADOWS the npm CLI after uninstall
50
+ * (because `~/.local/bin` precedes the npm global bin on PATH). Removes BOTH a
51
+ * symlink and a plain-file engine copy left behind by old installs.
52
+ * @param {string} [binPath] override the path (used by tests)
53
+ * @returns {boolean} true when something was removed
54
+ */
55
+ export function removeUserBin(binPath = USER_BIN_LINK) {
56
+ try {
57
+ if (fs.existsSync(binPath) || fs.lstatSync(binPath)) {
58
+ fs.rmSync(binPath, { force: true });
59
+ return !fs.existsSync(binPath);
60
+ }
61
+ } catch { /* nothing to remove */ }
62
+ return false;
63
+ }
64
+
47
65
  /** Copy the vendored engine script + shared libs into a runtime dir. */
48
66
  function copyEngineTo(dir) {
49
67
  fs.mkdirSync(path.join(dir, 'lib'), { recursive: true });
package/src/lib/self.js CHANGED
@@ -1,5 +1,8 @@
1
1
  import { execa } from 'execa';
2
2
  import pc from 'picocolors';
3
+ import fs from 'node:fs';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
3
6
  import { createRequire } from 'node:module';
4
7
 
5
8
  const require = createRequire(import.meta.url);
@@ -34,13 +37,13 @@ export async function getLatestVersion() {
34
37
  * Remove any leftover `# theamify CLI PATH` + follow-up `export PATH=` line from
35
38
  * ~/.bashrc and ~/.zshrc (added by old installs). Called by uninstall so no
36
39
  * traces of the tool remain in shell startup files.
40
+ * @param {string} [homeDir] override the home directory (used by tests)
37
41
  */
38
- export async function cleanRcMarkers() {
39
- const rcs = ['.bashrc', '.zshrc'].map((f) => path.join(require('node:os').homedir(), f));
42
+ export async function cleanRcMarkers(homeDir = os.homedir()) {
43
+ const rcs = ['.bashrc', '.zshrc'].map((f) => path.join(homeDir, f));
40
44
  const marker = '# theamify CLI PATH';
41
45
  for (const rc of rcs) {
42
46
  try {
43
- const fs = require('node:fs');
44
47
  if (!fs.existsSync(rc)) continue;
45
48
  const lines = fs.readFileSync(rc, 'utf8').split('\n');
46
49
  const out = [];
@@ -98,18 +101,12 @@ export async function promptSelfUpdate() {
98
101
 
99
102
  /**
100
103
  * Fully remove the npm package so the `theamify` command disappears from PATH.
104
+ * Uninstall is non-interactive about this — removing the npm package is a core
105
+ * part of "remove everything", not an optional extra.
101
106
  * @returns {Promise<boolean>} true when uninstalled
102
107
  */
103
108
  export async function selfUninstall() {
104
109
  const p = await import('@clack/prompts');
105
- const want = await p.confirm({
106
- message: `Also remove the ${NPM_NAME} npm package, so the ${pc.cyan(BIN_NAME)} command is gone from your system?`,
107
- initialValue: true,
108
- });
109
- if (p.isCancel(want) || !want) {
110
- p.log.message(pc.dim(`Keeping the npm package — the ${BIN_NAME} command will remain.`));
111
- return false;
112
- }
113
110
  console.log();
114
111
  const res = await execa('npm', ['uninstall', '-g', NPM_NAME], { stdio: 'inherit', reject: false });
115
112
  if (res.exitCode === 0) {
package/vendor/theamify CHANGED
@@ -10,7 +10,7 @@ set -euo pipefail
10
10
  # -----------------------------------------------------------------------------
11
11
  # VERSION & TOOL NAME
12
12
  # -----------------------------------------------------------------------------
13
- readonly VERSION="2.2.1"
13
+ readonly VERSION="2.2.3"
14
14
  readonly TOOL="theamify"
15
15
 
16
16
  # -----------------------------------------------------------------------------