@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 +17 -0
- package/README.md +14 -1
- package/package.json +1 -1
- package/scripts/postinstall.js +10 -0
- package/src/postinstall.test.ts +52 -0
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
package/scripts/postinstall.js
CHANGED
|
@@ -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
|
+
});
|