@nemus-cli/nemus 0.15.0 → 0.15.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.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.15.1] - 2026-09-04
11
+
12
+ ### Fixed
13
+
14
+ - **`npx @nemus-cli/nemus …` no longer hangs.** The postinstall first-run setup
15
+ (interactive `nemus configure`, shell-integration install) now runs **only on
16
+ a global install** (`npm i -g`). A transient `npx` run or a local dependency
17
+ install is not global — the `nemus`/`nem` bins aren't persisted on PATH, so
18
+ shell integration is pointless, and the interactive `configure` reached the
19
+ controlling terminal via `/dev/tty` and hung a non-interactive
20
+ `npx … --help`/`--version`. Global installs are unchanged.
21
+
22
+ ### Added
23
+
24
+ - **Zero-install usage** documented: `npx @nemus-cli/nemus <command>` runs any
25
+ command without installing or touching your shell.
26
+
10
27
  ## [0.15.0] - 2026-09-02
11
28
 
12
29
  ### Added
package/README.md CHANGED
@@ -140,7 +140,20 @@ npm install -g @nemus-cli/nemus
140
140
  The postinstall step sets up optional shell integration (auto-cd into new
141
141
  workspaces + a quick-navigate helper). It keeps the generated functions in
142
142
  `~/.nemus/shell-integration.sh` and adds only a guarded source line to your
143
- shell RC file, so upgrades don't churn your `.zshrc`/`.bashrc`.
143
+ shell RC file, so upgrades don't churn your `.zshrc`/`.bashrc`. First-time setup
144
+ (`nemus configure`) runs automatically **only on a global install** — a
145
+ throwaway `npx` run or a local dependency install leaves your shell untouched.
146
+
147
+ ### Try it without installing
148
+
149
+ ```bash
150
+ npx @nemus-cli/nemus list # run any command via npx
151
+ npx @nemus-cli/nemus -- "…" # or the AI prompt
152
+ ```
153
+
154
+ Zero install, nothing added to your PATH or shell RC. (Shell integration and
155
+ auto-cd only make sense once the `nemus`/`nem` bins are on your PATH, so install
156
+ globally when you want those.)
144
157
 
145
158
  ### From source
146
159
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nemus-cli/nemus",
3
- "version": "0.15.0",
3
+ "version": "0.15.1",
4
4
  "workspaces": [
5
5
  "packages/*"
6
6
  ],
@@ -20,6 +20,16 @@ const path = require('path');
20
20
  // Skip in CI environments
21
21
  if (process.env.CI) process.exit(0);
22
22
 
23
+ // Only run first-run setup for a real GLOBAL install (`npm i -g`). A transient
24
+ // `npx @nemus-cli/nemus …` (npm exec) or a local dependency install is not
25
+ // global: the `nemus`/`nem` bins aren't persisted on PATH, so shell integration
26
+ // is pointless — and worse, the interactive `configure` below reaches the
27
+ // controlling terminal via /dev/tty and would HANG a non-interactive
28
+ // `npx … --help`/`--version` invocation waiting for input. npm sets
29
+ // npm_config_global="true" only for `-g`/`--global`; npx and local installs
30
+ // leave it false/unset.
31
+ if (String(process.env.npm_config_global).toLowerCase() !== 'true') process.exit(0);
32
+
23
33
  const PKG_ROOT = path.join(__dirname, '..');
24
34
  const SHELL_SCRIPT = path.join(PKG_ROOT, 'install-shell-integration.sh');
25
35
  const CLI_BIN = path.join(PKG_ROOT, 'bin', 'workspace.js');
@@ -0,0 +1,52 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { execFileSync } from 'node:child_process';
3
+ import { mkdtempSync, writeFileSync, readFileSync, rmSync } from 'node:fs';
4
+ import { tmpdir } from 'node:os';
5
+ import { join } from 'node:path';
6
+
7
+ const SCRIPT = join(__dirname, '..', 'scripts', 'postinstall.js');
8
+
9
+ describe('postinstall.js', () => {
10
+ // Regression: a transient `npx @nemus-cli/nemus …` (npm exec) or a local
11
+ // dependency install is NOT global. Such installs must not launch the
12
+ // interactive `configure` (which reaches /dev/tty and would HANG a
13
+ // non-interactive `npx … --help`) nor mutate the user's shell RC — the bins
14
+ // aren't persisted on PATH for them anyway.
15
+ it('is a fast no-op for a non-global install: exits 0 and never touches the shell RC', () => {
16
+ const home = mkdtempSync(join(tmpdir(), 'nemus-postinstall-'));
17
+ const rc = join(home, '.zshrc');
18
+ writeFileSync(rc, '# user rc\n');
19
+ const env = { ...process.env, HOME: home, SHELL: '/bin/zsh', NEMUS_CACHE_DIR: join(home, '.nemus') };
20
+ // Delete CI so the global gate — not the CI early-exit — is what protects npx.
21
+ delete env.CI;
22
+ delete env.npm_config_global; // npx / local install leaves this unset
23
+ try {
24
+ // Throws on non-zero exit; throws on the 15s timeout if it ever hangs.
25
+ execFileSync(process.execPath, [SCRIPT], { env, stdio: 'ignore', timeout: 15_000 });
26
+ // RC untouched — no guarded `source` line appended.
27
+ expect(readFileSync(rc, 'utf8')).toBe('# user rc\n');
28
+ } finally {
29
+ rmSync(home, { recursive: true, force: true });
30
+ }
31
+ });
32
+
33
+ it('treats npm_config_global="false" the same as unset (still a no-op)', () => {
34
+ const home = mkdtempSync(join(tmpdir(), 'nemus-postinstall-'));
35
+ const rc = join(home, '.bashrc');
36
+ writeFileSync(rc, '# user rc\n');
37
+ const env = {
38
+ ...process.env,
39
+ HOME: home,
40
+ SHELL: '/bin/bash',
41
+ NEMUS_CACHE_DIR: join(home, '.nemus'),
42
+ npm_config_global: 'false',
43
+ };
44
+ delete env.CI;
45
+ try {
46
+ execFileSync(process.execPath, [SCRIPT], { env, stdio: 'ignore', timeout: 15_000 });
47
+ expect(readFileSync(rc, 'utf8')).toBe('# user rc\n');
48
+ } finally {
49
+ rmSync(home, { recursive: true, force: true });
50
+ }
51
+ });
52
+ });